Problem with ContextManager and multiple DataContexts

Problem with ContextManager and multiple DataContexts

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


msk posted on Wednesday, October 14, 2009

So I want to have more than one DataContext connected to same the database and using the same transaction. 

I am using the CSLA ContextManager so it should manage the connection and transactionscope stuff for me.  The sudo-code below should effectively demonstrate things:

<Transactional(TransactionalTypes.TransactionScope)> _

Protected Sub DataPortal_Insert()

Using mgr1 = ContextManager(Of LinqContext1) .GetManager("DBSettingName")

Using mgr2 = ContextManager(Of LinqContext2) .GetManager("DBSettingName")

End Using

End Using

This actually fails horribly throwing a cast exception:   

In the first call to the factory method ContextManager.GetManager() all is well.  It creates an instance of LinqContext1, wraps it in a new ContextManager and stores it in thread level storage for future use, then returns it for our use. 

In the second call, ContextManager.GetManager() spots that it already has a ContextManager object stored for the connection string "DBSettingName" (the connection string really) and returns that object.  Of course the stored ContextManager contains an instance of LinqContext1 not the LinqContext2 that we expect so this throws a cast exception. 

I then spotted that ContextManager rather helpfully accepts a label argument. 

When I used the label I expose a new problem.  This time my separate DataContexts are wrapped in ContextManager objects and returned to me.  All looks well for my app but in fact the DataContexts are now using completely seperate underlying connections.  Consquently the TransactionScope trickery steps in and attempts to start a DTC transaction.  Of course I don't use DTC and again it fails horribly. 

As I don't want the DTC overhead, my solution has been to modify ContextManager so that it also stores the DataContext type as part of it's key in the LocalContext store:

var contextLabel = GetContextName(database, label, typeof(C).AssemblyQualifiedName)

private static string GetContextName(string connectionString, string label, string typeName)

{

return "__ctx:" + label + "-" + connectionString + "-" + typeName;

}

and then the part that checks for ContextManagers with the same underlying connection:

...

else

{

// Check to see if a DataContext with matching database name/connection string already exists

IDataContextConnection match = null;

foreach (String key in ApplicationContext.LocalContext.Keys)

{

if (key.StartsWith("__ctx:" + label + "-" + database + "-"))

{

match = (IDataContextConnection) ApplicationContext.LocalContext[key];

break;

}

}

if (match != null)

{

mgr = new ContextManager<C>(database, label, match.Connection);

... (full source attached)

I'm not entirely happy with the matching code (.StartsWith...) but that will only run where a label, database, or DataContext is used that is different to the 'ambient' ContextManager.  So there should be little impact for other usage scenarios. 

I did consider the possibility adding an overload to ContextManager.GetManager that allows a DBConnection to be passed in but it has pretty much the same problems, would require more developer code and wouldn't work for my scenario where it's not easily passed to the second GetManager call. 

OK so why do I want two different DataContexts connecting to the same database?  My particular application is modular and extended at runtime with add on modules.  Those modules can extend the database schema and need to persists data as part of the same transaction as the core or other add on modules. 

I've only had a brief look but am thinking that this same problem will also apply to the ObjectContextManager class. 

Can this please be added to the wish list for CSLA v.next?

Martin.

RockfordLhotka replied on Wednesday, October 14, 2009

Added to wish list.

http://www.lhotka.net/cslabugs/edit_bug.aspx?id=600

Copyright (c) Marimer LLC