This is a migrated thread and some comments may be shown as answers.

Basic architecure question

10 Answers 249 Views
Getting Started
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
Chris
Top achievements
Rank 1
Chris asked on 09 Mar 2009, 07:14 PM
Hi,

I have a general question about best practices and architecure.

This question came up when I realized that, as far as I can tell, persistent entities cannot be modified in any way unless it is within  a transaction.  Take the following standard architecture:

1. GUI (Winforms, WPF, etc)
2. BLL (validation, etc)
3  DAL

Presumably all three have references to the persistant classes.

Suppose the BLL passes a persistent object (say a customer) to the GUI.  Now suppose the GUI wants to modify the Customer's name.  The GUI can not simply modify the Customer.Name property and then call myBLL.Save(myCustomer), because the GUI can not modify the Customer object at all without creating a transaction.

I can think of a number of solutions, but none seem great:

1.  Litter ORM code all over the GUI - clearly not good

2.  Create a function in the BLL for every property on every persistent object:
    ModifyCustomerName(byval cust as Customer, byval newName as String)
    ModifyCustomerNickName(byval cust as Customer, byval newNickName as String)
    ...
Clearly this is not a good solution.

3. Create GUI-Side proxy-Type objects for every persisent class.  Basically copies of the persistent classes withuot the persistance, and then pass these objects between the Gui and BLL, copying back and forth between persistant and non-persistant versions wih every call.  This is easily doable, and has some advantages, but is much less than ideal.

Am I missing something very basic about how to use/interact with the persistant objects? 
Shouldn't this be easier?
 If not, what is recommended, what are other people using?

10 Answers, 1 is accepted

Sort by
0
Robert
Top achievements
Rank 2
answered on 10 Mar 2009, 01:56 PM
Hey Chris,

You don't need to worry about all of this.  In one of your earlier statements you say:

"Suppose the BLL passes a persistent object (say a customer) to the GUI"  The BLL would have asked the DAL to load the object from the database and key here is that the DAL would have loaded the Customer in a "transaction" and kept it open.  As such, any edits made to the Customer while the transaction is open, be it by the BLL or DAL can be persisted simply by passing it back to the DAL and committing the transaction (ie. anything that was done to any objects loaded in the transaction).  Clean and simple.

One of the keys to understanding this is that a "transaction" in Open Access lingo is not a transaction like you're used to thinking about when thinking in database terms.  It is simply a window that is opened by Open Access where activity is remembered and tracked and can subsequently be committed.  Actual database transactions have no relationship to Open Access transactions when looking at opening and committing and rolling back so the "start and commit a db transaction asap" that we've heard doesn't really hold for Open Access transactions.  I imagine there are actual db transactions being used when committing an Open Access transaction but distinguishing between the two is important.

Hope this helps,
Robert
0
Chris
Top achievements
Rank 1
answered on 10 Mar 2009, 03:58 PM
Very cool!  Thanks for the info!

So, just to make sure I have it right, the flow would be:
1. DAL starts transaction and sends the object up to GUI
2. GUI modifies object and sends it back down to DAL
3.  Get the scope for the object (??)
4. Commit/rollback the transaction.

Because the GetScope method only exists on the Database Object, it looks like the easiest way to get the scope for an object is to create a Scope property in the persistent object:

         public IObjectScope Scope { get { return Database.GetContext(this) as IObjectScope; } }

I guess the only downside to this is that the scope is then exposed to the GUI layer.

QUESTION 1 - Why dont objects have a Scope property built in?  Is there something wrong with this approach?

QUESTION 2-  Are OA transactions really that lightweight?  Are they really made to exist for such long time frames (potentially forever, as they may never be commited/rolledback)?


It also necessitates being VERY careful about wich objects are in each transaction. For example:
1. Start Transaction
2. Send 3 objects up to the GUI
3. Gui modifies one of them
4. Gui sends the 1 modified object down to the DAl
5. DAL commits the transaction

At this point the 2 other objects become immutable.  Any attempt by the GUI to modify them will throw an exception.

The only way to get around this would be to have a one-one relationship between object and transaction. I.e. - every object exists in its own transaction. That could be a lot of transactions.

QUESTION 3- Not that I like it, but would that be a viable approach?  

QUESTION 4 - It's easy to imagine a user using an app for hours or days and thus having thousands of open/dead transactions.  Is that really OK?

Does the above seem correct?  Am I missing anything?  Any comments or thoughts are greatly appreciated.

BTW - I am spelling out the steps explicitly for 2 reasons: 1) just to make sure we are 100% on the same page, and 2) to help people without a good background in architecture.
0
Chris
Top achievements
Rank 1
answered on 10 Mar 2009, 04:22 PM
With regard to question 4 - I guess the choice is between having the GUI explicitly track when an object is no longer going to be used  and then having the DAL commit/rollback the transaction even if nothing has happened, which incurs a hit all the way down to the DAL; or having a bunch of dead transactions.  I'm guessing that explicitly ending the transaction is preferable.  I just hate to make such calls just to say "do nothing."  If that call is over a physical boundary, such as using WCF, it is somewhat expensive.

