我有以下课程:
class Source {
public int Id { get; set; }
public string Name { get; set; }
public SourceItem Item { get; set; }
}
class SourceItem {
public Guid Id { get; set; }
public decimal Price { get; set; }
}
class Dest {
public int Id { get; set; }
public string Name { get; set; }
public DestItem Item { get; set; }
}
class DestItem{
public Guid Id { get; set; }
public decimal Price { get; set; }
}
我构建了以下 lambda 表达式:
Expression<Func<Source, Dest>> projectionSource = source => new Dest{
Id = source.Id,
Name = source.Name
};
Expression<Func<SourceItem, DestItem>> projectionItem = item => new DestItem
{
Id = item.Id,
Price = item.Price
};
Expression<Func<Source, SourceItem>> sourceMember = source => source.Item;
Expression<Func<Dest, DestItem>> destMember = dest => dest.Item;
如何使用projectionItem lambda 表达式中的memberInitExpression 表达式将新元素绑定添加到原始投影表达式的元素绑定列表中?输出处需要 Lambda:
source => new Dest() // part of projectionSource expression
{
Id = source.Id,
Name = source.Name,
Item = new DestItem() // part of projectionItem expression
{
Id = source.Item.Id,
Price = source.Item.Price
}
}
我无法从 lambda destMember 和projectionItem 创建MemberAccess 并将其添加到绑定列表projectionSource。
您无需将成员添加到绑定列表中,而是构建一个新表达式。
使用以下助手来替换参数(如果您引用了 EF Core 3+,则可以使用它的
ReplacingExpressionVisitor
代替):
public static class ExpressionExt
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacingVisitor { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacingVisitor : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Source ? Target : base.VisitParameter(node);
}
}
}
您可以执行以下操作:
// get the body
var projectionSourceBody = projectionSource.Body as MemberInitExpression;
// get the "nested" Item members to map:
var sourceMemberE = sourceMember.Body as MemberExpression;
var destMemberE = destMember.Body as MemberExpression;
// replace the "nested" projection expression parameter with the one of the
// "root" mapping
var projectItemE = projectionItem.Body.ReplaceParameter(
projectionItem.Parameters[0],
Expression.MakeMemberAccess(projectionSource.Parameters[0], sourceMemberE.Member));
// generate Item = new DestItem {...} member binding
var assignItem = Expression.Bind(destMemberE.Member, projectItemE);
// rebuild member init expression with new binding list
var newInitE = Expression.MemberInit(projectionSourceBody.NewExpression, [.. projectionSourceBody.Bindings, assignItem]);
// create new lambda
var newProjectionSource = Expression.Lambda<Func<Source, Dest>>(newInitE, projectionSource.Parameters);
newProjectionSource.Compile(); // compile to at least verify we did something compilable