您好,欢迎访问一九零五行业门户网

LINQ-to-SQL那点事~LINQ-to-SQL中的数据缓存与应对

回到目录 这个文章写的有点滞后了,呵呵,因为总想把之前不确定的东西确定了之后,再写这篇,之前的linq-to-sql那点事,请点这里。 linq-to-sql中的数据缓存与应对 linq-to-sql它是微软自己推出的一个轻量级的orm框架,它很好地完成了与sqlserver数据库的映
回到目录
这个文章写的有点滞后了,呵呵,因为总想把之前不确定的东西确定了之后,再写这篇,之前的linq-to-sql那点事,请点这里。
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了,一般这种代码会被这样书写:
public iqueryable getorder_info(expressionbool>> predicate) { using (var datacontext = new linqdatacontext()) { return datacontext.where(predicate); } }
这段代码在执行上当然是有问题的,使用了using关键字后,在方法return这数据上下文datacontext将会被dispose,这是正常的,而由于linq语句返回的是iqueryable延时结果集,它将不会立即执行,只有真正返回数据时才会通过datacontext与sqlserver进行交互,而在上层方法中,由于datacontext这时已经被dispose了,所以,语句最终会报异常。
面对这种问题,我们知道了它的原因,所以接下来就寻找一种解决方法,即不叫datacontext立即dispose的方法,你可能会很容易的想到“把using去掉不就可以了”,事实上,如果你对linq to sql了解的话,这种做法是不可取的,因为这样,你在业务逻辑层无法实现“复杂查询,linq join”(一般地,我们为每个dal层的表对象写几个方法,可能是根据条件去查询数据的方法),为什么呢?因为,对于一个linq查询语句来说,你的数据上下文必须是同一个才行,如果用户业务使用一个上下文,而订单业务使用另一个上下文,那么,这两个业务进行组成查询时,就会出现不同数据上下文的问题。
代码可能是这样:
var linq =from user in userbll().getmodel() join order in orderbll().getmodel() on user.userid equals order.userid select new user_ext
{ ... }
为数据上下文添加一个工厂,用来生成由ui线程产生的数据上下文,再把这些上下文放在一个由ui线程作为键的字典里,当ui线程中的数据上下文在进行submitchange出现异常进,我们再将当然上下文dispose,并从数据上下文字典中移除它。
数据上下文工厂及数据上下文基类代码如下:
/// /// 数据库建立工厂 /// created by : 张占岭 /// created date:2011-10-14 /// modify by: /// modify date: /// modify reason: /// internal static class dbfactory { #region fields static readonly string strconn = system.configuration.configurationmanager.connectionstrings[test].tostring(); static system.timers.timer systimer; volatile static dictionary divdatacontext; #endregion #region constructors static dbfactory() { divdatacontext = new dictionary(); systimer = new system.timers.timer(10000); systimer.autoreset = true; systimer.enabled = true; systimer.elapsed += new system.timers.elapsedeventhandler(systimer_elapsed); systimer.start(); } #endregion #region private methods /// /// 清理dbcontext上下文 /// /// /// static void systimer_elapsed(object sender, system.timers.elapsedeventargs e) { list list = divdatacontext.keys .where(item => item.threadstate == threadstate.stopped) .tolist(); if (list != null && list.count > 0) { foreach (var thread in list) { foreach (var context in divdatacontext[thread]) { if (context != null) context.dispose(); } } } } #endregion #region public methods /// /// 通过工厂的制造模式获取相应的linq数据库连接对象 /// /// 数据库名称(需要与真实数据库名称保持一致) /// linq数据库连接对象 public static datacontext intance(string dbname) { return intance(dbname, thread.currentthread); } /// /// 通过工厂的制造模式获取相应的linq数据库连接对象 /// /// 数据库名称(需要与真实数据库名称保持一致) /// 当前线程引用的对象 /// linq数据库连接对象 public static datacontext intance(string dbname, thread thread) { if (!divdatacontext.keys.contains(thread)) { divdatacontext.add(thread, new datacontext[3]); } if (dbname.equals(test)) { if (divdatacontext[thread][0] == null) { divdatacontext[thread][0] = new dal.dbdatacontext(strconn); } return divdatacontext[thread][0]; }return null; } /// /// 手动清除数据上下文,根据线程 /// /// public static void clearcontextbythread(thread thread, datacontext db) { divdatacontext.remove(thread);//从线程字典中移除 db.dispose();//释放数据资源 } #endregion }
下面是datacontext基类,已经对submitchanges(savechanges)方法进行了优化,手动dispose上下文。
/// /// repository基类 /// 所有linqtosql上下文对象都继承它 /// public abstract class contextbase { protected datacontext _db { get; private set; } protected iunitofwork unitofwork { get; private set; } public contextbase(datacontext db) { _db = db; unitofwork = (iunitofwork)db; } public void savechanges() { changeset cset = _db.getchangeset(); if ((cset.inserts.count > 0 || cset.updates.count > 0 || cset.deletes.count > 0) && !unitofwork.isnotsubmit) { try { unitofwork.savechanges(); } catch (system.data.linq.changeconflictexception) { foreach (system.data.linq.objectchangeconflict occ in _db.changeconflicts) { // 使用当前数据库中的值,覆盖linq缓存中实体对象的值 occ.resolve(system.data.linq.refreshmode.overwritecurrentvalues); // 使用linq缓存中实体对象的值,覆盖当前数据库中的值 occ.resolve(system.data.linq.refreshmode.keepcurrentvalues); // 只更新实体对象中改变的字段的值,其他的保留不变 occ.resolve(system.data.linq.refreshmode.keepchanges); } unitofwork.savechanges(); } catch (exception)//如果出现异常,就从数据字典中清除这个键值对 { dbfactory.clearcontextbythread(system.threading.thread.currentthread, _db); } } } }
下面是一个领域的repository基类,代码如下:
/// /// test数据库基类 /// created by : 张占岭 /// created date:2011-10-14 /// modify by: /// modify date: /// modify reason: /// public abstract class testbase : contextbase { #region constructors public eee114base() : this(null) { } public eee114base(iunitofwork db) : base((datacontext)db dbfactory.intance(test, thread.currentthread)) { } #endregion #region protected properies /// /// 可以使用的数据库连接对象 /// [xxb] /// protected dbdatacontext db { get { return (dbdatacontext)base._db; } } #endregion }}
ok,这就是改善之后的linq to sql架构的核心代码,主要体现在生成数据上下文对象上,及如何去避免并发冲突的产生,而对于并发冲突我们会在另一篇文章中做详细的说明。敬请期待!
回到目录
其它类似信息

推荐信息