EF Core使用Include
和join
在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);
这样数据库就执行了一个左连接,把Album
和Genre
的所有字段全部连起来了,并且Include()
是立即查询的,像ToList()
一样,不会稍后延迟优化后再加载。
这样其实效率很低,因为如果两张表记录很大,那么连接是个费时费资源的事情,建议少用,或者先筛选出需要的结果集再连接。
Include
和ThenInclude
区别
“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还有Client
的ClientMessage
using (var context = new MyContext())
{
var customers = context.Company
.Include(i => i.Manager )
.ThenInclude(a => a.ClientMessage )
.ToList();
}
这相当于用关联出来的manager
去关联client
EF的表左连接方法Include
和Join
在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);
这样数据库就执行了一个左连接,把Album
和Genre
的所有字段全部连起来了,并且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);
此时返回的结果就正确了!