Entity Framework Core 2.X Crash Tutorial (5)


这一小节的内容主要是EFCore对关联数据的查询和修改。

EFCore 保存关联数据

首先看下保存关联数据,以Province和City举例,这是一个一对多的关系。
实例一:

 public IActionResult Index()
 {
   var province=new Province()
   {
     Name = "辽宁",
     Population = 4000000,
     Cities = new List
     {
       new City(){AreaCode = "024",Name = "沈阳"},
       new City(){AreaCode = "0411",Name = "大连"}
     }
   };
   context.Provinces.Add(province);
   context.SaveChanges();
   return View();
 }

RelatedSaveExample1
实例二:
此时没有明确指定Cities的外键,DbContext自己会搞明白。

 public IActionResult Index()
 {
   var province = context.Provinces.Single(x => x.Name == "辽宁");
   province.Cities.Add(new City
   {
     AreaCode="0413",
     Name = "鞍山"
   });
   context.SaveChanges();
   return View();
 }

RelatedSaveExample1
但是在离线情况下,需要使用外键。下面这个例子中,City没有被追踪,所以需要指定外键。

 public IActionResult Index()
 {
   var city = new City
   {
     ProvinceId = 11,
     AreaCode = "0421",
     Name = "鞍山"
   };
   context.Cities.Add(city);
   context.SaveChanges();
   return View();
 }

查询关联数据

  • Eager Loading 预加载 一次查询的时候都查询出来。
  • Query Projections 查询映射 我定义出来我们想要的结果是什么样的 ,然后我们再进行查询。
  • Explicit Loading 显示加载 内存里已经存在一些数据了,然后再想从内存里加载一些它的关联数据。
  • Lazy loading 懒加载

    预加载

    public IActionResult Index()
    {
     var province =
     context.Provinces
       .Include(x => x.Cities)
       .ToList();
     return View();
    }
    在entity framework core中,如果两个实体涉及到外键连接,查询的时候默认是只查自身而不会去查询外键表的。如果想要让查询结果包含外键实体,则需要使用include方法来让查询结果包含外键实体。
    注意下Include只能跟在Province(DbSet)后面,后面还可以继续跟着过滤where等。
    EagerLoading
    EagerLoadingLog
    EFCore还支持继续向下钻取,比如City下一级是CityCompany,CityCompany的下一级是Company。可以这样写:
    public IActionResult Index()
    {
     var province =
     context.Provinces
       .Include(x => x.Cities)
       .ThenInclude(x=>x.CityCompanies)
       .ThenInclude(x=>x.Company)
       .ToList();
     return View();
    }
    EFCore还支持一同加载多个关联属性,例如Cities拥有三个关联属性,可以这么写:
    public IActionResult Index()
    {
     var cities=
     context.Cities
       .Include(x => x.Province)
       .Include(x=>x.CityCompanies)
       .Include(x=>x.Mayor)
       .ToList();
     return View();
    }

查询出来的关联数据都是基于DBSet集合的,换句话说每个CityEntity具有三个集合。Inculde会获取所有关联数据,并不会过滤查询。

Query Projections 查询映射

查询映射相当于自定义查询结果,方法里我们使用一个匿名类型,匿名类定义我们需要的属性。

 public IActionResult Index()
 {
   var provincesInfo = context.Provinces
     .Select(x => new
     {
       x.Name,
       x.Id
     })
     .ToList();
   return View();
 }

QueryProjections
provincesInfo 是匿名类型,这个集合只能在这个方法里面用。如果想在外面方法使用的话,需要使用dynamic类型。

 private List Query()
 {
   var provincesInfo = context.Provinces
     .Select(x => new
     {
       x.Name,
       x.Id
     })
     .ToList();
   return provincesInfo;
 }

 public IActionResult Index()
 {
   var provincesInfo = Query();
   foreach (var p in provincesInfo)
   {
     Console.WriteLine(p.Name);
   }
   return View();
 }

dymanicprovincesInfo
EFCore同样可以增加过滤条件进行关联查询,像下面这么写:

 public IActionResult Index()
 {
   var provincesInfo = context.Provinces
     .Select(x => new
     {
       x.Name,
       x.Id,
       x.Cities.Count,
       Cities=x.Cities.Where(y=>y.Name=="沈阳").ToList()
     })
     .ToList();
   return View();
 }

QueryProjectionswithCondition
EFCore进行过滤查询时,还可以根据字表City去查询Province,查询出来的是Province。

 public IActionResult Index()
 {
   var provincesInfo = context.Provinces
     .Where(x => x.Cities.Any(y=>y.Name=="沈阳"))
     .ToList();
   return View();
 }

EFCore修改关联数据

在线修改关联数据

EFCore修改关联数据时,可以像下面这么写:

 public IActionResult Index()
 {
   var provincesInfo = context.Provinces
     .Include(x=>x.Cities)
     .First(x=>x.Cities.Any());
   var city = provincesInfo.Cities[0];
   city.Name += " Updated";
   context.SaveChanges();
  //context.Cities.Remove(city);
   return View();
 }

这个查询是关联查询出province的city属性,然后这些province必须带有city,最后再取第一个province.
修改这个province的第一个city的名字。还可以进行删除,删除第一个city。

离线修改关联数据

离线状态下修改关联数据有两种方法,下面进行一下比较。

 public IActionResult Index()
 {
   var provincesInfo = context.Provinces
     .Include(x=>x.Cities)
     .First(x=>x.Cities.Any());
   var city = provincesInfo.Cities[0];
   city.Name += " Updated";
   //Method 1
   context2.Cities.Update(city);

   //Method 2
   context2.Entry(city).State = EntityState.Modified;
   context2.SaveChanges();
   return View();
 }

第一种方法的执行过程中,会有五次update的SQL语句,因为此时更新的是这个city关联的所有数据,即它关联的province和province下面的四个city(包括了它自己)。
OffLineUpdate1
通过观察更新前后的DbContext下的状态,可以看到有五个状态的变化。
OffLineUpdate1Contextbeforechange
OffLineUpdate1ContextAfterchange
第二种方法是利用Entry方法,这个时候修改city,它会忽略cities所有的关联属性,只更新这个city对象本身。
OffLineUpdate2
这个时候只执行一次update操作,对应context对象中的changecount也是1.
OffLineUpdate2Context


文章作者: Chaoqiang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Chaoqiang !
评论
 上一篇
Entity Framework Core 2.X  Crash Tutorial (6) Entity Framework Core 2.X Crash Tutorial (6)
这一小节的内容主要是关于EFCore的种子数据。 EFCore中添加种子数据在DbContext中添加如下测试代码: protected override void OnModelCreating(ModelBuilder modelBu
下一篇 
Entity Framework Core 2.X  Crash Tutorial (4) Entity Framework Core 2.X Crash Tutorial (4)
这一小节的主要的内容是继续了解EFCore的执行过程,包括查询过滤和删除等操作。 EFCore查询过滤一般地,查询过滤我们会这么写: var provinces = context.Provinces .Where(x=>x.Name
  目录