|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Services
Chapters
Feature Zones
|
IntroductionCOM+ distributed transactions and NHibernate are two very powerful tools available to the .NET programmer. This article shows how to get the two to cooperate in a simple way. BackgroundDistributed transactions are my favorite part of using COM+. By opening up a distributed transaction, I can get a bunch of different databases, message queues, files, email, and almost anything else I can think of into the same transaction. The two-stage commit tells me that they all work or they all abort. And the transaction can be restarted from where it left off if a server goes down. NHibernate is another powerful tool that I enjoy using. If you are not familiar with this ORM (object-relational mapper), then click here to visit the main site. NHibernate is built off of standard ADO.NET and also has capabilities to use transactions. But a regular ADO.NET transaction is for one database connection only. A COM+ distributed transaction can span multiple data sources. I wanted my NHibernate code to participate in a distributed transaction. This article shows how I accomplished this. Using the codeThere are several things I do in my code that you should be aware of:
OrmManager classThis class is basically how I handle talking to NHibernate. It creates the configuration and gets the The real meat of the whole article centers around the So, to get our connections to participate in a distributed transaction, we simply have to call the private static void EnlistIfPossible(System.Data.IDbConnection conn)
{
if (ContextUtil.IsInTransaction)
{
MethodInfo mi = conn.GetType().GetMethod("EnlistDistributedTransaction",
BindingFlags.Public | BindingFlags.Instance);
if (mi != null)
{
mi.Invoke(conn, new object[] {
(System.EnterpriseServices.ITransaction)
ContextUtil.Transaction });
}
}
}
Pretty simple stuff. Now, whenever we open up an NHibernate session, we can enlist the database connection into the COM+ transaction. This is done for public void Save(object obj)
{
ISession session = SessionFactory.OpenSession();
try
{
EnlistIfPossible(session.Connection);
session.SaveOrUpdate(obj);
session.Flush();
if (ContextUtil.IsInTransaction)
ContextUtil.MyTransactionVote = TransactionVote.Commit;
}
catch
{
if (ContextUtil.IsInTransaction)
ContextUtil.MyTransactionVote = TransactionVote.Abort;
throw;
}
finally
{
if (session != null && session.IsOpen)
session.Close();
}
}
Note - I am by no means saying that it is OK to start a new session every time you do something in NHibernate. That's just plain slow. I did it in this example to illustrate the point that the connections are separate but still operate under the same distributed transaction. It's nice to not have to hold a transaction object yourself and just let COM+ handle it for you. TransactionController classThe Beginning and ending a transaction is a pretty standard chunk of COM+ code: public void BeginTransaction()
{
ServiceConfig sc = new ServiceConfig();
sc.Transaction = TransactionOption.RequiresNew;
ServiceDomain.Enter(sc);
ContextUtil.MyTransactionVote = TransactionVote.Commit;
...
}
public void EndTransaction()
{
if (ContextUtil.MyTransactionVote ==
TransactionVote.Commit)
ContextUtil.SetComplete();
else
ContextUtil.SetAbort();
ServiceDomain.Leave();
...
}
Test HarnessThe NUnit test harness illustrates how the code can be used: TransactionController tc = new TransactionController();
Table1 t1; // My NHibernate object
Table2 t2; // My NHibernate object
try
{
tc.BeginTransaction();
t1 = new Table1();
t1.Num = 1;
t2 = new Table2();
t2.Num = 2;
tc.Save(t1);
tc.Save(t2);
}
finally
{
tc.EndTransaction();
}
It's really that simple. COM+ and NHibernate free the developer from having to think about a lot of low-level programming. Of course, the real world isn't always this simple, but that's for another article. History
|
|||||||||||||||||||||||||||||||||||||||||||||||||||