功能?,c#,entity-framework,linq,linq-expressions"/>

首页 > 解决方案 > 有没有办法映射 Func功能?

问题描述

所以,我想知道是否可以在 c# 中做下一件事情:

我有一个数据库模型 - 假设它是Car

public class Car {
  public string Id {get;set;}
  public string Name {get;set}
}

以及此类型的 DbSet someDbContext

public DbSet<Car> Cars {get;set;}

而且我还有一个CarDto

public class CarDto {
  public string Id {get;set;}
  public string Name {get;set}
}

结果我们得到这样的结果:

var select = new Func<CarDto, bool>(car => car.Name == "BMW");

// And somehow use this expression for other type Car
someDbContext.Cars.Where(select);

也许有一种方法可以Funcs像这样映射这些:

var newFunc = mapper.Map<Func<Car, bool>>(select);

有什么想法吗?

标签: c#entity-frameworklinqlinq-expressions

解决方案


如果你只想处理重写属性访问,你可以使用一个ExpressionVisitor看起来有点像这样的:

public class Program
{
    public static void Main()
    {
        Expression<Func<Car, bool>> expr = x => x.Name == "BMW";
        var replaced = ReplaceParameter<CarDto>(expr);
    }

    private static Expression<Func<T, bool>> ReplaceParameter<T>(LambdaExpression expr)
    {
        if (expr.Parameters.Count != 1)
            throw new ArgumentException("Expected 1 parameter", nameof(expr));

        var newParameter = Expression.Parameter(typeof(T), expr.Parameters[0].Name);
        var visitor = new ParameterReplaceVisitor()
        {
            Target = expr.Parameters[0],
            Replacement = newParameter,
        };
        var rewrittenBody = visitor.Visit(expr.Body);
        return Expression.Lambda<Func<T, bool>>(rewrittenBody, newParameter);
    }
}

public class ParameterReplaceVisitor : ExpressionVisitor
{
    public ParameterExpression Target { get; set; }
    public ParameterExpression Replacement { get; set; }

    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Expression == this.Target)
        {
            // Try and find a property with the same name on the target type
            var members = this.Replacement.Type.GetMember(node.Member.Name, node.Member.MemberType, BindingFlags.Public | BindingFlags.Instance);
            if (members.Length != 1)
            {
                throw new ArgumentException($"Unable to find a single member {node.Member.Name} of type {node.Member.MemberType} on {this.Target.Type}");
            }
            return Expression.MakeMemberAccess(this.Replacement, members[0]);
        }

        return base.VisitMember(node);
    }
}

我们需要将其解构LambdaExpression为它的主体和参数。我们需要创建一个具有正确类型的新参数,并将旧参数的所有用法替换为新参数。这就是访问者进来的地方:每当它看到您访问旧参数上的成员时,它就会尝试在新参数上找到相应的成员,然后访问它。

LambdaExpression然后我们使用重写的主体和新参数构造一个新的。


推荐阅读