Quantcast
Channel: CSLA .NET
Viewing all articles
Browse latest Browse all 764

CSLA 4.x TransactionalDataPortal has a breaking change compared to CSLA 3.8

$
0
0

Houston we have a problem. I've been using both CSLA 4.x and 3.8 for some time now, and haven't run across this issue until now, where we are upgrading a CSLA 3.8 based project to CSLA 4.5.

This specific project executes along with other external application components from COM+ (Enterprise Services), making it sensitive to 2PC and Transaction Isolation levels.

Context: I'm referring to using the [Transactional( TransactionalTypes.TransactionScope )] attribute on the DataPortal_XYZ methods.

For most CSLA projects, the transaction starts from the DataPortal_XYZ methods and seldom goes further to include other transactions. We have combinations of both, and specifically, we let the DataPortal_XYZ methods "take-on" the transaction isolation level of the caller. This may vary in that one caller has one isolation level, and another caller may use a different one (don't you just love integration work Ick! ).

What cannot be done is to have a caller use one isolation level, and then have the subsequent DataPortal_XYZ method execute under a different isolation level, when it is enrolled in the same transaction scope of the caller (TransactionScope = Required).

The implementation of the CSLA 3.8 Transactional DataPortal code had created a new System.Transactions.TransactionScope instance using the default parameterless constructor. This default constructor assumes a Scope = Required but specficially does not specify an isolation level. There is no default here, it's unspecified.

Here is the code for the CSLA 3.8 version: http://www.lhotka.net/cslacvs/viewvc.cgi/core/branches/V3-8-x/cslacs/Csla/Server/TransactionalDataPortal.cs?revision=4288&view=markup

The implementation of the CSLA 4.5 Transactional DataPortal looks to have some refactorings and actually creates a new System.Transactions.TransactionScope instance using a constructor overload that takes both the Scope and the Isolation level as parameters. The Scope is defaulted to Required (when not expressly specified in the DataPortal_XYZ attribute), which matches the original behaviour. But, the isolation level is also specified in this overload, using a default value of Serializable and therein lies the rub, it's not unspecified/omitted.

Here is the code for the latest CSLA 4.5.x version: https://github.com/MarimerLLC/csla/blob/master/Source/Csla/Server/TransactionalDataPortal.cs

This default of Serializable is the safest (though most expensive) isolation level and makes a good default. Unfortunately, it is not the same behaviour as with CSLA 3.8, and does not allow the "flow from caller" scenario.

Please note I'm pointing out a subtle difference here.
Of course you can specify any isolation level that you want in the DataPortal_XYZ attribute (e.g. [Transactional( TransactionalTypes.TransactionScope, TransactionIsolationLevel.RepeatableRead )] but that is not the issue. The issue is that by specifying it in the first place, results that it will always be that isolation level, regardless of the isolation level flowed from caller's scope (if one is present).

With CSLA 3.8's implementation it would also have IMPLICITLY defaulted to serializable, but only if the isolation level from a caller's scope was not different, in which case it would've taken the isolation level of the caller.

With CSLA 4.5's implementation it is EXPLICITLY set to serializable by CSLA's default behavior, resulting in a transaction exception faulting the mismatch of isolation levels (the one from the callers scope not matching the one set on the DataPortal_XYZ.

It turns out the System.Transactions.IsolationLevel enumeration has an Unspecified option.
Refer: http://msdn.microsoft.com/en-au/library/system.transactions.isolationlevel.aspx

I've done some tests and if I manually create a transaction scope using the same overload as CSLA 4 but then use this "Unspecified" option is works as expected and behaves as per CSLA 3.8 allowing me to "flow" the isolation level from the caller.

Unfortunately, the ApplicationContext.DefaultTransactionIsolationLevel does not have an Unspecified enumeration either).

@Jonny / @Rocky - Would you accept this as an issue for rectification?

I really hope so, as this is a show stopper for our project development at the moment.

In mind, the fix would be to either:

  • Add a matching "Unspecified" option to the CSLA TransactionIsolationLevel enumeration and use that is the default value instead of Serializable in the method:
    private IsolationLevel GetIsolationLevel(TransactionIsolationLevel transactionIsolationLevel) {...}

    (Probably the easiest and least amount of code changes).

    alternatively,
  • Find some way call the same default parameterless constructor as CSLA 3.8 when no express values are set for the Transactional attribute on the DataPortal_XYZ methods.

 

I'd be happy to try and put a pull-request together if that would help further.
Thanks a million,

Jaans

 

 


Viewing all articles
Browse latest Browse all 764

Trending Articles