Basic architecure question

11 posts, 0 answers
  1. Chris
    Chris avatar
    23 posts
    Member since:
    Feb 2009

    Posted 09 Mar 2009 Link to this post

    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?
  2. Robert
    Robert avatar
    40 posts
    Member since:
    Jul 2008

    Posted 10 Mar 2009 Link to this post

    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
  3. DevCraft banner
  4. Chris
    Chris avatar
    23 posts
    Member since:
    Feb 2009

    Posted 10 Mar 2009 Link to this post

    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.
  5. Chris
    Chris avatar
    23 posts
    Member since:
    Feb 2009

    Posted 10 Mar 2009 Link to this post

    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.
  6. Robert
    Robert avatar
    40 posts
    Member since:
    Jul 2008

    Posted 12 Mar 2009 Link to this post

    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


  7. Chris
    Chris avatar
    23 posts
    Member since:
    Feb 2009

    Posted 12 Mar 2009 Link to this post

    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!
  8. Chris
    Chris avatar
    23 posts
    Member since:
    Feb 2009

    Posted 15 Mar 2009 Link to this post

    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.




  9. Robert
    Robert avatar
    40 posts
    Member since:
    Jul 2008

    Posted 16 Mar 2009 Link to this post

    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
  10. Chris
    Chris avatar
    23 posts
    Member since:
    Feb 2009

    Posted 16 Mar 2009 Link to this post

    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
  11. Robert Lautenbach
    Robert Lautenbach avatar
    18 posts
    Member since:
    Dec 2009

    Posted 03 Mar 2010 Link to this post

    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.
  12. Jan Blessenohl
    Admin
    Jan Blessenohl avatar
    707 posts

    Posted 10 Mar 2010 Link to this post

    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.
Back to Top
DevCraft banner