我有我的webapp一个NHibernate的竞争状态的问题。
我使用旧版本的log4net时(应固定在1.2.10),虽然我也遇到过很了解这种情况发生的。 正因为如此,我们已经禁用log4net的现在,由于竞争条件崩溃IIS,它是不能接受这种在生产中发生。 加载实体(见下面的堆栈跟踪),当这件事发生。 除此之外,类似的问题似乎都发生在RavenDB,看到这个链接 ,并举例没有NHibernate的位置链接 。
堆栈跟踪:
Server Error in '/' Application.
Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.IndexOutOfRangeException: Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader.
Source Error:
Line 105:
Line 106: if(webUser.Id > 0) { // logged in
Line 107: _user = session.Get<User>(webUser.Id);
Line 108: if(_user == null) { // session exists, but no user in DB with this id
Line 109: new SessionInit().Remove();
Source File: \App_Code\SessionInit.cs Line: 107
Stack Trace:
[IndexOutOfRangeException: Probable I/O race condition detected while copying memory. The I/O package is not thread safe by default. In multithreaded applications, a stream must be accessed in a thread-safe way, such as a thread-safe wrapper returned by TextReader's or TextWriter's Synchronized methods. This also applies to classes like StreamWriter and StreamReader.]
System.Buffer.InternalBlockCopy(Array src, Int32 srcOffsetBytes, Array dst, Int32 dstOffsetBytes, Int32 byteCount) +0
System.IO.StreamWriter.Write(Char[] buffer, Int32 index, Int32 count) +117
System.IO.TextWriter.WriteLine(String value) +204
System.IO.SyncTextWriter.WriteLine(String value) +63
NHibernate.AdoNet.AbstractBatcher.ExecuteReader(IDbCommand cmd) +71
NHibernate.Loader.Loader.GetResultSet(IDbCommand st, Boolean autoDiscoverTypes, Boolean callable, RowSelection selection, ISessionImplementor session) +580
NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +275
NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +205
NHibernate.Loader.Loader.LoadEntity(ISessionImplementor session, Object id, IType identifierType, Object optionalObject, String optionalEntityName, Object optionalIdentifier, IEntityPersister persister) +590
[GenericADOException: could not load an entity: [app.Presentation.User#338][SQL: SELECT user0_.userID as userID24_0_, user0_.instituteID as institut2_24_0_, user0_.email as email24_0_, user0_.password as password24_0_, user0_.username as username24_0_, user0_.mod_remarks as mod6_24_0_, user0_.lastLogin as lastLogin24_0_, user0_.active as active24_0_, user0_.isAcademic as isAcademic24_0_, user0_.created as created24_0_, (select p.firstName from ej_profile p where p.userID = user0_.userID) as formula11_0_, (select p.lastName from ej_profile p where p.userID = user0_.userID) as formula12_0_, (select p.timeZone from ej_profile p where p.userID = user0_.userID) as formula13_0_ FROM ej_user user0_ WHERE user0_.userID=?]]
NHibernate.Loader.Loader.LoadEntity(ISessionImplementor session, Object id, IType identifierType, Object optionalObject, String optionalEntityName, Object optionalIdentifier, IEntityPersister persister) +960
NHibernate.Loader.Entity.AbstractEntityLoader.Load(ISessionImplementor session, Object id, Object optionalObject, Object optionalId) +76
NHibernate.Loader.Entity.AbstractEntityLoader.Load(Object id, Object optionalObject, ISessionImplementor session) +32
NHibernate.Event.Default.DefaultLoadEventListener.LoadFromDatasource(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) +173
NHibernate.Event.Default.DefaultLoadEventListener.Load(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) +181
NHibernate.Event.Default.DefaultLoadEventListener.OnLoad(LoadEvent event, LoadType loadType) +1019
NHibernate.Impl.SessionImpl.FireLoad(LoadEvent event, LoadType loadType) +403
NHibernate.Impl.SessionImpl.Get(String entityName, Object id) +469
NHibernate.Impl.SessionImpl.Get(Type entityClass, Object id) +374
NHibernate.Impl.SessionImpl.Get(Object id) +391
SessionInit.GetCurrentUser(ISession session) in j:\dev\app\app_wwwroot\App_Code\SessionInit.cs:107
DynamicPage.OnPreInit(EventArgs e) in j:\dev\app\app_wwwroot\App_Code\DynamicPage.cs:24
MemberPage.OnPreInit(EventArgs e) in j:\dev\app\app_wwwroot\App_Code\MemberPage.cs:20
members_stocks_Default.OnPreInit(EventArgs e) in j:\dev\app\app_wwwroot\members\Default.aspx.cs:28
System.Web.UI.Page.PerformPreInit() +49
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1716
对于用户的映射:
public class UserViewMapping : ClassMap<User>
{
public UserViewMapping() {
Table("ej_user");
Id(s => s.Id, "userID").GeneratedBy.Native();
Map(s => s.InstituteId, "instituteID");
Map(s => s.Email, "email");
Map(s => s.Password, "password");
Map(s => s.Name, "username");
Map(s => s.ModRemarks, "mod_remarks");
Map(s => s.LastLogin, "lastLogin");
Map(s => s.Active, "active");
Map(s => s.IsAcademic, "isAcademic");
Map(s => s.Created, "created");
Map(s => s.FirstName).Formula("(select p.firstName from ej_profile p where p.userID = userID)");
Map(s => s.LastName).Formula("(select p.lastName from ej_profile p where p.userID = userID)");
Map(s => s.TimeZone).Formula("(select p.timeZone from ej_profile p where p.userID = userID)");
HasMany<ProfileViewModel>(s => s.Profiles)
.Table("ej_profile")
.KeyColumn("userID")
.Cascade.All()
.Inverse();
}
一些细节:我使用的查询和命令(和两个会话工厂)两会,因为我用的是CQRS般有所模式。 阅读对象,一个更改(这有助于我保持我的域模型简单视图模型和映射在命令模式可能不同)的一个会议。
装载在我的开发环境的用户视图模型(单用户)时发生的竞争状态,但我们确信,这将永远不会在生产发生,因为它坠毁IIS 7,此外,在生产中会有多个用户,所以也许错误会更频繁地发生可能。
另外,我们有很多的,它使用System.Data和MySql.Data.MySqlClient.MySqlDataAdapter读取/写入到数据库中的遗留代码。 这会不会影响?
我使用NHibernate的3.1.0(将会升级到3.3.1GA,但是这是很难再现),以及fluentNhibernate我的映射。
该sessionfactories是在Global.asax创建:
void Application_Start(object sender, EventArgs e)
{
QuerySessionFactory.Create(connectionString);
CommandSessionManager.Initialize(connString);
}
我的网页从我DynamicPage其中查询会话打开继承和关闭:
public class DynamicPage : System.Web.UI.Page
{
protected override void OnPreInit(EventArgs e)
{
Session = QuerySessionFactory.Instance.OpenSession();
}
protected override void OnUnload(EventArgs e) {
base.OnUnload(e);
Session.Close();
}
}
在SessionInit(从httpcontext.session读取用户ID,并创建一个“WEBUSER”,与像用户id一些简单的信息的用户)。 后来,我身边有把锁,并且在完成交易,不知道这是否将是有益的用户GET请求。
public IUser GetCurrentUser(ISession session) {
if(_user == null) {
var webUser = new SessionInit().Get;
if(webUser.Id > 0) { // logged in
lock(_lock) {
using(var tx = session.BeginTransaction()) {
_user = session.Get<User>(webUser.Id);
tx.Commit();
}
}
if(_user == null) { // session exists, but no user in DB with this id
new SessionInit().Remove();
}
((User)_user)._currentUser = webUser;
} else {
if(webUser is CurrentUser && webUser.Id == 0) {
if(HttpContext.Current.Session != null) {
HttpContext.Current.Response.Cookies.Remove("ASPSESSID");
HttpContext.Current.Request.Cookies.Remove("ASPSESSID");
HttpContext.Current.Session.RemoveAll();
HttpContext.Current.Session.Abandon();
}
if(HttpContext.Current.Request.Url.Host.Contains("members"))
HttpContext.Current.Response.Redirect("/login");
} else
if(webUser.Id == 0) {
var userId = webUser.Id;
var userName = webUser.UserName;
var loginUrl = webUser.LoginUrl;
var clientIp = webUser.ClientIp;
var isAdmin = webUser.IsAdmin();
return new eLab.Presentation.Visitor(userId, userName, loginUrl, clientIp, isAdmin, webUser.Theme);
}
}
if (_user == null)
return new eLab.Presentation.Visitor(webUser.Id, webUser.UserName, webUser.LoginUrl, webUser.ClientIp, false, webUser.Theme);
}
return _user;
}
命令会话被打开,并在需要时使用一个封闭块。
据堆栈跟踪,在StreamWriter的出现问题 - > System.Buffer,这是再次System.IO.SyncTextWriter,这应该是各地System.IO.TextWriter线程安全的包装调用。
由于这件事发生在TextWriter的,是有办法解决这个问题,使用一个线程的TextWriter?
它是安全的打开和关闭的会议上,我做它在DynamicPage的方式吗?
因为这显然是难以重现,如何做到这一点也欢迎任何想法。
[更新] NHibernate的探查告知,我们还打开和关闭会话(在使用块)在母版页,因为它需要检查一些权限,为当前用户,所以两会中每个请求打开。 我已经重构它,所以它现在不是在一个页面超打开会话,它会打开在的Application_BeginRequest在Global.asax了会议,并再次关闭它Application_EndRequest,其中会话放在HttpContext.Current.Items。
但测试没有确切方法,如果这修复它。