Linq - 包含的顺序

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 次并在内存中过滤结果。首先从客户状态中选择所有数据,然后从状态中选择所有数据,然后从客户中选择所有数据,然后过滤内存中的所有数据。有没有其他有效的方法来做到这一点?

这就是我通过实体类准备的方式 在此处输入图片说明

Chr*_*att 5

我认为正在发生的事情是您实际上正在覆盖Includeand ThenIncludeInclude明确地预先加载导航属性。但是,您正在做的一些事情可能会阻碍这一点。

首先,您要选择一个新的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,因为选择这些关系的行为将导致连接。


the*_*One 5

正如@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 {...}),则可能表明数据库架构没有很好地定义应用程序的需求,因此可能需要重构。