回到目录

这个文章写的有点滞后了,呵呵,因为总想把之前不确定的东西确定了之后,再写这篇,之前的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了,一般这种代码会被这样书写:

  1. public IQueryable<Order_Info> GetOrder_Info(Expression<Func<Order_Info, bool>> predicate)
  2. {
  3. using (var datacontext = new LinqDataContext())
  4. {
  5. return datacontext.Where(predicate);
  6. }
  7. }

这段代码在执行上当然是有问题的,使用了using关键字后,在方法return这数据上下文DataContext将会被dispose,这是正常的,而由于linq语句返回的是IQueryable延时结果集,它将不会立即执行,只有真正返回数据时才会通过DataContext与SQLSERVER进行交互,而在上层方法中,由于DataContext这时已经被dispose了,所以,语句最终会报异常。

面对这种问题,我们知道了它的原因,所以接下来就寻找一种解决方法,即不叫DataContext立即dispose的方法,你可能会很容易的想到“把using去掉不就可以了”,事实上,如果你对linq to sql了解的话,这种做法是不可取的,因为这样,你在业务逻辑层无法实现“复杂查询,linq join”(一般地,我们为每个DAL层的表对象写几个方法,可能是根据条件去查询数据的方法),为什么呢?因为,对于一个linq查询语句来说,你的数据上下文必须是同一个才行,如果用户业务使用一个上下文,而订单业务使用另一个上下文,那么,这两个业务进行组成查询时,就会出现不同数据上下文的问题。

代码可能是这样:

  1. var linq =from user in userBLL().GetModel()
  2. join order in orderBLL().GetModel() on user.UserID equals order.UserID
  3. select new user_Ext
    {
  4. ...
  5. }

为数据上下文添加一个工厂,用来生成由UI线程产生的数据上下文,再把这些上下文放在一个由UI线程作为键的字典里,当UI线程中的数据上下文在进行SubmitChange出现异常进,我们再将当然上下文dispose,并从数据上下文字典中移除它。

数据上下文工厂及数据上下文基类代码如下:

  1. /// <summary>
  2. /// 数据库建立工厂
  3. /// Created By : 张占岭
  4. /// Created Date:2011-10-14
  5. /// Modify By:
  6. /// Modify Date:
  7. /// Modify Reason:
  8. /// </summary>
  9. internal static class DbFactory
  10. {
  11. #region Fields
  12. static readonly string strConn = System.Configuration.ConfigurationManager.ConnectionStrings["test"].ToString();
  13. static System.Timers.Timer sysTimer;
  14. volatile static Dictionary<Thread, DataContext[]> divDataContext;
  15. #endregion
  16.  
  17. #region Constructors
  18. static DbFactory()
  19. {
  20. divDataContext = new Dictionary<Thread, DataContext[]>();
  21. sysTimer = new System.Timers.Timer();
  22. sysTimer.AutoReset = true;
  23. sysTimer.Enabled = true;
  24. sysTimer.Elapsed += new System.Timers.ElapsedEventHandler(sysTimer_Elapsed);
  25. sysTimer.Start();
  26. }
  27. #endregion
  28.  
  29. #region Private Methods
  30. /// <summary>
  31. /// 清理DbContext上下文
  32. /// </summary>
  33. /// <param name="sender"></param>
  34. /// <param name="e"></param>
  35. static void sysTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
  36. {
  37. List<Thread> list = divDataContext.Keys
  38. .Where(item => item.ThreadState == ThreadState.Stopped)
  39. .ToList();
  40. if (list != null && list.Count > )
  41. {
  42. foreach (var thread in list)
  43. {
  44. foreach (var context in divDataContext[thread])
  45. {
  46. if (context != null)
  47. context.Dispose();
  48. }
  49. }
  50. }
  51. }
  52. #endregion
  53.  
  54. #region Public Methods
  55. /// <summary>
  56. /// 通过工厂的制造模式获取相应的LINQ数据库连接对象
  57. /// </summary>
  58. /// <param name="dbName">数据库名称(需要与真实数据库名称保持一致)</param>
  59. /// <returns>LINQ数据库连接对象</returns>
  60. public static DataContext Intance(string dbName)
  61. {
  62. return Intance(dbName, Thread.CurrentThread);
  63. }
  64.  
  65. /// <summary>
  66. /// 通过工厂的制造模式获取相应的LINQ数据库连接对象
  67. /// </summary>
  68. /// <param name="dbName">数据库名称(需要与真实数据库名称保持一致)</param>
  69. /// <param name="thread">当前线程引用的对象</param>
  70. /// <returns>LINQ数据库连接对象</returns>
  71. public static DataContext Intance(string dbName, Thread thread)
  72. {
  73.  
  74. if (!divDataContext.Keys.Contains(thread))
  75. {
  76. divDataContext.Add(thread, new DataContext[]);
  77. }
  78.  
  79. if (dbName.Equals("test"))
  80. {
  81. if (divDataContext[thread][] == null)
  82. {
  83. divDataContext[thread][] = new DAL.dbDataContext(strConn);
  84. }
  85. return divDataContext[thread][];
  86. }
  87.  
  88. return null;
  89.  
  90. }
  91.  
  92. /// <summary>
  93. /// 手动清除数据上下文,根据线程
  94. /// </summary>
  95. /// <param name="thread"></param>
  96. public static void ClearContextByThread(Thread thread, DataContext db)
  97. {
  98. divDataContext.Remove(thread);//从线程字典中移除
  99. db.Dispose();//释放数据资源
  100. }
  101. #endregion
  102.  
  103. }

