Chr*_*eld 4 c# linq entity-framework asp.net-core
我有一种情况,需要为 Include 对象完成 OrderBy。这是我迄今为止尝试过的方式
Customers query = null;
try
{
query = _context.Customers
.Include(x => x.CustomerStatus)
.ThenInclude(x => x.StatusNavigation)
.Select(x => new Customers()
{
Id = x.Id,
Address = x.Address,
Contact = x.Contact,
Name = x.Name,
CustomerStatus = new List<CustomerStatus>
{
x.CustomerStatus.OrderByDescending(y => y.Date).FirstOrDefault()
}
})
.FirstOrDefault(x => x.Id == 3);
}
catch (Exception ex)
{
throw;
}
Run Code Online (Sandbox Code Playgroud)
上面的代码成功地对 include 元素进行了排序,但不包括它的子表。例如:客户包括 CustomerStatus 但 CustomerStatus 不包括 StatusNavigation 表。
我什至尝试过这个,但它都不能帮助我
_context.Customers
.Include(x => x.CustomerStatus.OrderByDescending(y => y.Date).FirstOrDefault())
.ThenInclude(x => x.StatusNavigation).FirstOrDefault(x => x.Id == 3);
Run Code Online (Sandbox Code Playgroud)
我做错了什么请指导我
即使我试过这种方式
var query = _context.CustomerStatus
.GroupBy(x => x.CustomerId)
.Select(x => x.OrderByDescending(y => y.Date).FirstOrDefault())
.Include(x => x.StatusNavigation)
.Join(_context.Customers, first => first.CustomerId, second => second.Id, (first, second) => new Customers
{
Id = second.Id,
Name = second.Name,
Address = second.Address,
Contact = second.Contact,
CustomerStatus = new List<CustomerStatus> {
new CustomerStatus
{
Id = first.Id,
CustomerId = first.CustomerId,
Date = first.Date,
StatusNavigation = first.StatusNavigation
}
},
}).FirstOrDefault(x => x.Id == 3);
Run Code Online (Sandbox Code Playgroud)
但这是命中数据库 3 次并在内存中过滤结果。首先从客户状态中选择所有数据,然后从状态中选择所有数据,然后从客户中选择所有数据,然后过滤内存中的所有数据。有没有其他有效的方法来做到这一点?
我认为正在发生的事情是您实际上正在覆盖Include
and ThenInclude
。Include
明确地预先加载导航属性。但是,您正在做的一些事情可能会阻碍这一点。
首先,您要选择一个新的Customer
. 仅此一点就足以打破Include
. 其次,您要覆盖放入CustomerStatus
集合中的内容。理想情况下,它应该通过 自动加载Include
,但是通过将其更改为仅包含第一个实体,您实际上是在丢弃Include
. (选择一个关系就足以引发一个连接,而无需显式调用Include
)。第三, theThenInclude
是基于 的Include
,因此覆盖可能ThenIncude
也会丢弃。
这一切都是猜想。我没有做过任何与您之前在这里所做的完全相同的事情,但其他任何事情都没有任何意义。
尝试选择一个新的CustomerStatus
:
CustomerStatus = x.CustomerStatus.OrderByDescending(o => o.Date).Select(s => new CustomerStatus
{
x.Id,
x.Status,
x.Date,
x.CustomerId,
x.Customer,
x.StatusNavigation
})
Run Code Online (Sandbox Code Playgroud)
您可以在那时删除Include
/ ThenInclude
,因为选择这些关系的行为将导致连接。
正如@Chris Pratt 提到的,一旦您在选择中创建新客户,您就会创建一个新模型。您正在丢弃由 EntityFramework 构建的模型。我的建议是查询只是:
query = _context.Customers
.Include(x => x.CustomerStatus)
.ThenInclude(x => x.StatusNavigation);
Run Code Online (Sandbox Code Playgroud)
像这样,您将拥有一个 IQueryable 对象,除非您从中进行选择,否则它不会被执行:
var customer3 = query.FirstOrDefault(x=>x.Id==3)
Run Code Online (Sandbox Code Playgroud)
它返回客户和相互关联的表(CustomerStatus 和 StatusNavigation)。然后你可以创建你想要的对象:
var customer = new Customers()
{
Id = customer3.Id,
Address = customer3.Address,
Contact = customer3.Contact,
Name = x.Name,
CustomerStatus = new List<CustomerStatus>
{
customer3.CustomerStatus.OrderByDescending(y => y.Date).FirstOrDefault()
}
})
Run Code Online (Sandbox Code Playgroud)
通过这种方式,您可以重用查询来创建不同的响应对象并对数据库进行单个查询,但缺点是使用的内存比原始查询多(即使它不应该是太大的问题)。
如果最初从数据库返回的模型不符合要求(即您总是需要执行:CustomerStatus = new List {...}),则可能表明数据库架构没有很好地定义应用程序的需求,因此可能需要重构。