(EF Core使用Include和join)|(Include和ThenInclude区别)

本文探讨了EF Core中Join和Include方法在表连接上的不同,包括Join的灵活性和结果字段指定,以及Include针对外键关联的便捷性和即时查询。通过实际案例解析了如何在唱片表和流派表的连接中使用这两种方法,并强调了Include在效率上的局限性及其替代方案ThenInclude。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

EF Core使用Includejoin

在EF中表连接常用的有Join()Include(),两者都可以实现两张表的连接,但又有所不同。

例如有个唱片表Album(AlbumId,Name,CreateDate,GenreId),表中含外键GenreId连接流派表Genre(GenreId,Name)。每个唱片归属唯一一个流派,一个流派可以对应多个唱片。

1.Join()

两表不必含有外键关系,需要代码手动指定连接外键相等(具有可拓展性,除了值相等,还能指定是>,<以及其他对两表的相应键的关系),以及结果字段。

那么可以这么写两个表的连接:

var wholeRecord = dc.Album.Join(dc.Genre, a => a.GenreId, g => g.GenreId, (a, g) => new { a.AlbumId,a.Name,g.GenreId,g.Name;

这样就选取除了两表的AlbumId,Name,GenreId,Name

2.Include()

两表必须含有外键关系,只需要指定键名对应的类属性名即可,不需指定结果字段(即全部映射)。默认搜索某表时,不会顺带查询外键表,直到真正使用时才会再读取数据库查询;若是使用 Include(),则会在读取本表时把指定的外键表信息也读出来。

那么可以这么写两个表的连接:

//EF已经生成了Album和Genre的数据库映射模型类以及导航属性
var wholeRecord=dc.Album.Include("Genre");
//或者
//var wholeRecord=dc.Album.Include(a=>Genre);

这样数据库就执行了一个左连接,把AlbumGenre的所有字段全部连起来了,并且Include()是立即查询的,像ToList()一样,不会稍后延迟优化后再加载。

这样其实效率很低,因为如果两张表记录很大,那么连接是个费时费资源的事情,建议少用,或者先筛选出需要的结果集再连接。

IncludeThenInclude区别

Include”在我们不需要多级数据的对象上运行良好,但如果需要获得多级数据,那么“ThenInclude”是最合适的。让我用一个例子解释一下。假设我们有3个实体,公司,客户经理和顾客:

public class Company
{
    public string Name { get; set; }

    public class Manager{ get; set; }

}

public class Manager
{
	public string Name { get; set; }
	
	public class Client { get; set; }
}

 public class Client
 {
    public string Name { get; set; }

    public string ClientMessage { get; set; }
 }

现在,如果你想公司和公司下的客户经理你可以像下面那样使用“Include”,这样你拿到的是company的name和Manager的name

using (var context = new YourContext())
{
  var customers = context.Company
    .Include(c => c.Clients)
    .ToList();
}

但是如果您想要公司和客户经理以及顾客,因为顾客没有直接和公司关联,所以不能使用include直接关联到,这时候您可以使用“ThenInclude”,这样你拿到的就是company的name和Manager的name还有ClientClientMessage

using (var context = new MyContext())
{
   var customers = context.Company
    .Include(i => i.Manager )
      .ThenInclude(a => a.ClientMessage )
    .ToList();
}

这相当于用关联出来的manager去关联client


EF的表左连接方法IncludeJoin

在EF中表连接常用的有Join()Include(),两者都可以实现两张表的连接,但又有所不同。

例如有个唱片表Album(AlbumId,Name,CreateDate,GenreId),表中含外键GenreId连接流派表Genre(GenreId,Name)。每个唱片归属唯一一个流派,一个流派可以对应多个唱片。

1.Join(),两表不必含有外键关系,需要代码手动指定连接外键相等(具有可拓展性,除了值相等,还能指定是>,<以及其他对两表的相应键的关系),以及结果字段。

重载方式(是扩展方法,第一个参数带this,代表自身):

public static IQueryable<TResult> Join<TOuter, TInner, TKey, TResult>(this IQueryable<TOuter> outer, IEnumerable<TInner> inner, Expression<Func<TOuter, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<TOuter, TInner, TResult>> resultSelector);
public static IQueryable<TResult> Join<TOuter, TInner, TKey, TResult>(this IQueryable<TOuter> outer, IEnumerable<TInner> inner, Expression<Func<TOuter, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<TOuter, TInner, TResult>> resultSelector, IEqualityComparer<TKey> comparer);

那么可以这么写两个表的连接:

var wholeRecord = dc.Album.Join(dc.Genre, a => a.GenreId, g => g.GenreId, (a, g) => new { a.AlbumId,a.Name,g.GenreId,g.Name;

这样就选取除了两表的AlbumId,Name,GenreId,Name

2.Include(),两表必须含有外键关系,只需要指定键名对应的类属性名即可,不需指定结果字段(即全部映射)。默认搜索某表时,不会顺带查询外键表,直到真正使用时才会再读取数据库查询;若是使用 Include(),则会在读取本表时把指定的外键表信息也读出来。

重载方式:

//位于namespace System.Data.Entity.Infrastructure
public DbQuery<TResult> Include(string path);
//位于namespace System.Data.Entity,务必引入才能找到该方法。否则只看到上个方法
public static IQueryable<T> Include<T, TProperty>(this IQueryable<T> source, Expression<Func<T, TProperty>> path) where T : class;
public static IQueryable<T> Include<T>(this IQueryable<T> source, string path) where T : class;

可以这么写:

//EF已经生成了Album和Genre的数据库映射模型类以及导航属性
var wholeRecord=dc.Album.Include("Genre");
//或者
//var wholeRecord=dc.Album.Include(a=>Genre);

这样数据库就执行了一个左连接,把AlbumGenre的所有字段全部连起来了,并且Include()是立即查询的,像ToList()一样,不会稍后延迟优化后再加载。

这样其实效率很低,因为如果两张表记录很大,那么连接是个费时费资源的事情,建议少用,或者先筛选出需要的结果集再连接。


EF的Join()Include()差异

在EF中表连接常用的有Join()Include(),两者都可以实现两张表的连接,但又有所不同。

1.Join(),两表不必含有外键关系,需要代码手动指定连接外键相等(具有可拓展性,除了值相等,还能指定是>,<以及其他对两表的相应键的关系),以及结果字段。

2.Include(),两表必须含有外键关系,只需要指定键名对应的类属性名即可,不需指定结果字段(即全部映射)。默认搜索某表时,不会顺带查询外键表,直到真正使用时才会再读取数据库查询;若是使用 Include(),则会在读取本表时把指定的外键表信息也读出来。

Include

1、现在有三张表

Math_RoleInfo 角色表

Math_User_Role_Select 用户角色选择表

Math_UserInfo 用户表

如何通过单个角色,获取用户信息呢。通过EF。

C#代码如下

Guid id = Guid.Parse("815D30FB-1050-413D-9E19-D8CBDC434E7C");
MathRoleAuthorEntities context = new MathRoleAuthorEntities();
List<Math_RoleInfo> list = context.Math_RoleInfo.Include("Math_User_Role_Select").Include("Math_User_Role_Select.Math_UserInfo").Where(item => item.RoleId== id).ToList<Math_RoleInfo>();
Console.ReadKey();

第一次的include是单级的导航属性,

第二次include是多级的导航属性。中间用.进行级别的传递。

JOIN

在EF中,当在dbset使用join关联多表查询时,连接查询的表如果没有建立相应的外键关系时,EF生成的SQL语句是inner join(内联),对于inner join,有所了解的同学都知道,很多时候这并不是我们的本意,实例如下:

var list = from o in context.CTMS_OD_ORDERS
                       join d in context.CTMS_SUP_DOCTOR
                       on o.OWNERDOCID equals d.USERID
                       join e in context.CTMS_OD_ORDERSEVALUATION
                       on o.ORDERID equals e.ORDERID
                       select o;`

EF生成了内连接(inner join)查询,当两个表的任一表的数据不匹配时,查询结果就为空!实际上left join(左联接)才是我们想要的,那么怎么样才能生成left join查询呢?其实只要我们如下改造,EF就能为我们生成left join(左联接)查询!

data = from o in context.CTMS_OD_ORDERS
                       join d in context.CTMS_SUP_DOCTOR 
                       on o.OWNERDOCID equals d.USERID into dc
                       from dci in dc.DefaultIfEmpty()
                       join e in context.CTMS_OD_ORDERSEVALUATION
                       on o.ORDERID equals e.ORDERID into ec
                       from eci in ec.DefaultIfEmpty()
                       where o.USERID == userID && (string.IsNullOrEmpty(type) || o.PRODUCTNAME.Contains(type))
                       select new ODOrders
                       {
                           BalanceStatus = o.BALANCESTATUS,
                           ChannelOrderID = o.CHANNELORDERID,
                           ChannelType = o.CHANNELTYPE,
                           CreateDateTime = o.CREATEDATETIME,
                           CreateUserID = o.CREATEUSERID,
                           CreateUserName = o.CREATEUSERNAME,
                           DocName = dci.DOCNAME,
                           EvalutionStatus = string.IsNullOrEmpty(eci.ORDERID) ? "0" : "1",
                           PayTime = o.PAYTIME,
                           ProductCode = o.PRODUCTCODE,
                           ProductName = o.PRODUCTNAME,
                           ProductInstanceId = o.PRODUCTINSTANCEID,
                           ProductID = o.PRODUCTID,
                           OrderID = o.ORDERID,
                           OrderCode = o.ORDERCODE,
                           OrderStatus = o.ORDERSTATUS,
                           OrderType=o.ORDERTYPE,
                           TotalFee = o.TOTALFEE,
                           UserID=o.USERID,
                           UserName=o.USERNAME
                       };                  

对比上下两种写法,可以看到在on表的后面我们加上了into xx,还有不要忘记,还需加上from xxx in xx.DefaultIfEmpty(),重要的就是最后的xx.DefaultIfEmpty(),它的作用是当连接的表为空时也会有一条空的数据,达到了left join的效果。


EF之外键Include() left join

项目中用EF实现外键查询出的数据, 查询数量正确, 但实现返回数据集数量不对

//DbContext.cs
HasRequired(s => s.ClassRoom)
            .WithMany()
            .HasForeignKey(student => student.ClassRoomId);
//查询语句
dbRead.Set<Student>().Include(x=>x.ClassRoom);

查询 .Count().ToList()结果数量不一致

经调试后发现生成的Sql语句为 inner join

正确的结果应该是 left join

此时应该如下定义外键

HasOptional(s => s.ClassRoom)
             .WithMany()
             .HasForeignKey(student => student.ClassRoomId);

此时返回的结果就正确了!

Summary Entity Framework Core in Action teaches you how to access and update relational data from .NET applications. Following the crystal-clear explanations, real-world examples, and around 100 diagrams, you'll discover time-saving patterns and best practices for security, performance tuning, and unit testing. Purchase of the print book includes a free eBook in PDF, Kindle, and ePub formats from Manning Publications. About the Technology There's a mismatch in the way OO programs and relational databases represent data. Enti ty Framework is an object-relational mapper (ORM) that bridges this gap, making it radically easier to query and write to databases from a .NET application. EF creates a data model that matches the structure of your OO code so you can query and write to your database using standard LINQ commands. It will even automatically generate the model from your database schema. About the Book Using crystal-clear explanations, real-world examples, and around 100 diagrams, Entity Framework Core in Action teaches you how to access and update relational data from .NET applications. You'l start with a clear breakdown of Entity Framework, long with the mental model behind ORM. Then you'll discover time-saving patterns and best practices for security, performance tuning, and even unit testing. As you go, you'll address common data access challenges and learn how to handle them with Entity Framework. What's Inside Querying a relational database with LINQ Using EF Core in business logic Integrating EF with existing C# applications Applying domain-driven design to EF Core Getting the best performance out of EF Core Covers EF Core 2.0 and 2.1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值