下面是DataContext基类,已经对SubmitChanges(SaveChanges)方法进行了优化,手动dispose上下文。

  1. /// <summary>
  2. /// Repository基类
  3. /// 所有linqTosql上下文对象都继承它
  4. /// </summary>
  5. public abstract class ContextBase
  6. {
  7. protected DataContext _db { get; private set; }
  8. protected IUnitOfWork UnitOfWork { get; private set; }
  9. public ContextBase(DataContext db)
  10. {
  11. _db = db;
  12. UnitOfWork = (IUnitOfWork)db;
  13. }
  14. public void SaveChanges()
  15. {
  16. ChangeSet cSet = _db.GetChangeSet();
  17. if ((cSet.Inserts.Count >
  18. || cSet.Updates.Count >
  19. || cSet.Deletes.Count > )
  20. && !UnitOfWork.IsNotSubmit)
  21. {
  22. try
  23. {
  24. UnitOfWork.SaveChanges();
  25. }
  26. catch (System.Data.Linq.ChangeConflictException)
  27. {
  28. foreach (System.Data.Linq.ObjectChangeConflict occ in _db.ChangeConflicts)
  29. {
  30. // 使用当前数据库中的值,覆盖Linq缓存中实体对象的值
  31. occ.Resolve(System.Data.Linq.RefreshMode.OverwriteCurrentValues);
  32. // 使用Linq缓存中实体对象的值,覆盖当前数据库中的值
  33. occ.Resolve(System.Data.Linq.RefreshMode.KeepCurrentValues);
  34. // 只更新实体对象中改变的字段的值,其他的保留不变
  35. occ.Resolve(System.Data.Linq.RefreshMode.KeepChanges);
  36. }
  37. UnitOfWork.SaveChanges();
  38. }
  39. catch (Exception)//如果出现异常,就从数据字典中清除这个键值对
  40. {
  41. DbFactory.ClearContextByThread(System.Threading.Thread.CurrentThread, _db);
  42. }
  43. }
  44. }
  45. }

下面是一个领域的repository基类,代码如下:

  1. /// <summary>
  2. /// Test数据库基类
  3. /// Created By : 张占岭
  4. /// Created Date:2011-10-14
  5. /// Modify By:
  6. /// Modify Date:
  7. /// Modify Reason:
  8. /// </summary>
  9. public abstract class TestBase : ContextBase
  10. {
  11. #region Constructors
  12. public EEE114Base()
  13. : this(null)
  14. { }
  15.  
  16. public EEE114Base(IUnitOfWork db)
  17. : base((DataContext)db ?? DbFactory.Intance("test", Thread.CurrentThread))
  18. { }
  19. #endregion
  20.  
  21. #region Protected Properies
  22. /// <summary>
  23. /// 可以使用的数据库连接对象
  24. /// [xxb]
  25. /// </summary>
  26. protected dbDataContext db
  27. {
  28. get
  29. {
  30. return (dbDataContext)base._db;
  31. }
  32. }
  33.  
  34. #endregion
  35. }
  36. }

OK,这就是改善之后的linq to sql架构的核心代码,主要体现在生成数据上下文对象上,及如何去避免并发冲突的产生,而对于并发冲突我们会在另一篇文章中做详细的说明。敬请期待!

回到目录

