ibatis是一个挺不错的半自动orm框架,从java移植到c#
在ibatis中是支持多线程操作的,但是这几天的使用过程中发现就框架本身任然存在一些问题,可能会让你对多线程的使用并不是那么的顺利
在我们查询了ibatis的帮助手册后,不难找到这样一段内容
由此我们可以知道在使用sqlmapper时,我们对sqlmapper.sessionstore进行初始化HybridWebThreadSessionStore便可以进行多线程的使用,原理是什么呢?让我们下面来进行分析
首先ibatis定义了ISqlMapper接口,在接口中定义了一系列的数据库操作,ibaits本身给出了ISqlmapper接口的实现SqlMapper。
在我们的代码中,我们仅仅需要调用SqlMapper的方法即可完成对数据库的各种DML、DQL操作
看一下SqlMapper的初始化我们会发现
1 public SqlMapper(IObjectFactory objectFactory, 2 AccessorFactory accessorFactory) 3 { 4 _typeHandlerFactory = new TypeHandlerFactory(); 5 _dbHelperParameterCache = new DBHelperParameterCache(); 6 _objectFactory = objectFactory; 7 _accessorFactory = accessorFactory; 8 9 _dataExchangeFactory = new DataExchangeFactory(_typeHandlerFactory, _objectFactory, accessorFactory);10 _id = HashCodeProvider.GetIdentityHashCode(this).ToString();11 _sessionStore = SessionStoreFactory.GetSessionStore(_id);12 }
在第11行代码进行了_sessionStore 的复制,跟踪代码进入GetSessionStore方法
1 static public ISessionStore GetSessionStore(string sqlMapperId) 2 { 3 if (System.Web.HttpContext.Current == null) 4 { 5 return new CallContextSessionStore(sqlMapperId); 6 } 7 else 8 { 9 return new WebSessionStore(sqlMapperId);10 }11 }
可以很容易判断出当System.Web.HttpContext.Current为null时候通过CallContextSessionStore方法获取sessionstore,反之则通过WebSessionStore获取
这里我们总结一下,其实sqlmapper的_sessionStore 类型为ISessionStore,而前面根据帮助文档我们知道的HybridWebThreadSessionStore类以及GetSessionStore方法中提到的CallContextSessionStore类、WebSessionStore类都是对抽象类AbstractSessionStore的继承,而AbstractSessionStore抽象类则是对ISessionStore的实现,这样我们知道这里所有的动作其实都是对_sessionStore 赋值,至于具体使用哪一个实现类是根据情景的不同而初始化不同的实现类,不难发现,其实是ibatis在这里采用策略模式的实现方式。
这个时候我们来看一下从ISessionStore到具体的三个实现类中的代码
首先是ISessionStore接口代码
1 public interface ISessionStore 2 { 3 ///4 /// Get the local session 5 /// 6 ISqlMapSession LocalSession 7 { 8 get; 9 }10 11 ///12 /// Store the specified session.13 /// 14 /// The session to store15 void Store(ISqlMapSession session);16 17 ///18 /// Remove the local session from the storage.19 /// 20 void Dispose();21 }
可以看到在6-9行,这里的ISqlMapSession即是我们需要了解的核心,不同的实现类对该属性的返回采用了不同的方式
CallContextSessionStore
1 public override ISqlMapSession LocalSession2 {3 get { return CallContext.GetData(sessionName) as SqlMapSession; }4 }
WebSessionStore
1 public override ISqlMapSession LocalSession2 {3 get4 {5 HttpContext currentContext = ObtainSessionContext();6 return currentContext.Items[sessionName] as SqlMapSession;7 }8 }
HybridWebThreadSessionStore
1 public override ISqlMapSession LocalSession 2 { 3 get 4 { 5 HttpContext currentContext = HttpContext.Current; 6 if (currentContext == null) 7 { 8 return CallContext.GetData(sessionName) as SqlMapSession; 9 }10 return currentContext.Items[sessionName] as SqlMapSession;11 }12 }
下面我们着重分析WebSessionStore的实现方式,由于在初始化实现类的过程判断中,我们不难发现当System.Web.HttpContext.Current不为null时,选择通过WebSessionStore来进行实例化,而WebSessionStore中的LocalSession属性我们可以看到直接通过HttpContext currentContext = ObtainSessionContext();对currentContext 进行赋值,跟踪ObtainSessionContext方法
1 private static HttpContext ObtainSessionContext()2 {3 HttpContext currentContext = HttpContext.Current;4 if (currentContext == null)5 {6 throw new IBatisNetException("WebSessionStore: Could not obtain reference to HttpContext");7 }8 return currentContext;9 }
终于找到了WebSessionStore: Could not obtain reference to HttpContext异常的来源,很奇怪的是在之前的实例化的过程中我们其实已经对HttpContext.Current进行过判断,这里怎么会出现为null的情况呢?
有这个疑问的话具体可以查看http://www.cnblogs.com/fish-li/archive/2013/04/06/3002940.html这里就不作详细的解释了
好了,问题终于找到了,下面就是如何去解决该问题,按照文档的说法是在使用sqlMap时通过sqlMap.SessionStore = new IBatisNet.DataMapper.SessionStore.HybridWebThreadSessionStore(sqlMap.Id);直接对SessionStore 进行赋值,但是问题来了,通过这样的方式在后面我们会发现还是会走到WebSessionStore中出现WebSessionStore: Could not obtain reference to HttpContext的异常,所以我在看了源码之后,发现其实WebSessionStore仅仅只是从currentContext.Items[sessionName]获取已经存储的SessionStore ,这样就好办了,于是我通过修改源码的方式彻底解决了这个问题
1 static public ISessionStore GetSessionStore(string sqlMapperId) 2 { 3 if (System.Web.HttpContext.Current == null) 4 { 5 return new CallContextSessionStore(sqlMapperId); 6 } 7 else 8 { 9 //return new WebSessionStore(sqlMapperId);10 return new HybridWebThreadSessionStore(sqlMapperId);11 }12 }