- Home
- LLBLGen Pro
- Architecture
Using COM+ with LLBLGen & the Adapter Pattern
Joined: 04-Feb-2004
I just want to say to Frans, that your work with the COM+ Support for the Adapter Pattern is outstanding.
I also wanted to share some work with the rest of the community that I did over the weekend that might save you all some time with implementing COM+ in your applications. What I have done is taken the SAF.Transaction framework component from Xin Chen's book titled "Developing Application Frameworks in .NET" and tied it to LLBLGen Controller Logic and COM+ Support for distributed transactions.
Key points in the demonstation:
- COM+ Transaction Code is NOT tied to Controller Logic
- All 5 COM+ 1.5 Isolation Levels are Supported
- Multiple data controller methods can be executed in the context of a given transaction
Constraints:
- The sample uses the default sql server northwind DB
- The categories table is the only table used, for the sake of simplicity
- a unique constraint was created on the category name field
- The ComPlusAdapterContext's transaction attribute is set to Supported, using the COM+ mmc snapin
- The ComPlusAdapterContext's transaction isolation level was set to Any using the COM+ mmc snapin
If youre interested in the code here is what you can do with it:
- Allow your developers to use COM+ to manager transactions
- Allow developers to choose when, where, and how transactions are used without having to create new COM+ components
- Allow your developers to place any data controller object(s) within the scope of a COM+ transaction
The code is way too much to post here, so I will send it to Frans, and if he wants to post it on the 3rd party section or samples section then he can, or if you want to contact me directly for the code feel free.
Unit Test Sample 1: Shows the attempted update of 2 entities in the same transaction and the creation of a new entity in a new transaction. The sample shows how the 2 updates fail (because of the unique index) and are rolled back, but the new entity is created provided it doesnt run across any other exceptions:
public void CreateDuplicateCategories_PlusNewCategory()
{
ITransactionController updateCategoryTxController = RequiresTxManager.SerializableTxController();
ITransactionController createNewCategoryTxController = RequiresNewTxManager.SerializableTxController();
CategoryController categoryController = new CategoryController();
CategoriesEntity category1=null;
CategoriesEntity category2=null;
try
{
// do the first fetch
category1 = updateCategoryTxController.ExecuteMethod(categoryController, "FetchCategory", new object[]{1}) as CategoriesEntity;
Assert.IsNotNull(category1, "Category is null");
category1.CategoryName = "My Beverages";
// do the second fetch
category2 = updateCategoryTxController.ExecuteMethod(categoryController, "FetchCategory", new object[]{2}) as CategoriesEntity;
Assert.IsNotNull(category2, "Category2 is null");
category2.CategoryName = "My Beverages";
// do the first save
updateCategoryTxController.ExecuteMethod(categoryController, "SaveCategory", new object[]{category1});
// create the new category entity in a new transaction
try
{
CategoriesEntity newCategory = new CategoriesEntity();
newCategory.CategoryName = "Adult Beverages";
newCategory.IsNew=true;
createNewCategoryTxController.ExecuteMethod(categoryController, "SaveCategory", new object[]{newCategory});
RequiresNewTxManager.Commit(createNewCategoryTxController);
Console.WriteLine("Create New Category Succeeded");
}
catch
{
RequiresNewTxManager.Rollback(createNewCategoryTxController);
Console.WriteLine("Create new category failed");
throw;
}
// do the second update
updateCategoryTxController.ExecuteMethod(categoryController, "SaveCategory", new object[]{category2});
}
catch(System.Reflection.TargetInvocationException ex)
{
RequiresTxManager.Rollback(updateCategoryTxController);
ORMQueryExecutionException innerEx = ex.InnerException as ORMQueryExecutionException;
if (innerEx != null)
{
Console.WriteLine("We received the proper exception");
}
}
finally
{
RequiresTxManager.DisposeAll(updateCategoryTxController, createNewCategoryTxController);
}
}
** Unit Test Sample 2:** Shows how a developer can manually control the transaction isolation level to allow a dirty read. In some systems, dirty reads are sometimes required to support high levels of concurrency.
public void PerformDirtyRead()
{
ITransactionController updateWithReadUC = RequiresTxManager.SerializableTxController();
ITransactionController readUpdatesBeforeCommit = RequiresNewTxManager.ReadUncommittedTxController();
CategoryController dataController = new CategoryController();
try
{
CategoriesEntity category = updateWithReadUC.ExecuteMethod(dataController, "FetchCategory", new object[]{1}) as CategoriesEntity;
Assert.IsNotNull(category, "Category is null") ;
Console.WriteLine("Category Name before update:" + category.CategoryName);
category.CategoryName = "New Category";
Console.WriteLine("Category Name before setting calling the dataController.Save method: " + category.CategoryName);
updateWithReadUC.ExecuteMethod(dataController, "SaveCategory", new object[]{category});
CategoriesEntity tempCategory = readUpdatesBeforeCommit.ExecuteMethod(dataController, "FetchCategory", new object[]{1}) as CategoriesEntity;
Console.WriteLine("Category Name after dirty read in new TX: " + tempCategory.CategoryName);
RequiresTxManager.Rollback(updateWithReadUC);
tempCategory = readUpdatesBeforeCommit.ExecuteMethod(dataController, "FetchCategory", new object[]{1}) as CategoriesEntity;
Console.WriteLine("Category Name after rollback: " + tempCategory.CategoryName);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
RequiresTxManager.DisposeAll(updateWithReadUC, readUpdatesBeforeCommit);
}
}
Joined: 17-Aug-2003
great great stuff, Devildog!
I've the archive here, ready for uploading, there is one file in the archive (the strong key) which isn't supposed to be there I think. I've mailed you about that.