试图对此进行大量搜索,但无法给出有效的答案。这是我想要做的:
我有一个实体ObjectA
,它的导航属性为ObjectB
(不是Collection类型,它是一个虚拟属性,可以通过延迟加载来加载)。当我对A进行Where查询时,我还想使用另一个映射B的表达式来扩展表达式中的属性B。
这里有一些代码演示,问题在ToObjectADto()函数中
public static Expression<Func<ObjectB, ObjectBDto>> ToObjectBDto()
{
return b => new ObjectBDto
{
Prop1 = b.Prop1,
Prop2 = b.Prop2;
};
}
public static Expression<Func<ObjectA, ObjectADto>> ToObjectADto()
{
return a => new ObjectADto
{
Name = a.Name,
SomeProperty = a.SomeProperty,
ObjectB = /* How can I call the ToObjectBDto Expression here without re-writing it? */
};
}
var aDto = _dbContext.ObjectAs.Where(q => q.SomeProperty > 0).Select(ToObjectADto());
我试图创建一个已编译的表达式:
private static _toBDtoCompiled = ToObjectBDto().Compile();
,然后像下面这样在ToObjectADto()
中调用它,但我收到了API Error There is already an open DataReader associated
错误,因为它是在客户端执行的。
public static Expression<Func<ObjectA, ObjectADto>> ToObjectADto()
{
return a => new ObjectADto
{
Name = a.Name,
SomeProperty = a.SomeProperty,
ObjectB = _toBDto().Invoke(a.ObjectB)
};
}
答案 0 :(得分:2)
我的建议是为自己节省工作并利用AutoMapper。这样做的好处是Automapper可以通过master
馈送EF的IQueryable
实现,以构建查询并填充DTO图。
ProjectTo
可以在映射中设置无法在对象和DTO之间推断的任何特定映射。 var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ObjectA, ObjectADto>();
cfg.CreateMap<ObjectB, ObjectBDto>();
});
var aDto = _dbContext.ObjectAs.Where(q => q.SomeProperty > 0).ProjectTo<ObjectADto>(config);
与自定义映射相比的优势在于,它将构建相关的查询,而不会导致延迟加载命中,也不会冒EF无法转换为SQL的触发代码的风险。 (一个查询可填充所有相关的DTO)
Automapper可以协助将DTO中的值复制回新实体或更新现有实体:
ProjectTo
新..
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<NewObjectADto, ObjectA>();
cfg.CreateMap<UpdateObjectADto, ObjectA>();
});
var mapper = config.CreateMapper();
...或更新现有内容。
var objectA = mapper.Map<ObjectA>(dto);
_dbContext.ObjectAs.Add(objectA);
DTO在其中反映了创建新对象所需的数据,或允许客户端更新以更新现有对象的数据。目标是保持更新/添加/删除操作尽可能原子化,而不是传递大型复杂对象/ w亲戚一次全部更新。即之类的操作,例如“ AddObjectBToA”,“ RemoveObjectBFromA”等,而不是通过单个“ UpdateObjectA”解决所有操作。
答案 1 :(得分:1)
很遗憾,C#无法处理将lambda编译为表达式,其中一个表达式调用另一个表达式。特别是因为表达式树可以表示这种情况。但是,EF Core 3或更高版本无论如何都不会浏览调用表达式。
Automapper可能更容易。但是,如果您不想使用第三方代码,则必须自己内联表达式。包括用方法的参数替换任何ParameterExpression
。
public static R Invoke<T, R>(this Expression<Func<T, R>> expression, T argument) => throw new NotImplementedException();
// etc for expressions with more parameters
public class InlineVisitor : ExpressionVisitor {
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.Name == "Invoke"
&& node.Object == null
&& node.Arguments.Count >= 1
&& node.Arguments[0] is LambdaExpression expr)
return Visit(
new ReplacingExpressionVisitor(
expr.Parameters.ToArray(),
node.Arguments.Skip(1).ToArray())
.Visit(expr.Body)
);
return base.VisitMethodCall(node);
}
}
// usage;
public static Expression<Func<ObjectA, ObjectADto>> ToObjectADto()
{
var ToBorNotToB = ToObjectBDto();
Expression<Func<ObjectA, ObjectADto>> expr = a => new ObjectADto
{
Name = a.Name,
SomeProperty = a.SomeProperty,
ObjectB = ToBorNotToB.Invoke(a.ObjectB)
};
return new InlineVisitor().VisitAndConvert(expr), "");
}