A while back I wrote three unrelated "editable root" objects that were used separately. Now for a new project, I'm reusing those three objects together. And I just realized that this around I need them to save using the same transaction object. So if one editable root object's save fails, all three objects will roll back their data changes. What is the best way to accomplish this?
Thanks,
Jason
This isn't too hard if you manage your transactions manually. Your DataPortal_Update() method initiates a new transaction if none is in progress, otherwise it obtains the existing transaction.
On the "server side" of the data portal, you can adopt a convention where all your objects implement a Save(TransactionContext transaction) type method for doing the actual work, and you can pass your transaction around as needed. We have done almost arbitrary compounding of object saving using this technique.
Have a look at the following : How to handle transactions across root objects.
http://forums.lhotka.net/forums/thread/16605.aspx
Here is some real code you can use:
http://forums.lhotka.net/forums/thread/9066.aspx
Joe
Would this a goofy or bad way to do it - or would it be better to do it a different way?
Add a second "Save" method like this...
--------------------------------------------------------------------
Private mobjSharedTransaction As System.Data.SqlClient.SqlTransaction
Public Function SaveWithSharedTransaction(ByVal transaction As System.Data.SqlClient.SqlTransaction) As Order
mobjSharedTransaction = transaction
Return MyBase.Save()
End Function
--------------------------------------------------------------------
And then inside of DataPortal_Insert and DataPortal_Update do something like this...
--------------------------------------------------------------------
Dim blnUsingSharedTransaction as Boolean
'...other code...
If mobjSharedTransaction Is Nothing Then
objTransaction = objConnection.BeginTransaction
blnUsingSharedTransaction = False
Else
objTransaction = mobjSharedTransaction
blnUsingSharedTransaction = True
mobjTransaction = Nothing
End If
'...other code...
If blnUsingSharedTransaction = False Then
objTransaction.Commit()
End If
Catch ex as Exception
If blnUsingSharedTransaction = False Then
objTransaction.Rollback()
End If
Throw
End Try
Try reading the link I posted for some ideas.
ajj3085:...
Your root BOs will need an internal Save method, so that it doesn't try calling the dataportal at all. The internal save method should just do the data access part and not worry about transactions at all. Also, since they'll be using the Connection / ContextManager, they'll automatically use the connection your command object opens and hence the transaction you start on that connection.
Yes, this is very important. Also remember that if you are doing any *reading* of data during your transactions, you need to use the transaction object to do the reading, otherwise you can deadlock on yourself.
(This may come for "free" with some of the transaction types supported by CSLA, but if doing manual transactions you definitely have to be aware of this).
Please forgive my ignorance....I've apparently discovered some gaps in my knowledge of CSLA.
I found and am re-reading about "ApplicationContext.LocalContext" in the main book and the 2.1 e-book.
However I don't see this "ConnectionManager" or "ContextManager" you guys mentioned -- is this a CSLA framework thing or a .NET Framework thing?
These classes are part of Csla framework itself. They are
in Csla.Data namespace. They were added as part of 3.0, if I remember
correctly.
Sergey Barskiy
Principal Consultant
office: 678.405.0687 |
mobile: 404.388.1899
Microsoft Worldwide Partner of the Year | Custom
Development Solutions, Technical Innovation
From: xxxJasonxxx
[mailto:cslanet@lhotka.net]
Sent: Wednesday, September 10, 2008 2:47 PM
To: Sergey Barskiy
Subject: Re: [CSLA .NET] Editable Root Objects Sharing Transaction
Please forgive my ignorance....I've apparently discovered some gaps in my
knowledge of CSLA.
I found and am re-reading about "ApplicationContext.LocalContext"
in the main book and the 2.1 e-book.
However I don't see this "ConnectionManager" or
"ContextManager" you guys mentioned -- is this a CSLA framework thing
or a .NET Framework thing?
Actually it looks like the "ContextManager" and "ConnectionManager" were not added until version 3.5. So it's not covered in the original book or two e-books (2.1 & 3.0) that I've been looking at.
Ok, I've downloaded 3.5 and I've been looking at the code inside the "ConnectionManager" class. I agree this looks like a really slick way to share a connection or transaction.
However, I have one major concern...
As the ConnectionManager shares the connection/transaction among multiple objects, how can each object determine whether it is the originator or owner of the shared connection/transaction? I think they will need to know that for deciding whether to call "Commit" or "Rollback". Am I thinking correctly?
- - - - -
Related to that, if you look at the link Joe Fallon posted above...
http://forums.lhotka.net/forums/thread/9066.aspx
... it looks like his code posted towards the bottom of the conversation is trying to address this problem of how to determine which object originated the connection/transaction when he does this.....
If LocalContext.Item("StartedTrans") Is BO Then
...but I'm honestly not following what is happening here (what is BO or Core.IBusinessObject and what is this actually comparing).
Can anyone offer any insight?
BO is the specific business object class that you are using.
e.g. If you are doing Orders then you would an Order BO with a child collection of OrderLines each with an OrderLine.
So the code above becomes:
If LocalContext.Item("StartedTrans") Is Order Then
===========================================
Say you have 3 root BOs to save and each has the code I show in them.
Then the first one to call BeginTransaction is the one that actually starts the transaction. The next two make the same call but since an active tr exists, they simply enlist in it. When the next two call EndTransaction, nothing happens because they are not the one that started it (their Types are different than the test If LocalContext.Item("StartedTrans") Is Order Then). Only when the first BO calls EndTransaction does it meet the test above and then the entire tr is committed.
Joe
Joe:
Please bear with me a tiny bit longer. You've been a huge help and I've almost got it.....
I think I understand the concept or intent, but I may be getting mixed up on the syntax.
CONCEPT / INTENT.....
I think you're saying that if my Order object was the originator of the connection/transaction, that IF statement in your code is asking every object that uses it......are you the Order object? Do I understand your concept/intent correctly?
SYNTAX.....
I'm assuming you're NOT saying I would explicitly change the code from this.....
If LocalContext.Item("StartedTrans") Is BO Then
.....to this....
If LocalContext.Item("StartedTrans") Is Order Then
.....because that seems too specific rather than generic/resuable (I don't think that's your intent).
Are you saying that the "BO" variable would evaluate as an "Order" type or "Order" object?
This could be part of my confusion too -- I noticed you are passing "BO" in like this...
Public Shared Function BeginTransaction(ByVal BO As Core.IBusinessObject) As IDbTransaction
.....which makes me wonder if every business object would evaluate as True for being a Core.IBusinessObject (I think that's an interface all business base objects inherit from).
And possibly a major point of confusion for me with "BO" could be.......is this storing and comparing a memory reference to a specific object or is it storing and comparing an object type?
I'm not sure I'm expressing my confusion clearly or asking the right questions......but hopefully you get the idea and can help.
Thanks,
Jason
Sorry...I think my confusion is simply not understanding how the "IS" keyword works in your IF statement.
If LocalContext.Item("StartedTrans") Is BO Then
I am going back to do research on this.....it's one of those things I haven't used much lately except for when evaluating whether a variable "Is Nothing". So I've just forgotten and need to refresh.
Thank you for the help -- really appreciate it.
In case anyone else's memory is as bad as mine...
The Is operator determines if two object references refer to the same object. However, it does not perform value comparisons. If object1 and object2 both refer to the exact same object instance, result is True; if they do not, result is False.
Dim myObject As New Object
Dim otherObject As New Object
Dim yourObject, thisObject, thatObject As Object
Dim myCheck As Boolean
yourObject = myObject
thisObject = myObject
thatObject = otherObject
' The following statement sets myCheck to True.
myCheck = yourObject Is thisObject
' The following statement sets myCheck to False.
myCheck = thatObject Is thisObject
' The following statement sets myCheck to False.
myCheck = myObject Is thatObject
thatObject = myObject
' The following statement sets myCheck to True.
myCheck = thisObject Is thatObject
xxxJasonxxx:Sorry...I think my confusion is simply not understanding how the "IS" keyword works in your IF statement.
If LocalContext.Item("StartedTrans") Is BO Then
I am going back to do research on this.....it's one of those things I haven't used much lately except for when evaluating whether a variable "Is Nothing". So I've just forgotten and need to refresh.Thank you for the help -- really appreciate it.
The "is" operator in C# just tests whether the left hand side is the of the Type on the right side. So, the above code is just testing if the object is of type "BO".
xxxJasonxxx:CONCEPT / INTENT.....
I think you're saying that if my Order object was the originator of the connection/transaction, that IF statement in your code is asking every object that uses it......are you the Order object? Do I understand your concept/intent correctly?SYNTAX.....
I'm assuming you're NOT saying I would explicitly change the code from this.....
If LocalContext.Item("StartedTrans") Is BO Then
.....to this....
If LocalContext.Item("StartedTrans") Is Order Then
.....because that seems too specific rather than generic/resuable (I don't think that's your intent).
Are you saying that the "BO" variable would evaluate as an "Order" type or "Order" object?
This could be part of my confusion too -- I noticed you are passing "BO" in like this...
Public Shared Function BeginTransaction(ByVal BO As Core.IBusinessObject) As IDbTransaction
.....which makes me wonder if every business object would evaluate as True for being a Core.IBusinessObject (I think that's an interface all business base objects inherit from).
And possibly a major point of confusion for me with "BO" could be.......is this storing and comparing a memory reference to a specific object or is it storing and comparing an object type?
Concept intent: Yes. That is what is going on.
So the call to EndTransaction has this code:
If
LocalContext.Item("StartedTrans") Is BO ThenWhich says: get the item from the local context which I stored when I *started* the transaction and compare it to each BO which called End Transaction. So for the two later objects, they fail the test and nothing happens. But the last call is the Order object and it is the one that started the tr so the code evaluates to true and commits everything.
Syntax: right again - no need to change the code at all since BO is a parameter which is passed in (as Me) to each method. Only the BO that starts the tr is stored in context with the string key: "StartedTrans"
The BO itself is stored in context when the tr is started and it is compared with each BO that calls EndTransaction to be sure that only the one that started it can end it.
Joe
Copyright (c) Marimer LLC