Help with Child_Update CSLA 3.5

Help with Child_Update CSLA 3.5

Old forum URL: forums.lhotka.net/forums/t/4959.aspx


bt1 posted on Tuesday, June 10, 2008

Hey, I have a question that is probably obvious but I am new to the latest versions of CSLA.  (I used older versions for framework 1.1)

It has to do with providing/passing info to children objects. Where a root BusinessBase with a property that is a BusinessListBase containing children BusinessBase objects.  I am NOT using LINQ and most of the examples for 3.5 are, so it is harder to understand(and the 3.5 book isn't available yet).  

I would like to be able to pass DbTransaction or DbConnection objects to the children so they can participate in explicit db transactions or use TransactionScope without being promoted to a distributed transaction.

ConnectionManager will not work because the DAL we are using is provider agnostic and I only have a DbConnection  object. ConnectionManager is defined public class ConnectionManager<C> : IDisposable where C : IDbConnection, new()  DbConnection objects do not support the constraint of where new(). 

I think there is two methods to do this. 

1. Parameters passed to the DataPortal.UpdateChild method ex in the parents DataPortal_Insert / Update method that occurs on the server.  DataPortal.UpdateChild(ReadProperty<Type>(ChildProperty), parameters) where parameters is an array of object.  Just pass the connection or transaction object.

I am not sure of the implications of passing connections/transaction objects to the UpdateChild portal method.  Will this work as long as the code is running on the server (parents DataPortal_xyz) and the portal wont' try to serialize the parameters? How is lazy loading handled?

2. Using Csla.ApplicationContext.LocalContext

This uses thread local storage when running on the server and should be fine when running multi tier.  However, what is the threading implications where the portal is run local. Is thread local storage used when running local? I would like to maintain the ability for location transparancy and allow the Business objects be built in a way that allows them to run local or on a portal (without code changes).  Is this method better that #1 above.

I am sure I will need to further explain as I have several related topics inter twined in this question but I am in need of some guidance just to get started with this and to help me ask the question better.

 

 

JoeFallon1 replied on Wednesday, June 11, 2008

I have used the LocalContext since CSLA 2.1 for handling transactions.

I decided that for child objects I would pass the tr as a parameter in my method calls because that was the way it worked in earlier versions. But there is no reason they could not have used the same pattern as the root object.

Here is what I do in a root BO:

Protected Overrides Sub DataPortal_Insert()
Dim tr As IDbTransaction = Nothing
Try
 
tr = Me.BeginTransaction()
  DoInsert(tr)
 
Me.EndTransaction(tr)
Catch ex As Exception
 
Me.RollbackTransaction(tr)
 
Throw
End Try
End Sub

In my BusinessBase class I have code like this:

Public Function BeginTransaction(Optional ByVal pxIsolationLevel As IsolationLevel = IsolationLevel.ReadCommitted, Optional ByVal poOpenedConection As IDbConnection = Nothing) As IDbTransaction
Return BOUtil.BeginTransaction(Me, pxIsolationLevel, poOpenedConection)
End Function

Public Sub EndTransaction(ByVal tr As IDbTransaction, Optional ByVal plCloseConnection As Boolean = True)
BOUtil.EndTransaction(tr,
Me, plCloseConnection)
End Sub

Public Sub RollbackTransaction(ByVal tr As IDbTransaction, Optional ByVal plCloseConnection As Boolean = True)
BOUtil.RollbackTransaction(tr,
Me, plCloseConnection)
End Sub

Which then calls into a help class named BOUtil with methods like this:

Public Shared Function BeginTransaction(ByVal BO As Core.IBusinessObject, _Optional ByVal pxIsolationLevel As IsolationLevel = IsolationLevel.ReadCommitted, _Optional ByVal poOpenedConection As IDbConnection = Nothing) As IDbTransaction
Dim tr As IDbTransaction
If TransactionExists() Then
 
tr = CType(LocalContext.Item("tr"), IDbTransaction)
Else
 
If poOpenedConection Is Nothing Then
   
poOpenedConection = DAL.CreateConnection
    poOpenedConection.Open()
 
End If
 
tr = poOpenedConection.BeginTransaction(pxIsolationLevel)
  LocalContext.Add(
"tr", tr)
  LocalContext.Add(
"StartedTrans", BO)
End If
Return tr
End Function

Public Shared Sub EndTransaction(ByVal tr As IDbTransaction, ByVal BO As Core.IBusinessObject, Optional ByVal plCloseConnection As Boolean = True)
If LocalContext.Item("StartedTrans") Is BO Then
Dim cn As IDbConnection = tr.Connection
tr.Commit()
CleanupTransaction(cn, plCloseConnection)
End If
End Sub

Public Shared Sub RollbackTransaction(ByVal tr As IDbTransaction, ByVal BO As Core.IBusinessObject, Optional ByVal plCloseConnection As Boolean = True)
If LocalContext.Item("StartedTrans") Is BO Then
 
Dim cn As IDbConnection = tr.Connection
  tr.Rollback()
  CleanupTransaction(cn, plCloseConnection)
End If
End Sub

Private Shared Sub CleanupTransaction(ByVal cn As IDbConnection, Optional ByVal plCloseConnection As Boolean = True)
If cn IsNot Nothing And plCloseConnection Then
 
cn.Close()
End If
LocalContext.Remove("tr")
LocalContext.Remove(
"StartedTrans")
End Sub

I use a single transaction across multiple BOs using this style.

The idea is to always call BeginTransaction in a BO but then check if the TransactionExists in the LocalContext.

If it does, then return the already running transaction, otherwise start a new one.

I store a reference to the BO which really begins the transaction and so when other BOs call EndTransaction, nothing happens until the BO that started the transaction calls it. So EndTransaction basically checks the LocalContext to see if the BO that called End is the same that actually started the transaction.

Using this style of code a single BO just works as normal.

If I have a Controller BO that has 3 Root objects and I want to call Save on all three BOs then there will be 4 calls to BeginTransaction but the only one that will end the tr is the one in the Controller. The others will simply "enlist" in the LocalContext tr and when they call EndTransaction nothing happens because they did not really start it.

I think this solves a longstanding concern of mine. The original problem is: A single BO saves itself and its children inside a tr. But the next BO uses a different tr and that is an issue if they are to be saved together.

Joe

bt1 replied on Friday, June 13, 2008

Thanks, for the info.  It looks like you are recommending option #2 LocalContext. I was looking through the CSLA3.5 code and it looks like it uses Thread Local Storage when it isn't in a HTTPContext. So I shouldn't have any problems with threading either on the client or server(portal) because LocalContext is specific to the thread(client) or specific to the request (IIS Portal). 

Has anyone heard a more specific timeframe for the CSLA 3.5 book.  Last I heard was October 2008.

ajj3085 replied on Friday, June 13, 2008

Alternately you can use the ConnectionManager or ContextManager.  You can backport them to earlier versions of Csla also.

Copyright (c) Marimer LLC