好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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(Expression bool >>  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架构的核心代码,主要体现在生成数据上下文对象上,及如何去避免并发冲突的产生,而对于并发冲突我们会在另一篇文章中做详细的说明。敬请期待!

回到目录

查看更多关于LINQ-to-SQL那点事~LINQ-to-SQL中的数据缓存与应对的详细内容...

  阅读:33次