NHibernate - Distributed transactions and providing your own connection result in exception
NHibernate is throwning an exception when particpating in a distirbuted transaction and you've opened a session by specifying your own connection object.
Unhandled Exception: System.InvalidOperationException: Disconnect cannot be call
ed while a transaction is in progress.
at NHibernate.AdoNet.ConnectionManager.Disconnect()
at NHibernate.Impl.SessionImpl.Close()
at NHibernate.Impl.SessionImpl.Dispose(Boolean isDisposing)
at NHibernate.Transaction.AdoNetWithDistrubtedTransactionFactory.<>c__Display
Class1.<EnlistInDistributedTransactionIfNeeded>b__0(Object sender, TransactionEv
entArgs e)
at System.Transactions.TransactionCompletedEventHandler.Invoke(Object sender,
TransactionEventArgs e)
at System.Transactions.TransactionStatePromotedCommitted.EnterState(InternalT
ransaction tx)
at System.Transactions.InternalTransaction.DistributedTransactionOutcome(Inte
rnalTransaction tx, TransactionStatus status)
at System.Transactions.Oletx.RealOletxTransaction.FireOutcome(TransactionStat
us statusArg)
at System.Transactions.Oletx.OutcomeEnlistment.InvokeOutcomeFunction(Transact
ionStatus status)
at System.Transactions.Oletx.OletxTransactionManager.ShimNotificationCallback
(Object state, Boolean timeout)
at System.Threading._ThreadPoolWaitOrTimerCallba开发者_高级运维ck.PerformWaitOrTimerCallback
(Object state, Boolean timedOut)
The following code will reproduce the issue; reference the current FluentNibernate, and NHibernate 2.1.2.4000.
using System;
using System.Data.SqlClient;
using System.Transactions;
using FluentNHibernate.Mapping;
using FluentNHibernate.Cfg.Db;
using FluentNHibernate.Cfg;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
var cfg =
Fluently.Configure().Database(
MsSqlConfiguration.MsSql2008.ConnectionString("Integrated Security=SSPI;Data Source=.;Initial Catalog=Test").DefaultSchema("dbo")
).Mappings(x => x.FluentMappings.AddFromAssemblyOf<MyTableMap>()).BuildConfiguration();
using (var sf = cfg.BuildSessionFactory())
{
using (var ts = new TransactionScope().PromoteToDtc())
{
using (var conn = new SqlConnection("Integrated Security=SSPI;Data Source=.;Initial Catalog=Test"))
{
conn.Open();
using (var session = sf.OpenSession(conn))
{
session.Save(new MyTable { String = "Hello!" });
}
}
ts.Complete();
}
Console.WriteLine("It saved!");
Console.ReadLine();
}
}
}
public class DummyEnlistmentNotification : IEnlistmentNotification
{
public static readonly Guid Id = new Guid("E2D35055-4187-4ff5-82A1-F1F161A008D0");
public void Prepare(PreparingEnlistment preparingEnlistment)
{
preparingEnlistment.Prepared();
}
public void Commit(Enlistment enlistment)
{
enlistment.Done();
}
public void Rollback(Enlistment enlistment)
{
enlistment.Done();
}
public void InDoubt(Enlistment enlistment)
{
enlistment.Done();
}
}
public static class TSExetensions
{
public static TransactionScope PromoteToDtc(this TransactionScope scope)
{
Transaction.Current.EnlistDurable(DummyEnlistmentNotification.Id, new DummyEnlistmentNotification(), EnlistmentOptions.None);
return scope;
}
}
public class MyTable
{
public virtual int Id { get; private set; }
public virtual string String { get; set; }
}
public sealed class MyTableMap : ClassMap<MyTable>
{
public MyTableMap()
{
Id(x => x.Id).GeneratedBy.Native();
Map(x => x.String).Not.Nullable();
}
}
}
The issue is indeed with NHiberate. They're working on a patch. For reference, the issue can be found here
精彩评论