针对投影的 EF Core 导航属性进行搜索
Searching Against Projected EF Core Navigation Properties
使用 EF Core 3.1,我正在尝试对可空导航 属性(a.k.a.left join)在投影到不同类型后执行搜索。
例如:
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();
问题是,它给了我一个可能有条件地导致 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.
如果我在将实体投影到 DTO 之前尝试完全相同的事情,我没有问题:
var entities = await query
.Where(x => x.Nested.Description.Contains("2"))
.ToArrayAsync();
我意识到简单的解决方案是在投影发生之前执行 Where
语句,但这不是一个选项。上面的例子被简化以说明意图。我也对评估查询客户端不感兴趣,只是因为我不知道三元的更好替代方法。
理想情况下,我只是在寻找有条件地投影左连接导航的最佳实践 属性,这样我仍然可以对其执行搜索。
编辑:我决定试用 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 完全改变了查询的评估方式,并非所有查询结构都受支持。我怀疑在 DTO 投影之后尝试过滤是这里的根本问题。您能否使用此查询测试升级到 EF Core 5,看看它是否仍然失败,以及 5 支持更多查询模式。
我对 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();
请注意,我反转了三进制 和 将 !x.NestedId.HasValue
转换为 x.Nested == null
。但即使有这些更改,我仍然必须升级到 EF Core 5。
使用 EF Core 3.1,我正在尝试对可空导航 属性(a.k.a.left join)在投影到不同类型后执行搜索。
例如:
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();
问题是,它给了我一个可能有条件地导致 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.
如果我在将实体投影到 DTO 之前尝试完全相同的事情,我没有问题:
var entities = await query
.Where(x => x.Nested.Description.Contains("2"))
.ToArrayAsync();
我意识到简单的解决方案是在投影发生之前执行 Where
语句,但这不是一个选项。上面的例子被简化以说明意图。我也对评估查询客户端不感兴趣,只是因为我不知道三元的更好替代方法。
理想情况下,我只是在寻找有条件地投影左连接导航的最佳实践 属性,这样我仍然可以对其执行搜索。
编辑:我决定试用 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 完全改变了查询的评估方式,并非所有查询结构都受支持。我怀疑在 DTO 投影之后尝试过滤是这里的根本问题。您能否使用此查询测试升级到 EF Core 5,看看它是否仍然失败,以及 5 支持更多查询模式。
我对 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();
请注意,我反转了三进制 和 将 !x.NestedId.HasValue
转换为 x.Nested == null
。但即使有这些更改,我仍然必须升级到 EF Core 5。