如何将 MemberInitExpression 添加到 Bindings 其他 Lambda MemberInitExpression

问题描述 投票:0回答:1

我有以下课程:

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。

c# linq expression linq-to-entities linq-expressions
1个回答
1
投票

您无需将成员添加到绑定列表中,而是构建一个新表达式。

使用以下助手来替换参数(如果您引用了 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
© www.soinside.com 2019 - 2024. All rights reserved.