EntityFramework優化:查詢效能

libingql發表於2018-05-19

1. 禁用延遲載入

  延遲載入是常見的方式,Entity Framework在需要時可以自動為一個實體的例項獲取關聯的資料。

  Entity Framework自動延遲載入需要同時滿足以下3個條件:

  (1)DbContext.Configuration.LazyLoadingEnabled = true (預設為true)

  (2)DbContext.Configuration.ProxyCreationEnabled = true (預設為true)

  (3)POCO類必須是public而非sealed,且集合屬性使用Virtual修飾,這樣Entity Framework才能Override以包含延遲載入。

  延遲載入示例:

using (var ctx = new LibingContext())
{
    var category = ctx.Categories.Find(1);
    foreach (var product in category.Products)
    {
        Console.WriteLine(product.ProductName);
    }
}

  EntityFramework禁用延遲載入:

public class LibingContext : DbContext
{
    public LibingContext()
        : base("Name=LibingContext")
    {
        // 禁用延遲載入
        this.Configuration.LazyLoadingEnabled = false;
        this.Configuration.ProxyCreationEnabled = false;
    }
}

2. AsNoTracking()與Attach()

  對於只讀操作,強烈建議使用AsNoTracking進行資料獲取,降低資料獲取所需的時間。

  由於沒有受到DbContext的跟蹤,利於對資料及時性要求高的資料查詢。

  使用AsNoTracking查詢出來的資料,要進行刪除時,需使用Attach()。

using (var ctx = new LibingContext())
{
    var products = ctx.Products
        .AsNoTracking()
        .ToList();

    var product = products.Where(t => t.ProductID == 1).FirstOrDefault();

    //// 修改不用Attach
    //product.ProductName = "新名稱";
    //ctx.Entry(product).State = EntityState.Modified;

    ctx.Set<Product>().Attach(product);
    ctx.Entry(product).State = EntityState.Deleted;

    ctx.SaveChanges();
}

  注:查詢過程Select對映不需要加AsNoTracking()

var products = ctx.Products
    .Select(t => new { t.ProductID, t.ProductName })
    .ToList();

3. AsNonUnicode

using (var ctx = new LibingContext())
{
    var products = ctx.Products
        .Where(t => t.ProductName == "商品")
        .ToList();
}
SELECT 
    [Extent1].[ProductID] AS [ProductID], 
    [Extent1].[CategoryID] AS [CategoryID], 
    [Extent1].[ProductName] AS [ProductName], 
    [Extent1].[UnitPrice] AS [UnitPrice], 
    [Extent1].[UnitsInStock] AS [UnitsInStock]
    FROM [dbo].[Product] AS [Extent1]
    WHERE N'商品' = [Extent1].[ProductName]
using System.Data.Entity;
using (var ctx = new LibingContext())
{
    var products = ctx.Products
        .Where(t => t.ProductName == DbFunctions.AsNonUnicode("商品"))
        .ToList();
}
SELECT 
    [Extent1].[ProductID] AS [ProductID], 
    [Extent1].[CategoryID] AS [CategoryID], 
    [Extent1].[ProductName] AS [ProductName], 
    [Extent1].[UnitPrice] AS [UnitPrice], 
    [Extent1].[UnitsInStock] AS [UnitsInStock]
    FROM [dbo].[Product] AS [Extent1]
    WHERE '商品' = [Extent1].[ProductName]

  EF正常情況生成的SQL會在前面帶上“N”,加上DbFunctions.AsNonUnicode生成的SQL沒有“N”。“N”是將字串作為Unicode格式進行儲存。

  .Net字串是Unicode格式,SQL中帶“N”會進行資料轉換,無法使用索引,只能全表掃描。

  DbFunctions.AsNonUnicode()讓.Net將其作為一個非Unicode來處理。

  在進行字串查詢或者比較時建議用AsNonUnicode()方法來提高查詢效能。

4. 使用AutoMapper查詢DTO

  查詢需要的欄位:DTO

using AutoMapper;
using AutoMapper.QueryableExtensions;
using (var ctx = new LibingContext())
{
    Mapper.Initialize(cfg => cfg.CreateMap<Product, ProductDTO>());

    var products = ctx.Products
        .ProjectTo<ProductDTO>()
        .ToList();
}

相關文章