AutoMapper + EF Core:查询嵌套属性等于值的位置
Posted
技术标签:
【中文标题】AutoMapper + EF Core:查询嵌套属性等于值的位置【英文标题】:AutoMapper + EF Core: Query where nested property equals value 【发布时间】:2021-12-05 16:26:44 【问题描述】:看起来很简单,但我无法使用 EF 核心执行以下查询。
public class ParentThing // EF entity
public int Id get; set;
public string Name get; set;
public int? ChildThingId get; set;
public ChildThing Child get; set ;
public class ChildThing // EF entity
public int Id get; set;
public string Name get; set;
public class ChildDTO
public int Id get; set;
public class ParentDTO
public string Name get; set;
public ChildDTO Child get; set;
public static void Run(int id, DbSet<ParentThing> dbSet, IMapper mapper)
// mappings are something like this...
// CreateMap<ChildThing, ChildDTO>();
// CreateMap<ParentThing, ParentDTO>();
var dtos = mapper.ProjectTo<ParentDTO>(dbSet)
.Where(e => e.Child.Id == id) // this throws an exception
.ToList();
尝试执行查询时出现以下异常。
System.InvalidOperationException: The binary operator Equal is not defined for the types 'System.Nullable`1[System.Int32]' and 'System.Int32'.
我尝试将查询到的 id 转换为可为空的 int 并得到以下异常。
System.InvalidOperationException: Rewriting child expression from type 'System.Int32' to type 'System.Nullable`1[System.Int32]' is not allowed, because it would change the meaning of the operation. If this is intentional, override 'VisitUnary' and change it to allow this rewrite.
我真的需要展平嵌套属性才能查询它们吗?
【问题讨论】:
尝试更新 EF Core。过滤器也必须在ProjectTo
之前。
你试过这个吗.Where(e => e.Child!=null && e.Child.Id == id)
首先检查空对象仍然会引发异常:System.InvalidOperationException:不允许将子表达式从“System.Int32”类型重写为“System.Nullable`1[System.Int32]”类型,因为它会改变操作的含义。如果这是故意的,请覆盖“VisitUnary”并将其更改为允许此重写。
在调用 ProjectTo 之前过滤 dbSet 似乎确实有效,尽管感觉有点笨拙。我将看看底层的查询字符串,看看它有多复杂。
您是否在没有映射的情况下尝试了相同的查询?它可以帮助找到异常源
【参考方案1】:
我真的需要展平嵌套属性才能查询它们吗?
看起来你需要。
整个问题是“子”引用是否可以是null
(即可选或必需)。
如果它是可选的,除了扁平化引用对象属性并使它们可以为空之外,没有其他解决方案。或者在投影之前对实体查询应用过滤器。这是因为 EF Core 处理导航属性的方式与投影不同(并且更好)。 AutoMapper 所做的相当于
db.Set<ParentThing>()
.Select(p => new ParentDto
// ...
Child = p.ChildThing == null ? null : new ChildDto ... )
.Where(...);
您得到的错误是由于条件构造。但是如果你不使用它,那么当数据为空时你会得到不同的异常。
因此,仅在需要参考时才有可能提供干净的解决方案。在这种情况下,您只需要让 AutoMapper 消除空检查并直接生成Child = new ChildDto ...
。可以用DoNotAllowNull
指定,例如
CreateMap<ParentThing, ParentDTO>()
.ForMember(dst => dst.Child, opt => opt.DoNotAllowNull());
但同样,这仅适用于必需的参考。
【讨论】:
@Lucian 我知道你是 AM 的主要贡献者之一。但是......我总是在发布之前测试这些东西。DoNotAllowNull
(或AllowNullDestinationValues = false
)消除了null
签入投影。
感谢您的反馈
@IvanStoev 是的,对不起,我猜这些名字没有多大帮助。【参考方案2】:
既然你正在使用
public ChildThing Child get; set ;
会混淆实体框架
所以我认为创建显式导航属性会有所帮助
public class ParentThing // EF entity
public int Id get; set;
public string Name get; set;
public int? ChildThingId get; set;
public ChildThing ChildThing get; set ;
public class ChildThing // EF entity
public int Id get; set;
public string Name get; set;
public string Name get; set;
public ParentThing ParentThing get; set ;
我想知道您的 DbSet 是否包含 Child,它可以为 null 并且可能导致异常。但是在这种情况下,您可以使用此代码
var parents= dbSet
.Where(e => e.ChildThingId!=null && ( (int) e.ChildThingId ) == id)
.ToList();
var dtos = mapper.Map<List<ParentThing>, List<ParentDto>>(parents);
【讨论】:
我已经在原帖中阐明了 ParentThing 类。导航属性存在,但我忘记在示例中包含它。它使我可以在 ProjectTo 之前进行过滤,但想知道是否有办法在 ProjectTo 之后进行过滤 @WaitsAtWork 我之前告诉过你 child 是 null 。你试过 ChildThingId==id 吗? 我做到了,它可以作为一种解决方法。如果这是唯一的方法,那么我会关闭这个问题。以上是关于AutoMapper + EF Core:查询嵌套属性等于值的位置的主要内容,如果未能解决你的问题,请参考以下文章
使用 entityFramework Automapper .Net Core 的嵌套映射
EF Core 相关的千倍性能之差: AutoMapper ProjectTo VS Mapster ProjectToType