简体   繁体   English

搜索投影的 EF Core 导航属性

[英]Searching Against Projected EF Core Navigation Properties

Using EF Core 3.1, I'm trying to perform a search against a nullable navigation property (aka left join) after it has been projected to a different type.使用 EF Core 3.1,我尝试在可空导航属性(又名左连接)投影到不同类型后对其执行搜索。

For example:例如:

var dtos = await query
    .Select(x => new RootEntityDto
    {
        // ...

        Nested = x.NestedId.HasValue
            ? new NestedEntityDto
            {
                // ...

                Description = x.Nested.Description
            }
            : null
    })
    .Where(x => x.Nested.Description.Contains("2"))
    .ToArrayAsync();

The problem is, it gives me an error over the ternary that may conditionally result in null:问题是,它给了我一个可能有条件地导致空值的三元错误:

The LINQ expression 'DbSet<RootEntity>
    .LeftJoin(
        outer: DbSet<NestedEntity>, 
        inner: f => EF.Property<Nullable<int>>(f, "NestedId"), 
        outerKeySelector: o => EF.Property<Nullable<int>>(o, "Id"), 
        innerKeySelector: (o, i) => new TransparentIdentifier<RootEntity, NestedEntity>(
            Outer = o, 
            Inner = i
        ))
    .Where(f => EF.Property<Nullable<int>>(f.Inner, "Id") != null ? new NestedEntityDto{ 
        Description = f.Inner.Description, 
    }
     : null.Description.Contains("2"))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

If I try precisely the same thing before projecting the entity to a DTO, I have no problems:如果我在将实体投影到 DTO 之前尝试完全相同的事情,我没有问题:

var entities = await query
    .Where(x => x.Nested.Description.Contains("2"))
    .ToArrayAsync();

I realize the simple solution is to perform the Where statement before the projection occurs, but that's not an option.我意识到简单的解决方案是在投影发生之前执行Where语句,但这不是一个选项。 The above examples are simplified in order to illustrate the intent.为了说明意图,对上述示例进行了简化。 I'm also not interested in evaluating the query client-side just because I don't know a better alternative to the ternary.我也对评估查询客户端不感兴趣,因为我不知道三元的更好替代方案。

Ideally, I'm just looking for the best practice for conditionally projecting a left join navigation property, such that I can still perform searches on it.理想情况下,我只是在寻找有条件地投影左连接导航属性的最佳实践,这样我仍然可以对其执行搜索。

Edit: I decided to try AutoMapper 10.1.1 and received a similar error:编辑:我决定尝试 AutoMapper 10.1.1 并收到类似的错误:

var dtos = await query
    .ProjectTo<RootEntityDto>(_mapper.ConfigurationProvider, x => x.Nested)
    .Where(x => x.Nested.Description.Contains("2"))
    .ToArrayAsync();
The LINQ expression 'DbSet<RootEntity>
    .LeftJoin(
        outer: DbSet<NestedEntity>, 
        inner: f => EF.Property<Nullable<int>>(f, "NestedId"), 
        outerKeySelector: o => EF.Property<Nullable<int>>(o, "Id"), 
        innerKeySelector: (o, i) => new TransparentIdentifier<RootEntity, NestedEntity>(
            Outer = o, 
            Inner = i
        ))
    .Where(f => EF.Property<Nullable<int>>(f.Inner, "Id") == null ? null : new NestedEntityDto{ 
        Description = f.Inner.Description
    }
    .Description.Contains("2"))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

EF Core 3 completely changed how the queries were evaluated and not all query constructs are supported. EF Core 3 彻底改变了查询的评估方式,并非所有查询构造都受支持。 I suspect trying to filter after the DTO projection is the underlying issue here.我怀疑在 DTO 投影之后尝试过滤是这里的潜在问题。 Could you test upgrading to EF Core 5 with this query and see if it still fails there as well as more query patterns are supported with 5.您能否使用此查询测试升级到 EF Core 5,看看它是否仍然失败,以及 5.x 支持更多查询模式。

I did some research into what AutoMapper was doing and discovered a convention that works:我对 AutoMapper 正在做什么进行了一些研究,并发现了一个有效的约定:

var dtos = await query
    .Select(x => new RootEntityDto
    {
        // ...

        Nested = x.Nested == null
            ? null
            : new NestedEntityDto
            {
                // ...

                Description = x.Nested.Description
            }
    })
    .Where(x => x.Nested.Description.Contains("2"))
    .ToArrayAsync();

Notice that I inverted the ternary and switched what would have been !x.NestedId.HasValue to x.Nested == null .请注意,我反转了三元并将本来应该是!x.NestedId.HasValuex.Nested == null But even with those changes, I still had to upgrade to EF Core 5.但即使有这些变化,我仍然必须升级到 EF Core 5。

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

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