zoukankan      html  css  js  c++  java
  • Code First Entity Framework 6化被动为主动之explicit loading模式实战分析( 附源码)

    在使用Entity Framework加载关联实体时,可以有三种方式:

    1.懒加载(lazy Loading);

     2.贪婪加载(eager loading);  

    3.显示加载(explicit loading)。

    EF默认使用的是懒加载(lazy Loading)。一切由EF自动处理。

    这种方式会导致应用程序多次连接数据库,这种情况推荐在数据量较大的情况下使用。当我们需要加载数据较少时,一次性全部加载数据会相对更高效。

    我们来看看EF的显示加载(explicit loading)如何让我们完全掌控数据的加载(非自动化)。

    这里我们使用Code First模式。

    数据表:商品表,商品分类表,品牌表

    Step1:新建控制台应用程序,添加EF6引用(可以使用NuGet获取)

    Step2:创建三个实体对象:Product、Category 、Brand

    Product实体类:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 
     6 namespace EFExplicitLoading.Models {
     7   public  class Product {
     8       public int ProductId { get; set; }
     9       public String Title { get; set; }
    10       public int CategoryId { get; set; }
    11 
    12       public virtual Category Category { get; set; }
    13 
    14       public virtual Brand Brand { get; set; }
    15 
    16   }
    17 }

    Category实体类:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 
     6 namespace EFExplicitLoading.Models {
     7   public  class Category {
     8 
     9       public Category() {
    10           Products = new HashSet<Product>();
    11       }
    12 
    13       public int CategoryId { get; set; }
    14       public String Name { get; set; }
    15 
    16       public virtual ICollection<Product> Products { get; set; } 
    17   }
    18 }

    Brand实体类:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 
     6 namespace EFExplicitLoading.Models {
     7   public  class Brand {
     8 
     9       public Brand() {
    10           Products = new HashSet<Product>();
    11       }
    12 
    13       public int BrandId { get; set; }
    14 
    15       public String Name { get; set; }
    16 
    17       public virtual ICollection<Product> Products { get; set; } 
    18   }
    19 }

    Step3:创建派生自DbContext的类(ExplicitContext)

     1 using System.Data.Entity;
     2 using EFExplicitLoading.Models;
     3 
     4 namespace EFExplicitLoading {
     5 
     6     public class ExplicitContext : DbContext {
     7         public ExplicitContext()
     8             : base("ExplicitConnectionString") {
     9         }
    10 
    11         public DbSet<Product> Products { get; set; }
    12 
    13         public DbSet<Category> Categories { get; set; }
    14 
    15         public DbSet<Brand> Brands { get; set; }
    16 
    17         protected override void OnModelCreating(DbModelBuilder modelBuilder) {
    18             modelBuilder.Entity<Product>().ToTable("Product");
    19             modelBuilder.Entity<Brand>().ToTable("Brand");
    20             modelBuilder.Entity<Category>().ToTable("Category");
    21         }
    22     }
    23 
    24 }

    Step4:在App.config中添加connectionStrings节点

    1   <connectionStrings>
    2     <add name="ExplicitConnectionString" connectionString="Data Source=.;Initial Catalog=EFExplicit;Integrated Security=True;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
    3   </connectionStrings>

    准备工作已就绪,当我们使用ExplicitContext类时,EF会根据实体自动创建数据库和表

    下面回到控制台入口函数处:

    Step5:添加测试数据

     1 
     2             using (var context = new ExplicitContext()) {
     3                 //先清空数据
     4                 context.Database.ExecuteSqlCommand("delete from product");
     5                 context.Database.ExecuteSqlCommand("delete from brand");
     6                 context.Database.ExecuteSqlCommand("delete from category");
     7 
     8                 var category1 = new Category {Name = "服装"};
     9                 var category2 = new Category {Name = "家电"};
    10                 var brand1 = new Brand {Name = "品牌1"};
    11                 var brand2 = new Brand {Name = "品牌2"};
    12 
    13                 context.Products.Add(new Product {Brand = brand1, Category = category1, Title = "产品1"});
    14                 context.Products.Add(new Product {Brand = brand1, Category = category2, Title = "产品2"});
    15                 context.Products.Add(new Product {Brand = brand1, Category = category2, Title = "产品3"});
    16                 context.Products.Add(new Product {Brand = brand2, Category = category2, Title = "产品4"});
    17                 context.Products.Add(new Product {Brand = brand2, Category = category2, Title = "产品5"});
    18                 context.Products.Add(new Product {Brand = brand2, Category = category2, Title = "产品6"});
    19                 context.SaveChanges();
    20             }

    Step6:开始测试explicit loading

     1 using (var context = new ExplicitContext()) {
     2                 //禁用懒加载
     3                 context.Configuration.LazyLoadingEnabled = false;
     4 
     5                 var category = context.Categories.First(c => c.Name == "家电");
     6 
     7                 //判断分类关联的产品是否已经被加载
     8                 if (!context.Entry(category).Collection(x => x.Products).IsLoaded) {
     9                     context.Entry(category).Collection(x => x.Products).Load();
    10                     Console.WriteLine("分类{0}的产品被显示加载...", category.Name);
    11                 }
    12 
    13                 Console.Write("分类{0}下的共有{1}件产品", category.Name, category.Products.Count());
    14 
    15                 foreach (var product in context.Products) {
    16                     if (!context.Entry(product).Reference(x => x.Category).IsLoaded) {
    17                         context.Entry(product).Reference(x => x.Category).Load();
    18                         Console.WriteLine("分类{0}被显示加载.", product.Category.Name);
    19                     }
    20                     else {
    21                         Console.WriteLine("分类{0}已经加载过了.", product.Category.Name);
    22                     }
    23                 }
    24 
    25                 //重新使用category计算 产品数量
    26                 Console.WriteLine("产品加载完后,分类{0}下有{1}件产品", category.Name, category.Products.Count());
    27 
    28                 category.Products.Clear();
    29                 Console.WriteLine("产品集合被清空了.");
    30                 Console.WriteLine("此时分类{0}下有{1}件产品", category.Name, category.Products.Count());
    31 
    32 
    33                 context.Entry(category).Collection(x => x.Products).Load();
    34                 Console.WriteLine("重新显示加载产品");
    35                 Console.WriteLine("此时分类{0}下有{1}件产品", category.Name, category.Products.Count());
    36 
    37                 //使用ObjectContext刷新实体
    38                 var objectContext = ((IObjectContextAdapter) context).ObjectContext;
    39                 var objectSet = objectContext.CreateObjectSet<Product>();
    40                 objectSet.MergeOption = MergeOption.OverwriteChanges;
    41                 objectSet.Load();
    42                 //MergeOption.AppendOnly
    43                 //MergeOption.OverwriteChanges
    44                 //MergeOption.NoTracking
    45                 //MergeOption.PreserveChanges
    46                 
    47 
    48                 Console.WriteLine("产品集合以MergeOption.OverwriteChanges的方式重新加载");
    49                 Console.WriteLine("此时分类{0}下有{1}件产品", category.Name, category.Products.Count());
    50             }
    51 
    52 
    53             Console.WriteLine("测试部分加载...");
    54             //测试部分加载,然后加载所有
    55             using (var context = new ExplicitContext()) {
    56                 //禁用懒加载
    57                 context.Configuration.LazyLoadingEnabled = false;
    58                 var category = context.Categories.First(c => c.Name == "家电");
    59                 //先加载1条数据
    60                 Console.WriteLine("先加载1条数据");
    61                 context.Entry(category).Collection(x => x.Products).Query().Take(1).Load();
    62                 Console.WriteLine("分类{0}下共有{1}件产品", category.Name, category.Products.Count());
    63 
    64                 //加载所有
    65                 context.Entry(category).Collection(x => x.Products).Load();
    66                 Console.WriteLine("加载所有数据");
    67 
    68                 Console.WriteLine("此时分类{0}下有{1}件产品", category.Name, category.Products.Count());
    69             }
    70 
    71             Console.ReadKey();

    执行结果:

      禁用懒加载(lazy loading)            

    要想测试我们的explict loading,必须先禁用懒加载,有2种方式可以禁用懒加载:

    1.设置Context.Configuration.LazyLoadingEnabled的值为false   或

    2.移除每一个实体类中的关联实体属性(导航属性)的virtual访问修饰符,这种方法可以更精确控制懒加载,即只禁用移除virtual修饰符实体的懒加载

      IsLoaded属性和Load方法            

    接下来,我们利用EF获取一条Name为家电的分类实体数据,由于Category与Product是一对多的关系,此时我们可以使用IsLoaded属性来测试此分类下的Products集合是否被加载

    context.Entry(category).Collection(x => x.Products).IsLoaded

    若没有加载,我们使用Load方法来加载关联的Products,在接下来的foreach遍历中,我们可以看到,Category关联的Product已经全部被加载完毕

    PS:当使用Take(n)方法Load数据时,IsLoaded属性依然为False,因为只有当关联的所有数据加载完,IsLoaded才为true

      关系修复(relationship fixup)      

    这里Entity Framework使用称为"关系修复"的技术(relationship fixup),即当我们单独加载关联实体的时候(Product),这些数据会自动挂载到已经载入的实体(Category),但这种关系修复并不是总是会生效,比如在多对多的关系中就不会这样处理。

      关于Clear()                                 

    然后我们打印出来Category中有多少Product,之后使用Clear()方法清空category对象的关联集合Products。

    Clear方法会移除Category和Product的关联关系,但Product集合数据并没有删除,依然存在上下文的内存中,只是它不再与Category关联。

    后面我们又重新使用Load加载Product但category.Products.Count()的值依然为0。这正好说明Clear方法的本质。

    这是因为Load默认使用MergeOption.AppendOnly的方式加载数据,找不到Product实例了(被Clear了)。然后我们使用MergeOption.OverwriteChanges的方式才将数据重新关联

      关于MergeOption(实体合并选项) 

    MergeOption枚举共有4个选项

    1.MergeOption.AppendOnly

      在现有的关系基础上合并
    2.MergeOption.OverwriteChanges

      OverwriteChanges模式,会从数据库中更新当前上下文实例的值(使用同一个实体对象的实例,如category)。

      当你想要恢复实体在上下文的改变,从数据库刷新实体时,使用这个模式就非常有用。

    3.MergeOption.NoTracking

      无追踪模式不会跟踪对象的变化,也不会意识到对象已经被加载到当前上下文

      NoTracking可以应用到一个实体的导航属性(关联实体属性),但这个实体也必须使用NoTracking

      反过来,NoTracking应用到某个实体时,这个实体的导航属性会忽略默认的AppendOnly模式而使用NoTracking模式
    4.MergeOption.PreserveChanges

      PreserveChanges选项本质上与OverwriteChanges选项相反,

      PreserveChanges模式会更新查询结果集(若数据库中有变化),但不会更新在当前上下文内存中的值

    PS: 也许你已经注意到,这里我们使用了ObjectContext对象,而不是context对象,这是因为DbContext不能直接使用MergeOption类型,所以必须使用ObjectContext对象

      关于性能提升                                

    在任何时候,关联实体集合的数量被限制时,使用Load方法,会有助于我们提升程序的性能,比如加载一个Category中的5条Product。

    在少量场景中,我们需要整个关联集合时,也可以使用Load

    PS:当一个实体在Added(添加)、Deleted(删除)、Detected(分离)状态时,Load方法无法使用。

    关于Entity Framework性能提升的方法还有很多,当然,我们必须根据自己的项目实际情况来做优化。不同的应用场景,选择不同的优化方案

    示例代码:点此下载



    原文链接:http://www.cnblogs.com/jayshsoft/p/entity-framework-explicit-loading.html

  • 相关阅读:
    2016年 CodePen 最热门的前端代码 Top 100
    HttpClientHelper
    .net core mvc 简易登录
    senparc 第三方授权回调,保存授权信息到文件
    盛派微信Sdk .Net Core 初始化注册服务信息
    分享一个gif截图工具
    C# 单例模式
    获取微信AccessToken,保存在内存中,过期后重新获取
    .NET Core WebAPI Swagger使用
    .net Core 读取配置文件参数
  • 原文地址:https://www.cnblogs.com/Percy_Lee/p/5200071.html
Copyright © 2011-2022 走看看