Rumah >pangkalan data >tutorial mysql >LINQ-to-SQL那点事~LINQ-to-SQL中的数据缓存与应对
回到目录 这个文章写的有点滞后了,呵呵,因为总想把之前不确定的东西确定了之后,再写这篇,之前的LINQ-to-SQL那点事,请点这里。 LINQ-to-SQL中的数据缓存与应对 Linq-to-SQL它是微软自己推出的一个轻量级的ORM框架,它很好地完成了与SQLSERVER数据库的映
回到目录
这个文章写的有点滞后了,呵呵,因为总想把之前不确定的东西确定了之后,再写这篇,之前的LINQ-to-SQL那点事,请点这里。
Linq-to-SQL它是微软自己推出的一个轻量级的ORM框架,它很好地完成了与SQLSERVER数据库的映射(它目前只支持SQLSERVER,也不会有以后的,因为微软不对它进行更新了),在使用它时,微软提出了“数据上下文”的概念,这个上下文(context)类似于HttpContext,RequestContext,是指对某种事物的完整的抽象,把对这种事物的操作都集成在上下文中。
Linq-to-SQL的上下文被称为DataContext,它进一步的封装了SQL语句,亮点在于它的查询上,支持延时查询,再配合linq的语法,使得开发人员在写代码时很优雅,代码表现力更强。
DataContext在性能方面提出了缓存的概念,它可以装查询出来的数据缓存到上下文中(这有时会产生并发问题),对于Insert,Update,Delete这类执行类操作也提供了缓存语句,每当SubmitChange方法被触发时,这时缓存的语句被一次性的提交到SQLSERVER,之后将当前上下文的缓存语句清空。
当你希望把延时的数据返回到表示层时,DataContext如果被dispose之后,这种操作是不被允许的,这是正确的,因为你的数据上下文可能在表示层方法执行前已经被dispose了,一般这种代码会被这样书写:
<span style="color: #0000ff;">public</span> IQueryable<order_info> GetOrder_Info(Expression<func style="color: #0000ff;">bool>><span style="color: #000000;"> predicate) { </span><span style="color: #0000ff;">using</span> (<span style="color: #0000ff;">var</span> datacontext = <span style="color: #0000ff;">new</span><span style="color: #000000;"> LinqDataContext()) { </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> datacontext.Where(predicate); } }</span></func></order_info>
这段代码在执行上当然是有问题的,使用了using关键字后,在方法return这数据上下文DataContext将会被dispose,这是正常的,而由于linq语句返回的是IQueryable延时结果集,它将不会立即执行,只有真正返回数据时才会通过DataContext与SQLSERVER进行交互,而在上层方法中,由于DataContext这时已经被dispose了,所以,语句最终会报异常。
面对这种问题,我们知道了它的原因,所以接下来就寻找一种解决方法,即不叫DataContext立即dispose的方法,你可能会很容易的想到“把using去掉不就可以了”,事实上,如果你对linq to sql了解的话,这种做法是不可取的,因为这样,你在业务逻辑层无法实现“复杂查询,linq join”(一般地,我们为每个DAL层的表对象写几个方法,可能是根据条件去查询数据的方法),为什么呢?因为,对于一个linq查询语句来说,你的数据上下文必须是同一个才行,如果用户业务使用一个上下文,而订单业务使用另一个上下文,那么,这两个业务进行组成查询时,就会出现不同数据上下文的问题。
代码可能是这样:
<span style="color: #0000ff;">var</span> linq =<span style="color: #0000ff;">from</span> user <span style="color: #0000ff;">in</span><span style="color: #000000;"> userBLL().GetModel() join order </span><span style="color: #0000ff;">in</span><span style="color: #000000;"> orderBLL().GetModel() on user.UserID equals order.UserID </span><span style="color: #0000ff;">select</span> <span style="color: #0000ff;">new</span><span style="color: #000000;"> user_Ext<br> { ... }</span>
为数据上下文添加一个工厂,用来生成由UI线程产生的数据上下文,再把这些上下文放在一个由UI线程作为键的字典里,当UI线程中的数据上下文在进行SubmitChange出现异常进,我们再将当然上下文dispose,并从数据上下文字典中移除它。
数据上下文工厂及数据上下文基类代码如下:
<span style="color: #808080;">///</span> <span style="color: #808080;"><summary></summary></span> <span style="color: #808080;">///</span><span style="color: #008000;"> 数据库建立工厂 </span><span style="color: #808080;">///</span><span style="color: #008000;"> Created By : 张占岭 </span><span style="color: #808080;">///</span><span style="color: #008000;"> Created Date:2011-10-14 </span><span style="color: #808080;">///</span><span style="color: #008000;"> Modify By: </span><span style="color: #808080;">///</span><span style="color: #008000;"> Modify Date: </span><span style="color: #808080;">///</span><span style="color: #008000;"> Modify Reason: </span><span style="color: #808080;">///</span> <span style="color: #808080;"></span> <span style="color: #0000ff;">internal</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> DbFactory { </span><span style="color: #0000ff;">#region</span> Fields <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">readonly</span> <span style="color: #0000ff;">string</span> strConn = System.Configuration.ConfigurationManager.ConnectionStrings[<span style="color: #800000;">"</span><span style="color: #800000;">test</span><span style="color: #800000;">"</span><span style="color: #000000;">].ToString(); </span><span style="color: #0000ff;">static</span><span style="color: #000000;"> System.Timers.Timer sysTimer; </span><span style="color: #0000ff;">volatile</span> <span style="color: #0000ff;">static</span> Dictionary<thread datacontext><span style="color: #000000;"> divDataContext; </span><span style="color: #0000ff;">#endregion</span> <span style="color: #0000ff;">#region</span> Constructors <span style="color: #0000ff;">static</span><span style="color: #000000;"> DbFactory() { divDataContext </span>= <span style="color: #0000ff;">new</span> Dictionary<thread datacontext><span style="color: #000000;">(); sysTimer </span>= <span style="color: #0000ff;">new</span> System.Timers.Timer(<span style="color: #800080;">10000</span><span style="color: #000000;">); sysTimer.AutoReset </span>= <span style="color: #0000ff;">true</span><span style="color: #000000;">; sysTimer.Enabled </span>= <span style="color: #0000ff;">true</span><span style="color: #000000;">; sysTimer.Elapsed </span>+= <span style="color: #0000ff;">new</span><span style="color: #000000;"> System.Timers.ElapsedEventHandler(sysTimer_Elapsed); sysTimer.Start(); } </span><span style="color: #0000ff;">#endregion</span> <span style="color: #0000ff;">#region</span> Private Methods <span style="color: #808080;">///</span> <span style="color: #808080;"><summary></summary></span> <span style="color: #808080;">///</span><span style="color: #008000;"> 清理DbContext上下文 </span><span style="color: #808080;">///</span> <span style="color: #808080;"></span> <span style="color: #808080;">///</span> <span style="color: #808080;"><param name="sender"></span> <span style="color: #808080;">///</span> <span style="color: #808080;"><param name="e"></span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span> sysTimer_Elapsed(<span style="color: #0000ff;">object</span><span style="color: #000000;"> sender, System.Timers.ElapsedEventArgs e) { List</span><thread> list =<span style="color: #000000;"> divDataContext.Keys .Where(item </span>=> item.ThreadState ==<span style="color: #000000;"> ThreadState.Stopped) .ToList(); </span><span style="color: #0000ff;">if</span> (list != <span style="color: #0000ff;">null</span> && list.Count > <span style="color: #800080;">0</span><span style="color: #000000;">) { </span><span style="color: #0000ff;">foreach</span> (<span style="color: #0000ff;">var</span> thread <span style="color: #0000ff;">in</span><span style="color: #000000;"> list) { </span><span style="color: #0000ff;">foreach</span> (<span style="color: #0000ff;">var</span> context <span style="color: #0000ff;">in</span><span style="color: #000000;"> divDataContext[thread]) { </span><span style="color: #0000ff;">if</span> (context != <span style="color: #0000ff;">null</span><span style="color: #000000;">) context.Dispose(); } } } } </span><span style="color: #0000ff;">#endregion</span> <span style="color: #0000ff;">#region</span> Public Methods <span style="color: #808080;">///</span> <span style="color: #808080;"><summary></summary></span> <span style="color: #808080;">///</span><span style="color: #008000;"> 通过工厂的制造模式获取相应的LINQ数据库连接对象 </span><span style="color: #808080;">///</span> <span style="color: #808080;"></span> <span style="color: #808080;">///</span> <span style="color: #808080;"><param name="dbName"></span><span style="color: #008000;">数据库名称(需要与真实数据库名称保持一致)</span><span style="color: #808080;"></span> <span style="color: #808080;">///</span> <span style="color: #808080;"><returns></returns></span><span style="color: #008000;">LINQ数据库连接对象</span><span style="color: #808080;"></span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> DataContext Intance(<span style="color: #0000ff;">string</span><span style="color: #000000;"> dbName) { </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> Intance(dbName, Thread.CurrentThread); } </span><span style="color: #808080;">///</span> <span style="color: #808080;"><summary></summary></span> <span style="color: #808080;">///</span><span style="color: #008000;"> 通过工厂的制造模式获取相应的LINQ数据库连接对象 </span><span style="color: #808080;">///</span> <span style="color: #808080;"></span> <span style="color: #808080;">///</span> <span style="color: #808080;"><param name="dbName"></span><span style="color: #008000;">数据库名称(需要与真实数据库名称保持一致)</span><span style="color: #808080;"></span> <span style="color: #808080;">///</span> <span style="color: #808080;"><param name="thread"></span><span style="color: #008000;">当前线程引用的对象</span><span style="color: #808080;"></span> <span style="color: #808080;">///</span> <span style="color: #808080;"><returns></returns></span><span style="color: #008000;">LINQ数据库连接对象</span><span style="color: #808080;"></span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> DataContext Intance(<span style="color: #0000ff;">string</span><span style="color: #000000;"> dbName, Thread thread) { </span><span style="color: #0000ff;">if</span> (!<span style="color: #000000;">divDataContext.Keys.Contains(thread)) { divDataContext.Add(thread, </span><span style="color: #0000ff;">new</span> DataContext[<span style="color: #800080;">3</span><span style="color: #000000;">]); } </span><span style="color: #0000ff;">if</span> (dbName.Equals(<span style="color: #800000;">"</span><span style="color: #800000;">test</span><span style="color: #800000;">"</span><span style="color: #000000;">)) { </span><span style="color: #0000ff;">if</span> (divDataContext[thread][<span style="color: #800080;">0</span>] == <span style="color: #0000ff;">null</span><span style="color: #000000;">) { divDataContext[thread][</span><span style="color: #800080;">0</span>] = <span style="color: #0000ff;">new</span><span style="color: #000000;"> DAL.dbDataContext(strConn); } </span><span style="color: #0000ff;">return</span> divDataContext[thread][<span style="color: #800080;">0</span><span style="color: #000000;">]; } </span><span style="color: #0000ff;">return</span> <span style="color: #0000ff;">null</span><span style="color: #000000;">; } </span><span style="color: #808080;">///</span> <span style="color: #808080;"><summary></summary></span> <span style="color: #808080;">///</span><span style="color: #008000;"> 手动清除数据上下文,根据线程 </span><span style="color: #808080;">///</span> <span style="color: #808080;"></span> <span style="color: #808080;">///</span> <span style="color: #808080;"><param name="thread"></span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> ClearContextByThread(Thread thread, DataContext db) { divDataContext.Remove(thread);</span><span style="color: #008000;">//</span><span style="color: #008000;">从线程字典中移除</span> db.Dispose();<span style="color: #008000;">//</span><span style="color: #008000;">释放数据资源</span> <span style="color: #000000;"> } </span><span style="color: #0000ff;">#endregion</span><span style="color: #000000;"> }</span></thread></thread></thread>
下面是DataContext基类,已经对SubmitChanges(SaveChanges)方法进行了优化,手动dispose上下文。
<span style="color: #808080;">///</span> <span style="color: #808080;"><summary></summary></span> <span style="color: #808080;">///</span><span style="color: #008000;"> Repository基类 </span><span style="color: #808080;">///</span><span style="color: #008000;"> 所有linqTosql上下文对象都继承它 </span><span style="color: #808080;">///</span> <span style="color: #808080;"></span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">abstract</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> ContextBase { </span><span style="color: #0000ff;">protected</span> DataContext _db { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">set</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">protected</span> IUnitOfWork UnitOfWork { <span style="color: #0000ff;">get</span>; <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">set</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> ContextBase(DataContext db) { _db </span>=<span style="color: #000000;"> db; UnitOfWork </span>=<span style="color: #000000;"> (IUnitOfWork)db; } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> SaveChanges() { ChangeSet cSet </span>=<span style="color: #000000;"> _db.GetChangeSet(); </span><span style="color: #0000ff;">if</span> ((cSet.Inserts.Count > <span style="color: #800080;">0</span> || cSet.Updates.Count > <span style="color: #800080;">0</span> || cSet.Deletes.Count > <span style="color: #800080;">0</span><span style="color: #000000;">) </span>&& !<span style="color: #000000;">UnitOfWork.IsNotSubmit) { </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { UnitOfWork.SaveChanges(); } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (System.Data.Linq.ChangeConflictException) { </span><span style="color: #0000ff;">foreach</span> (System.Data.Linq.ObjectChangeConflict occ <span style="color: #0000ff;">in</span><span style="color: #000000;"> _db.ChangeConflicts) { </span><span style="color: #008000;">//</span><span style="color: #008000;"> 使用当前数据库中的值,覆盖Linq缓存中实体对象的值 </span> <span style="color: #000000;"> occ.Resolve(System.Data.Linq.RefreshMode.OverwriteCurrentValues); </span><span style="color: #008000;">//</span><span style="color: #008000;"> 使用Linq缓存中实体对象的值,覆盖当前数据库中的值 </span> <span style="color: #000000;"> occ.Resolve(System.Data.Linq.RefreshMode.KeepCurrentValues); </span><span style="color: #008000;">//</span><span style="color: #008000;"> 只更新实体对象中改变的字段的值,其他的保留不变 </span> <span style="color: #000000;"> occ.Resolve(System.Data.Linq.RefreshMode.KeepChanges); } UnitOfWork.SaveChanges(); } </span><span style="color: #0000ff;">catch</span> (Exception)<span style="color: #008000;">//</span><span style="color: #008000;">如果出现异常,就从数据字典中清除这个键值对</span> <span style="color: #000000;"> { DbFactory.ClearContextByThread(System.Threading.Thread.CurrentThread, _db); } } } }</span>
下面是一个领域的repository基类,代码如下:
<span style="color: #808080;">///</span> <span style="color: #808080;"><summary></summary></span> <span style="color: #808080;">///</span><span style="color: #008000;"> Test数据库基类 </span><span style="color: #808080;">///</span><span style="color: #008000;"> Created By : 张占岭 </span><span style="color: #808080;">///</span><span style="color: #008000;"> Created Date:2011-10-14 </span><span style="color: #808080;">///</span><span style="color: #008000;"> Modify By: </span><span style="color: #808080;">///</span><span style="color: #008000;"> Modify Date: </span><span style="color: #808080;">///</span><span style="color: #008000;"> Modify Reason: </span><span style="color: #808080;">///</span> <span style="color: #808080;"></span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">abstract</span> <span style="color: #0000ff;">class</span><span style="color: #000000;"> TestBase : ContextBase { </span><span style="color: #0000ff;">#region</span> Constructors <span style="color: #0000ff;">public</span><span style="color: #000000;"> EEE114Base() : </span><span style="color: #0000ff;">this</span>(<span style="color: #0000ff;">null</span><span style="color: #000000;">) { } </span><span style="color: #0000ff;">public</span><span style="color: #000000;"> EEE114Base(IUnitOfWork db) : </span><span style="color: #0000ff;">base</span>((DataContext)db ?? DbFactory.Intance(<span style="color: #800000;">"</span><span style="color: #800000;">test</span><span style="color: #800000;">"</span><span style="color: #000000;">, Thread.CurrentThread)) { } </span><span style="color: #0000ff;">#endregion</span> <span style="color: #0000ff;">#region</span> Protected Properies <span style="color: #808080;">///</span> <span style="color: #808080;"><summary></summary></span> <span style="color: #808080;">///</span><span style="color: #008000;"> 可以使用的数据库连接对象 </span><span style="color: #808080;">///</span><span style="color: #008000;"> [xxb] </span><span style="color: #808080;">///</span> <span style="color: #808080;"></span> <span style="color: #0000ff;">protected</span><span style="color: #000000;"> dbDataContext db { </span><span style="color: #0000ff;">get</span><span style="color: #000000;"> { </span><span style="color: #0000ff;">return</span> (dbDataContext)<span style="color: #0000ff;">base</span><span style="color: #000000;">._db; } } </span><span style="color: #0000ff;">#endregion</span><span style="color: #000000;"> } }</span>
OK,这就是改善之后的linq to sql架构的核心代码,主要体现在生成数据上下文对象上,及如何去避免并发冲突的产生,而对于并发冲突我们会在另一篇文章中做详细的说明。敬请期待!
回到目录