LINQ-to-SQL那点事~LINQ-to-SQL中的数据缓存与应对的更多相关文章

  1. PL/SQL那点事-->SqlSession operation; SQL []; ORA-01722: 无效数字

    PL/SQL那点事-->SqlSession operation;SQL []; ORA-01722: 无效数字 出现这种情况,在网上查了很多方法:大致主要有两种方法帮助我们解决这个问题: 1. ...

  2. 使用Hive或Impala执行SQL语句,对存储在HBase中的数据操作

    CSSDesk body { background-color: #2574b0; } /*! zybuluo */ article,aside,details,figcaption,figure,f ...

  3. 使用Hive或Impala执行SQL语句,对存储在Elasticsearch中的数据操作(二)

    CSSDesk body { background-color: #2574b0; } /*! zybuluo */ article,aside,details,figcaption,figure,f ...

  4. 使用Hive或Impala执行SQL语句,对存储在Elasticsearch中的数据操作

    http://www.cnblogs.com/wgp13x/p/4934521.html 内容一样,样式好的版本. 使用Hive或Impala执行SQL语句,对存储在Elasticsearch中的数据 ...

  5. sql语句 怎么从一张表中查询数据插入到另一张表中?

    sql语句 怎么从一张表中查询数据插入到另一张表中?  ----原文地址:http://www.phpfans.net/ask/MTc0MTQ4Mw.html 比如我有两张表 table1 字段 un ...

  6. sql存储过程通过ID删除两表中的数据。

    CREATE OR REPLACE PROCEDURE del_p --建立名为del_p 的过程 IS CURSOR get_abid --简历名为get_abid的cursor 用来存放a表的id ...

  7. DRDS SQL 审计与分析——全面洞察 SQL 之利器

    背景 数据库存储着系统的核心数据,其安全方面的问题在传统环境中已经成为泄漏和被篡改的重要根源.而在云端,数据库所面临的威胁被进一步的放大.因此,对云数据库的操作行为尤其是全量 SQL 执行记录的审计日 ...

  8. 【数据库-Azure SQL Database】如何创建事务复制将本地数据同步到 SQL Azure

    Azure SQL DB 可以被配置成为 SQL Server 事务复制的一个订阅者( subscriber ). 主要应用场景有两种: 将您的数据迁移到 Azure SQL DB, 并且没有宕机时间 ...

  9. 利用反射自动生成SQL语句(仿Linq)

    转:http://www.cnblogs.com/the7stroke/archive/2012/04/22/2465597.html using System; using System.Colle ...

随机推荐

  1. 动态ViewPager导航页面

    今天新学知识总计,个人信息,仅供参考: item设置: viewpager页面设置: <?xml version="1.0" encoding="utf-8&quo ...

  2. 初步认识JUnit

    初步认识JUnit 目前大多数的基于Java的企业应用软件,肯定少不了单元测试,程序员通过编写单元测试来验证自己程序的有效性:管理者通过持续自动的执行单元测试和分析单元测试覆盖率来确保软件本身的质量. ...

  3. POSIX信号处理

    信号用于通知进程发生了某件事情,可以由一个进程发给另一个进程,也可以由内核发个一个进程. 每个信号都有一个关联的行为,可以通过sigaction函数来设置一个信号的行为,有3种选择: 1.定义一个信号 ...

  4. JavaScript密码复杂度

    <!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8" ...

  5. linux 系统下开机自动启动oracle 监听和实例 (亲测有效)

    [oracle@oracle11g ~]$ dbstartORACLE_HOME_LISTNER is not SET, unable to auto-start Oracle Net Listene ...

  6. Spring学习笔记 Part.01

    Hibernate的作用: 1.可以解决以面向对象的方式操作数据库的问题 请求参数(String)→Strut2→Action(对象)→业务逻辑组件(面向对象) 2.所以任何一个ORM框架都可以代替H ...

  7. 访问https链接方法

    <a id='___szfw_logo___' href='https://credit.szfw.org/CX20160808028375110138.html' target='_blank ...

  8. EasyUI 添加tab页(iframe方式)(转)

    function addTab(title, href,icon){ var tt = $('#tabs'); if (tt.tabs('exists', title)){//如果tab已经存在,则选 ...

  9. select练习1

    1. 查询Student表中的所有记录的Sname.Ssex和Class列. select t.sname ,t.ssex , t.sclass from student t 2. 查询教师所有的单位 ...

  10. 找出单链表的倒数第K个(从1开始计数)结点的值

    typedef struct Link { int data; struct Link* next; }NODE,*pNODE; NODE *searchK(NODE *phead, int k) { ...