EntityFramework优化:查询性能
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(); }