NHibernate - 分布式事务并提供您自己的连接会导致异常
当参与分布式事务并且您通过指定自己的连接对象打开会话时,NHibernate 会引发异常。
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._ThreadPoolWaitOrTimerCallback.PerformWaitOrTimerCallback
(Object state, Boolean timedOut)
以下代码将重现该问题;参考当前的FluentNibernate和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();
}
}
}
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._ThreadPoolWaitOrTimerCallback.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();
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
问题确实出在NHiberate 上。他们正在开发补丁。作为参考,可以在此处找到该问题
The issue is indeed with NHiberate. They're working on a patch. For reference, the issue can be found here