在 Entity Framework 中组合 Left join、.includes() 和 Group By
Combining Left join, .includes() and Group By in Entity Framework
我正在尝试从不同的表中执行多项选择并将结果连接在一起。该项目 运行 在 .Net 5 环境中使用 EF core 5.0.14。我的最终目标是拥有一个 objects 的列表,其中包含简单的字段 Id(int)、Name(string)、Date(string)(如果存在)、Email(string)、Image(string)、 Phone(string) 以及 objects 员工和 Parents.
的列表
我已将查询分为以下几类:
var firstQuery = _context.Records.GroupBy(r => r.RecordId)
.Select(g => new
{
Id = g.Key,
Date = g.Max(x => x.Date)
});
第一个查询的结果是 objects 的列表,其中包含一个 ID 和相应 ID 的最大日期
var secondQuery = _context.Users.Include(u => u.Employees)
.Include(u => u.Parents)
第二个查询生成 objects 的列表,其中包含简单字段中的大部分数据和列表(对于员工和 Parents)
最后我尝试加入它们,这样如果 firstQuery 中有记录与 secondQuery 中的 Id 相匹配,它们应该被合并,否则日期字段应该为 null:
var result = (from u in secondQuery
from p in firstQuery
.Where(p => p.Id==u.Id)
.DefaultIfEmpty()
select new UserRecordsRequest{
Id = u.Id,
Email = u.Email,
Name = u.Name,
Date = (string?)p.Date,
Phone = u.Phone,
Image = u.Image,
Employees = u.Employees,
Parents = u.Parents,
}.toList()
UserRecordsModel 如下所示:
public class UserRecordsModel
{
[Key]
public int Id { get; set; }
public string Email { get; set; }
public string Date { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public virtual List<EmployeeModel> Employees { get; set; }
public virtual List<UserModel> Parents { get; set; }
public UserRecordsModel() { }
public UserRecordsModel(UserRecordsRequest userRecordsRequest)
{
Email = userRecordsRequest.Email;
Name = userRecordsRequest.Name;
Date = userRecordsRequest.Date;
Phone = userRecordsRequest.Phone;
}
}
简化ER-diagram:
但这会引发异常:
System.InvalidOperationException: Unable to translate collection subquery in projection since the parent query doesn't project key columns of all of it's tables which are required to generate results on client side. This can happen when trying to correlate on keyless entity or when using 'Distinct' or 'GroupBy' operations without projecting all of the key columns.
在最后一个连接查询之前,单独查询中的一切看起来都很好。我花了很多时间试图解决这个问题。我对这个问题有什么误解,你会如何改变它?
那是因为查询开始是用无键实体类型构建的,而且
您 Include
该无键实体的一些集合导航属性。
首先,您可以在 SQl-Profiler 中检查生成的等效查询,或者通过 Debug watcher 查看生成的查询。
但是通过执行该查询,第一个 sub-query 将首先执行,因此请尝试强制 firstQuery
成为 运行 之前 ToList()
像:
var firstQuery = _context.Records.GroupBy(r => r.RecordId)
.Select(g => new
{
Id = g.Key,
Date = g.Max(x => x.Date)
}).ToList();
如果这不起作用,试试这个:
var veryFirstQuery = _context.Records.GroupBy(r => r.RecordId)
.Select(g => new
{
Id = g.Key,
Date = g.Max(x => x.Date)
}).SelectMany(x=> x.Select(u=> u));
var firstQuery = veryFirstQuery.Select(x=> x.Id ).ToList();
然后(这不是正确的语法,只是从您的查询中复制并更改我认为需要的内容):
var result = (from u in secondQuery
where firstQuery.Contain(u.Id)
select u)
.DefaultIfEmpty()
select new UserRecordsRequest{
Id = u.Id,
Email = u.Email,
Name = u.Name,
Date = (string?)veryFirstQuery.FirstOrDefault(x=> x.Id == u.Id).Date,
Phone = u.Phone,
Image = u.Image,
Employees = u.Employees,
Parents = u.Parents,
}.ToList()
或者您可以:
var result = (from u in secondQuery
where firstQuery.Contain(u.Id)
select u)
.DefaultIfEmpty().ToList()
.Select(u => new UserRecordsRequest
{
Id = u.Id,
Email = u.Email,
Name = u.Name,
Date = (string?)veryFirstQuery.FirstOrDefault(sb => sb.Id == u.Id).Date,
Phone = u.Phone,
Image = u.Image,
Employees = u.Employees,
Parents = u.Parents,
});
我想说的是,您可以将主要查询分成小查询以防止 InvalidOperationException
或在 Fluent-Api.
中查看您的模型关系
我正在尝试从不同的表中执行多项选择并将结果连接在一起。该项目 运行 在 .Net 5 环境中使用 EF core 5.0.14。我的最终目标是拥有一个 objects 的列表,其中包含简单的字段 Id(int)、Name(string)、Date(string)(如果存在)、Email(string)、Image(string)、 Phone(string) 以及 objects 员工和 Parents.
的列表我已将查询分为以下几类:
var firstQuery = _context.Records.GroupBy(r => r.RecordId)
.Select(g => new
{
Id = g.Key,
Date = g.Max(x => x.Date)
});
第一个查询的结果是 objects 的列表,其中包含一个 ID 和相应 ID 的最大日期
var secondQuery = _context.Users.Include(u => u.Employees)
.Include(u => u.Parents)
第二个查询生成 objects 的列表,其中包含简单字段中的大部分数据和列表(对于员工和 Parents)
最后我尝试加入它们,这样如果 firstQuery 中有记录与 secondQuery 中的 Id 相匹配,它们应该被合并,否则日期字段应该为 null:
var result = (from u in secondQuery
from p in firstQuery
.Where(p => p.Id==u.Id)
.DefaultIfEmpty()
select new UserRecordsRequest{
Id = u.Id,
Email = u.Email,
Name = u.Name,
Date = (string?)p.Date,
Phone = u.Phone,
Image = u.Image,
Employees = u.Employees,
Parents = u.Parents,
}.toList()
UserRecordsModel 如下所示:
public class UserRecordsModel
{
[Key]
public int Id { get; set; }
public string Email { get; set; }
public string Date { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
public virtual List<EmployeeModel> Employees { get; set; }
public virtual List<UserModel> Parents { get; set; }
public UserRecordsModel() { }
public UserRecordsModel(UserRecordsRequest userRecordsRequest)
{
Email = userRecordsRequest.Email;
Name = userRecordsRequest.Name;
Date = userRecordsRequest.Date;
Phone = userRecordsRequest.Phone;
}
}
简化ER-diagram:
System.InvalidOperationException: Unable to translate collection subquery in projection since the parent query doesn't project key columns of all of it's tables which are required to generate results on client side. This can happen when trying to correlate on keyless entity or when using 'Distinct' or 'GroupBy' operations without projecting all of the key columns.
在最后一个连接查询之前,单独查询中的一切看起来都很好。我花了很多时间试图解决这个问题。我对这个问题有什么误解,你会如何改变它?
那是因为查询开始是用无键实体类型构建的,而且
您 Include
该无键实体的一些集合导航属性。
首先,您可以在 SQl-Profiler 中检查生成的等效查询,或者通过 Debug watcher 查看生成的查询。
但是通过执行该查询,第一个 sub-query 将首先执行,因此请尝试强制 firstQuery
成为 运行 之前 ToList()
像:
var firstQuery = _context.Records.GroupBy(r => r.RecordId)
.Select(g => new
{
Id = g.Key,
Date = g.Max(x => x.Date)
}).ToList();
如果这不起作用,试试这个:
var veryFirstQuery = _context.Records.GroupBy(r => r.RecordId)
.Select(g => new
{
Id = g.Key,
Date = g.Max(x => x.Date)
}).SelectMany(x=> x.Select(u=> u));
var firstQuery = veryFirstQuery.Select(x=> x.Id ).ToList();
然后(这不是正确的语法,只是从您的查询中复制并更改我认为需要的内容):
var result = (from u in secondQuery
where firstQuery.Contain(u.Id)
select u)
.DefaultIfEmpty()
select new UserRecordsRequest{
Id = u.Id,
Email = u.Email,
Name = u.Name,
Date = (string?)veryFirstQuery.FirstOrDefault(x=> x.Id == u.Id).Date,
Phone = u.Phone,
Image = u.Image,
Employees = u.Employees,
Parents = u.Parents,
}.ToList()
或者您可以:
var result = (from u in secondQuery
where firstQuery.Contain(u.Id)
select u)
.DefaultIfEmpty().ToList()
.Select(u => new UserRecordsRequest
{
Id = u.Id,
Email = u.Email,
Name = u.Name,
Date = (string?)veryFirstQuery.FirstOrDefault(sb => sb.Id == u.Id).Date,
Phone = u.Phone,
Image = u.Image,
Employees = u.Employees,
Parents = u.Parents,
});
我想说的是,您可以将主要查询分成小查询以防止 InvalidOperationException
或在 Fluent-Api.