Also, if it is over WCF with the dal residing on a server, it could still end up with lots of dead transactions over time.  If the app terminates without the call to "do nothing," the transaction will still exist.
0
Robert
Top achievements
Rank 2
answered on 12 Mar 2009, 03:28 PM
Hi Chris,

Yes, the 4 step flow you outline is exactly right.

In our application we started down the road of having different scopes and having the caller to our persistence service specify which scope to use but in the end the complexity had no ROI.  We are now using just one transaction scope for everything and we've flipped the setting where the transaction is renewed automatically after a commit.  As such, the one transaction scope is always open.  This works out well because our user will typically enter a screen, make some changes, and then move on to another screen.  If they attempt to leave the screen and the transaction has outstanding changes, we ask if they wish to save or not, and if they say not, we rollback the entire transaction (ie. all the work they decided to walk away from) and the app is ready to accept changes to other domain objects.

So to sum up, we have just one ORM transaction open and it stays open as long as the user in in the application (it's a WPF application).  The same approach could be taken with a web app, where the 1 ORM transaction could be kept open as long as the users' session remains alive.  Not again that an OpenAccess ORM transaction is NOT THE SAME as a database transaction.

Hope this helps,
Rob


0
Chris
Top achievements
Rank 1
answered on 12 Mar 2009, 06:18 PM
Very interesting.  I will have to ponder that for a while to see how it might fit into my app.

I really appreciate the info.  Thanks!
0
Chris
Top achievements
Rank 1
answered on 15 Mar 2009, 08:29 AM
That is almost working for me.  I don't have to worry about passing around the scope and such, but it did raise a related issue.
Is seems that it is not always possible to add an item to a scope twice?

I was writing my functions to add the parameters (persistent classes) to the (static) scope every time.  The functions have no way of knowing whether or not they have already been added to the scope.

I am receiveing:

Instance of 'MemoryScience.Data.DalObjects.Category' with identity '4647572-3ba6197a-df6e-41ae-9199-e2c076e8cf4b' already exists in the cache of the object scope.

I'm not sure about the exact conditions that cause this.  Simple tests of adding the same object multiple times seems to work.  Do you know of an easy way to check the cache to see if an object is already present?  Or, better yet, under which conditions this exception is thrown?

Thanks a million for all your help, I do really appreciate it.




0
Robert
Top achievements
Rank 2
answered on 16 Mar 2009, 03:07 PM
Hey Chris,

I haven't run into any such problems.  My gut tells me that you're probably writing too much code concerned with adding objects to the scope.  Just on friday I updated my code library post with a full 3 project sample of using a Generic Persistence Manager.  Check it out here:


Also, if you're still getting issues maybe this thread would help:


Cheers,
Robert
0
Chris
Top achievements
Rank 1
answered on 17 Mar 2009, 03:10 AM
Thanks,

I haven't delved into it to see exactly what was happening with OA, but I think I found the problem.  The architecture I am working with uses BusinessObjects in the GUi and BLL.  The BLL passes them to the DAL, which converts them to PersistentObjects. (I'm not advocating this in all cases, but it is what I must use right now)  It looks like there was a bug in the conversion routine.  A single BO object was being converted to twoPersistentObjects with the same ID.  The DAL then tried to add and operate on both of them.

I got around it by simply retrieving the PersistentObject with the specified ID whenever a BO is converted to a PO.  Because of the L2 cache this shouldn't result in a performance issue.

I am looking forward to looking at the Generic Persistence Manager.  Actually,  I was the one that requested a full project.  Thanks for posting it, and for all your help
0
Robert Lautenbach
Top achievements
Rank 1
answered on 03 Mar 2010, 08:37 PM
Forgive my late response on this thread. I'm beginning to use OpenAccess ORM on a new project. And this thread hits on some issues I've run into.

Let's assume I'm using persistent classes the way you describe. I grab a persistent class, make some changes, and commit the scope. I have some extra custom functions I need to call during this process that is specific to the object I have loaded. Where would you put these functions? Would you add them into the persistent class itself? Or do you have some other layer of business logic? If the latter, how do you connect your business logic to the persistent class?

Thanks in advance.
0
Jan Blessenohl
Telerik team
answered on 10 Mar 2010, 03:42 PM
Hi Robert Lautenbach,
I am a fan of naming the persistent class business object. If you do the mapping by hand you would write code that is reading the dataset and copying the data to the business object. Now we are doing this and this does not mean that you should handle the object differently.

Please add the business methods that are related to the persistent object directly at the object. If you need to do something directly before commit you can implement our IInstanceCallbacks interface as well.

Regards,
Jan Blessenohl
the Telerik team

Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
Tags
Getting Started
Asked by
Chris
Top achievements
Rank 1
Answers by
Robert
Top achievements
Rank 2
Chris
Top achievements
Rank 1
Robert Lautenbach
Top achievements
Rank 1
Jan Blessenohl
Telerik team
Share this question
or