博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Linq to sql 简单性能差异指引 (转)

Posted on 2008-06-07 13:28  linFen  阅读(330)  评论(0编辑  收藏  举报
下面是一些讨论LINQ to SQL性能一些文章。


http://blogs.msdn.com/ricom/archive/2007/06/22/dlinq-linq-to-sql-performance-part-1.aspx
http://www.jdconley.com/blog/archive/2007/11/28/linq-to-sql-surprise-performance-hit.aspx

"LINQ Changes How You Will Program" by Bill McCarthy.

"Layer Data Access with LINQ to SQL" by Roger Jennings.




1:关闭OjectTrackingEnabled属性.
如果你只是找回数据,并且不修改任何东西,你不需要objectTracking,所有就像下面一样:

using (NorthwindDataContext context = new NorthwindDataContext())
{
  context.ObjectTrackingEnabled 
= false;
}





2:不要将你的所有DB Object都放入单个DataContext中.

DataContext表示一个单独的工作单元,而不是你数据库全部.如若你有多个没有连接的数据库对象,或他们没有被使用.这些对象就不需要添加进内存中的标识管理中消耗的内存空间和在DataContext的CUD engine跟踪对象的花费.

我建议你将你的工作数据区域分割几个DataContexts,每一个区域表示一个单独的使用数据的工作单元.你还是能使用同一个连接.




3:不要使用 CompiledQuery

当你创建和执行你的查询,从expression到相应的SQL,有几个步骤:

   1.

      Create expression tree(创建表达树)
   2.

      Convert it to SQL(转换SQL)
   3.

      Run the query(运行查询)
   4.

      Retrieve the data(找回数据)
   5.

      Convert it to the objects(转换为对象)

你注意:当你使用一次一次使用同一个查询时,第一和第二步是最耗费时间.
这里要少使用用来CompiledQuery的在System.Data.Linq 命名空间中的类,使用CompiledQuery,你一次编译你的查询并且存储它在之后使用的地方.这可以使用static 的CompiledQuery.Compile方法.

如使用下面代码:


Func
<NorthwindDataContext, IEnumerable<Category>> func =
   CompiledQuery.Compile
<NorthwindDataContext, IEnumerable<Category>>
   ((NorthwindDataContext context) 
=> context.Categories.
      Where
<Category>(cat => cat.Products.Count > 5));

并且现在,"func"是被编译的查询.我们能存储它在static utility class中,它将在它第一次运行时被编译:

/// <summary>
/// Utility class to store compiled queries
/// </summary>
public static class QueriesUtility
{
  
/// <summary>
  
/// Gets the query that returns categories with more than five products.
  
/// </summary>
  
/// <value>The query containing categories with more than five products.</value>
  public static Func<NorthwindDataContext, IEnumerable<Category>>
    GetCategoriesWithMoreThanFiveProducts
    {
      
get
      {
        Func
<NorthwindDataContext, IEnumerable<Category>> func =
          CompiledQuery.Compile
<NorthwindDataContext, IEnumerable<Category>>
          ((NorthwindDataContext context) 
=> context.Categories.
            Where
<Category>(cat => cat.Products.Count > 5));
        
return func;
      }
    }
}


接着我们使用这个被编译的查询就非常容易拉(现在它是什么都没有,面对我们的只是一个强类型的函数)


using (NorthwindDataContext context = new NorthwindDataContext())
{
  QueriesUtility.GetCategoriesWithMoreThanFiveProducts(context);
}

按这样的方法存储和使用它,减少多次调用这个方法访问集合的开销----实践就缩减为只调用一次.如果你没有调用这样的查询,就不用考虑编译问题,因为它只有在执行时,才被编译.




4:过滤数据你需要使用DataLoadOption.AssociateWith

当我们找回数据时使用Load或LoadWith来确保我们能找回结合数据主键的数据.但在大部分case中我们需要另外过滤数据.这里使用DataLoadOption.AssociateWith泛型方法能给我们带来很多便利.这个方法能将一个查询做为标准加载的数据参数应用于这些数据.


using (NorthwindDataContext context = new NorthwindDataContext())
{
  DataLoadOptions options 
= new DataLoadOptions();
  options.AssociateWith
<Category>(cat=> cat.Products.Where<Product>(prod => !prod.Discontinued));
  context.LoadOptions 
= options;
}





5:除非你需要它否则都要关闭Optimistic Concurrency.

LINQ to SQL 带来Optimistic Concurrency的选项支持使用SQL timestamp columns影射为Binary类型.你能打开这个特性和通过关闭影射文件和attributes的属性来关系这个特性.如果你的应用程序能提供运行"last update wins",那使用update check检测是浪费时间.

UpdateCheck.Never就是Optimistic Concurrency不被使用.

[Column(Storage=“_Description”, DbType=“NText”,
            UpdateCheck
=UpdateCheck.Never)]
public string Description
{
  
get
  {
    
return this._Description;
  }
  
set
  {
    
if ((this._Description != value))
    {
      
this.OnDescriptionChanging(value);
      
this.SendPropertyChanging();
      
this._Description = value;
      
this.SendPropertyChanged(“Description”);
      
this.OnDescriptionChanged();
    }
  }
}




6:经常检测DataContext的查询产生和分析找回数据.

如果你的查询产生很快,有可恩你不会知道还有数据在你执行后才被找回.使用DataContext的Loq属性能看到SQL.

using (NorthwindDataContext context = new NorthwindDataContext())
{
  context.Log 
= Console.Out;
}

using (NorthwindDataContext context = new NorthwindDataContext())
{
#if DEBUG
context.Log 
= Console.Out;
#endif
}




7:避免多余的表放入Context中.

Objcet Tracking是很好的机制,当你追加一个对象进入您的context时,你意味着你现在要返回它时这个对象已是一个非连接对象.DataContext将会对待像一个潜在更改的对象样对它标记.如果当你的目的就是这样那就是正确的拉.

但有可能一些情况下就不绝对正确拉,你追加一个潜在不会更改的对象,如这个的情况下那你为集合追加完成后,这个对象被更改还或不会更改变都不会被检测.但最好的性能是你应该只对在集合中正在追加的对象时被更改的对象进行检测.




8 注意Entity标识管理

用一个没只读内容运行期间,这个对象仍然被跟踪-所以考虑下面的code:

using (NorthwindDataContext context = new NorthwindDataContext())
{
  var a 
= from c in context.Categories
  select c;
}


非常有计划,看不出有什么问题,再看下面:

using (NorthwindDataContext context = new NorthwindDataContext())
{
  var a 
= from c in context.Categories
  select 
new Category
  {
    CategoryID 
= c.CategoryID,
    CategoryName 
= c.CategoryName,
    Description 
= c.Description
  };
}


直觉上是第二个工作比第一个慢,但错,实际是第二个比第一个快.

因为在第一个查询中,每一行对象都需要存储,在这个期间你仍然能会更改他们,但在第二个,你放弃原来的对象,创建一个新的对象来存储.这样更有效.



9:你只需要找记录数.

当你绑定一个datagrid,做分页时--可以考虑使用最容易的方法是LINQ to SQL provides.这些主要都是Take和Skip方法.下面是对ListView分页的代码片段.

/// <summary>
/// Gets the products page by page.
/// </summary>
/// <param name=”startingPageIndex”>Index of the starting page.</param>
/// <param name=”pageSize”>Size of the page.</param>
/// <returns>The list of products in the specified page</returns>
private IList<Product> GetProducts(int startingPageIndex, int pageSize)
{
  
using (NorthwindDataContext context = new NorthwindDataContext())
  {
    
return context.Products
           .Take
<Product>(pageSize)
           .Skip
<Product>(startingPageIndex * pageSize)
           .ToList
<Product>();
   }
}


10:--不要滥用CompiledQuery