简体   繁体   English

是否可以将复杂的 EF Core“.Include”调用重写为“Join”调用?

[英]Is it possible to rewrite complex EF Core ".Include" calls to "Join" calls?

I have two EF Core entity models named Service and ServiceBranch:我有两个名为 Service 和 ServiceBranch 的 EF Core 实体模型:

public class Service
{
    public Guid ID { get; set; }
    public Guid? ParentServiceID { get; set; } // foreign key to the same table
    public string Name { get; set; }
    public DateTime? DateDeleted { get; set; }

    public virtual ICollection<Service> InverseParent { get; set; } // navigation property
    public virtual ICollection<ServiceBranch> ServiceBranches { get; set; } // navigation property
}

public class ServiceBranch
{
    public Guid ID { get; set; }
    public Guid ServiceID { get; set; } // foreign key
    public string Name { get; set; }
    public DateTime? DateDeleted { get; set; }

    public virtual Service Service { get; set; } // navigation property
}

For simplicity, let's say parent Service can have only one level of children Services.为简单起见,假设父服务只能有一个级别的子服务。 both parent and children services are being referenced by a lot of service branches.许多服务分支都引用了父服务和子服务。

I want to update DateDeleted field for a specific Service ID and update DateDeleted in all other rows that are referencing it (if that field doesn't already have a value).我想更新特定服务 ID 的 DateDeleted 字段,并更新引用它的所有其他行中的 DateDeleted(如果该字段尚无值)。

Currently my call to bring all the required entities looks like this:目前,我要求携带所有必需的实体的电话如下所示:

var serviceEntity = _context.Set<Service>().Where(x => x.ID == neededID && x.DateDeleted == null)
                .Include(x => x.ServiceBranches)
                .Include(x => x.InverseParent)
                    .ThenInclude(InverseParent => InverseParent.ServiceBranches)
                .FirstOrDefault();

I use Where clause for the main entity but unfortunately I cannot use Where inside.Includes since we haven't upgraded to EF Core 5.0 yet (which supports filtering inside.Includes).我对主要实体使用 Where 子句,但不幸的是我不能使用 Where inside.Includes,因为我们还没有升级到 EF Core 5.0(支持过滤 inside.Includes)。

Thus I bring too many unneeded rows (where DateDeleted already has a value) from the database and then filter them within a foreach loop by comparing that field to null:因此,我从数据库中获取了太多不需要的行(其中 DateDeleted 已经有一个值),然后通过将该字段与 null 进行比较来在 foreach 循环中过滤它们:

if (myentity.DateDeleted == null) myentity.DateDeleted = DateTime.Now;

I want to rewrite my query so that I use "join" instead of ".Include", something like this:我想重写我的查询,以便使用“join”而不是“.Include”,如下所示:

var serviceEntity = from service in _context.Set<Service>()
                where service.ID == neededID && service.DateDeleted == null
                join branches in _context.Set<ServiceBranch>() on service.ID equals branches.ServiceID
                where branches.DateDeleted == null // filter which I cannot use with .Include-s
                let // whatever, can't make it work
                select // etc

I can't get my head around how to rewrite it.我不知道如何重写它。 Or if it is even possible to do it with joins.或者,如果甚至可以通过连接来做到这一点。

In this particular case you don't need joins, but flat list containing the desired Service entity plus all its children.在这种特殊情况下,您不需要连接,而是包含所需Service实体及其所有子实体的平面列表。 In general this requires recursive query (not supported by LINQ / EF Core), but for single level it is a matter of simple filter like一般来说,这需要递归查询(LINQ / EF Core 不支持),但对于单层来说,这是一个简单的过滤器问题,比如

service.ID == neededID || service.ParentServiceID == neededID

Then you could apply the additional DateDeleted criteria.然后您可以应用附加的DateDeleted条件。 Finally, to get the related ServiceBranch entities with DateDeleted filter, instead of Include just use projection ( Select ).最后,要使用DateDeleted过滤器获取相关的ServiceBranch实体,而不是Include使用投影 ( Select ) 包含。

And when processing the returned data, use the projected entities and ignore their navigation properties.并且在处理返回的数据时,使用投影实体并忽略它们的导航属性。 eg例如

var itemsToUpdate = _context.Set<Service>()
    .Where(service => (service.ID == neededID || service.ParentServiceID == neededID)
        && service.DateDeleted == null)
    .Select(service => new
    {
        Service = service,
        ServiceBrances = service.ServiceBranches
            .Where(branch => branch.DateDeleted == null),
    });

foreach (var item in itemsToUpdate)
{
    item.Service.DateDeleted = DateTime.Now;
    foreach (var branch in item.ServiceBrances)
        branch.DateDeleted =  DateTime.Now;
}

Include puts the loaded objects directly on your Entity Framework model, and that model is really supposed to directly represent what's in the database. Include将加载的对象直接放在您的实体框架 model 上,而 model 实际上应该直接表示数据库中的内容。 If you're wanting a filtered collection, I'd recommend coming up with a different model that represents what you're trying to represent, and then using Select to project to that model. Something like this:如果你想要一个过滤的集合,我建议提出一个不同的 model 来代表你想要代表的东西,然后使用 Select 投射到那个 model。像这样:

var serviceEntity = _context.Set<Service>()
    .Where(x => x.ID == neededID && x.DateDeleted == null)
    .Select(x => new // you can make this a named type if necessary
        {
           Service = x,
           ActiveBranches = x.ServiceBranches.Where(b => b.DateDeleted == null),
           InverseParent,
           InverseParentServiceBranches = x.InverseParent.ServiceBranches
        })
    .FirstOrDefault();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM