nhibernate事务有时不插入所有记录



在做了更多的研究并听取了这里的建议之后,我已经能够更接近于找出我的问题。我正在处理一个事务的NHibernate问题,它只在生产中被复制,因为当我们在本地测试时它不会发生。我们有2张桌子预订和房间,每1个预订入口应该有1个房间入口。所以如果我们有802个预订条目,我们应该有802个房间条目。在代码中,首先添加Booking,然后添加包含BookingID列的Room条目,这就是它与Booking表的关系。每周大约有两天我们会发现400个Booking条目和398个Room条目。我们测试了不同的场景并制造了一些错误,当发生这种情况时,所有的东西都被回滚,并显示在日志中。我们还在事务周围添加了日志记录,并且可以看到,即使没有插入Room条目,事务也已成功提交。是否有某种方式,我可以获得会话ID传递到日志记录器或调优当前代码中的东西?这个问题大约每200个请求发生一次。我在想,也许在这些情况下,我们可以有不同的会话,或者Nhibernate没有跟踪第二次插入查询的数据。现在我们在Application_EndRequest中处理会话,不确定在事务提交后显式地处理会话是否会解决任何问题。

// Repository
public class EntityRepository : IRepository
{
private readonly string connectionStringKey;
private NHibernate.ISession context;

protected NHibernate.ISession Context
{
get
{
return (this.context == null || !this.context.IsOpen) ?
(this.context = DataContextFactory.GetContextForConnection(this.connectionStringKey)) : this.context;
}
}

public void Add<TObject>(TObject obj) where TObject : class
{
Context.Save(obj);
}
public NHibernate.ITransaction CreateTransaction()
{
return Context.BeginTransaction(); // flushmode is auto as default
}

}
// Main Method

public Models.BookingRequestResult BookingRoom(Models.Booking booking)
{
ITransaction transaction = null;
try
{
booking.BookingAppID = CodeGenerator.UniqueIdGenerator.Generate(10);
transaction = repository.CreateTransaction(); 
logger.InfoFormat("Transaction Started for: {0}", booking.BookingAppID );
transaction.Begin(System.Data.IsolationLevel.Serializable);

repository.Add(booking); // booking added
logger.InfoFormat("Booking Added for {0}", booking.BookingAppID);

var RoomAppt = new Rooms { Booking = booking , Type="Room" };

repository.Add(RoomAppt); // room added and the BookingID is the ID of the newly created booking


transaction.Commit();
logger.InfoFormat("Booking: {0} has been committed", booking.BookingAppID); // This is in the logger even when the room does not get added 

...        
} catch(exception e) {
// any exceptions we log here and rollback transaction
}
}
// Creates SessionFactory for application
public static class DataContextFactory
{
private static IDictionary<string, NHibernate.ISessionFactory> sFactories = new Dictionary<string, NHibernate.ISessionFactory>();

public static NHibernate.ISession GetContextForConnection(string connectionStringKey = null)
{
if (string.IsNullOrEmpty(connectionStringKey)) connectionStringKey = GetCurrentContext();

var context = sContextStore[connectionStringKey];
if(context == null || !context.IsOpen)
{
lock (sCreateSyncRoot)
{
context = sContextStore[connectionStringKey];
if (context == null || !context.IsOpen)
{
if(sFactories.ContainsKey(connectionStringKey))
{
context = sFactories[connectionStringKey].OpenSession();
sContextStore[connectionStringKey] = context;
}
else
{
throw new ArgumentException("This connection string wasn't specified in Db Context configuration.");
}
}
}
}
return context;
}

public static void Initialize(System.Xml.Linq.XElement configNode = null)
{
lock (sInitSyncRoot)
{
if (sContextWrappers != null)
{
throw new InvalidOperationException("Context is already initialized");
}

if (configNode == null)
{
var configFile =
System.IO.Path.Combine(
System.Configuration.ConfigurationManager.AppSettings[ConfigurationTokens.ConfigurationPath],
"DataContextFactory.conf");
if (System.IO.File.Exists(configFile))
{
configNode = System.Xml.Linq.XElement.Load(configFile);

}
}
if (configNode == null)
{
throw new NullReferenceException("No configuration node or file was provided.");
}

var attrib = configNode.Attribute("storeType");
if (attrib == null)
{
throw new NullReferenceException("Store type is undefined");
}
sContextStore = Utilities.ReflectionUtility.Get<IContextStore>(attrib.Value);

attrib = configNode.Attribute("defaultConnectionStringKey");
if (attrib != null)
{
sDefaultConnectionString = attrib.Value;
}

foreach (var dbContext in configNode.Elements("DbContext"))
{
var dbConnectionStringAttribute = dbContext.Attribute("connectionStringKey");
if (dbConnectionStringAttribute == null || string.IsNullOrWhiteSpace(dbConnectionStringAttribute.Value))
{
throw new ArgumentException("No connection string key specified in DataContextFactory.conf.");
}
FluentConfiguration configuration = 
ConfigureWrapper(dbContext, dbConnectionStringAttribute.Value);
configuration.Mappings(c => {
c.FluentMappings.Conventions.Add<EnumConvention>();
});
AddMappingAssemblies(dbContext, configuration);
sFactories.Add(dbConnectionStringAttribute.Value, configuration.BuildSessionFactory());

}
}
}


public static void DeInitialize()
{
if (sContextStore != null)
{
var ctxStores = sContextStore.GetAll();
foreach (var ctx in ctxStores)
{
if (ctx.IsOpen)
{
var objCtx = ctx.Close();
}
ctx.Dispose();
}
}
}

} 

// Global.asax

protected void Application_EndRequest(object sender, EventArgs args)
{
PatientPoint.Infrastructure.Data.DataContextFactory.DeInitialize();
} 

您的repository.CreateTransaction调用ISession.BeginTransaction-它以默认隔离级别打开事务。然后尝试使用指定的显式隔离级别打开事务:

transaction = repository.CreateTransaction(); 
logger.InfoFormat("Transaction Started for: {0}", booking.BookingAppID );
transaction.Begin(System.Data.IsolationLevel.Serializable); //<-- THIS LINE LOOKS WRONG

很可能不支持这样的多个begin事务调用。如来源所述:

一个特定的会话在同一时间最多有一个未提交的ittransaction。

你只需要调用一次ISession.BeginTransaction(isolationLevel),不要在开放的事务中调用ITransaction.Begin

相关内容

  • 没有找到相关文章

最新更新