C# 有没有办法映射Func<;T1,bool>;to Func<;T2,bool>;?

C# 有没有办法映射Func<;T1,bool>;to Func<;T2,bool>;?,c#,entity-framework,linq,linq-expressions,C#,Entity Framework,Linq,Linq Expressions,所以,我想知道是否有可能在c中做下一件事: 我有一个DB模型-假设它是汽车: public class Car { public string Id {get;set;} public string Name {get;set} } 以及someDbContext中此类型的数据库集: public DbSet<Car> Cars {get;set;} 结果我们得到了这样的结果: var select = new Func<CarDto, bool>(car =

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

我有一个DB模型-假设它是
汽车

public class Car {
  public string Id {get;set;}
  public string Name {get;set}
}
以及
someDbContext
中此类型的数据库集:

public DbSet<Car> Cars {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);
var newFunc = mapper.Map<Func<Car, bool>>(select);
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);
    }
}

有什么想法吗?

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

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

// And somehow use this expression for other type Car
someDbContext.Cars.Where(select);
var newFunc = mapper.Map<Func<Car, bool>>(select);
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);
    }
}
公共类程序
{
公共静态void Main()
{
表达式expr=x=>x.Name==“BMW”;
替换变量=替换参数(expr);
}
专用静态表达式替换参数(LambdaExpression expr)
{
如果(expr.Parameters.Count!=1)
抛出新ArgumentException(“应为1个参数”,nameof(expr));
var newParameter=Expression.Parameter(typeof(T),expr.Parameters[0].Name);
var visitor=新参数replaceVisitor()
{
目标=表达式参数[0],
替换=新参数,
};
var rewritenbody=visitor.Visit(expr.Body);
返回表达式.Lambda(RewritenBody,newParameter);
}
}
公共类参数replaceVisitor:ExpressionVisitor
{
公共参数表达式目标{get;set;}
公共参数表达式替换{get;set;}
受保护的重写表达式VisitMember(MemberExpression节点)
{
if(node.Expression==this.Target)
{
//尝试在目标类型上查找具有相同名称的属性
var members=this.Replacement.Type.GetMember(node.Member.Name、node.Member.MemberType、BindingFlags.Public | BindingFlags.Instance);
如果(members.Length!=1)
{
抛出新的ArgumentException($“无法在{this.Target.type}上找到类型为{node.member.MemberType}的单个成员{node.member.Name}”);
}
返回表达式.MakeMemberAccess(this.Replacement,members[0]);
}
返回base.VisitMember(节点);
}
}
我们需要将
LambdaExpression
解构为它的主体和参数。我们需要创建一个具有正确类型的新参数,并用新参数替换旧参数的所有用法。这就是访问者进入的地方:每当它看到您访问旧参数上的成员时,它都会尝试查找新参数上的相应成员,然后访问该成员


然后,我们使用重写的正文和新参数构造一个新的
LambdaExpression

您有一大堆选项:

  • 从上下文类派生Dto类。这样,您就可以正常使用多态性

  • 提取接口并在Dto和上下文类中实现它。和上面一样,使用多态性

  • 使用duck类型。在C#中,这是通过
    dynamic
    关键字完成的。您将丢失Intellisense和编译时错误检查,但您的代码将正常工作

  • 反思。它的代码很多,速度很慢,实际上是#3的一个更糟糕的版本,但是如果你真的尝试的话,你可以把它拼凑起来


  • 像Automapper这样的东西可以帮助您将上下文映射到数据,并逐块映射,但是它不会帮助您转换lambda函数过滤器。

    但是这种方法只适用于包含
    Name
    属性的
    car
    类型。最好将
    Func
    映射到
    Func
    ,然后在linq中使用它query@Batuhan的确修改这一点留给读者作为练习:)@LabLab显然,如果您有
    Car
    对象的集合,则需要使用
    表达式而不是
    表达式对其进行过滤。在本文中,您的问题被解释为询问如何为不同的对象类型构造谓词——如果这不是您的问题,请澄清它@对不起,我的错。问题已编辑假定您所说的是
    Expression
    而不是
    Func
    ?即使使用表达式,进行映射也有点麻烦:您需要将对第一个对象成员的每个访问替换为对新对象上相应成员的访问。我的答案是,从头开始,两者都更容易构建does@canton7我希望构建不同的查询(如果可能的话),而不仅仅是一个参数<代码>x=>x.Name==“bla bla”和&x.Id!=“bla bla”
    例如。您只是在访问属性,还是也在调用方法/运算符/etc?@canton7 only属性,因为我要传递的对象只是数据模型。从上下文派生Dto是一种很好的做法?这取决于您的特定类以及它们之间的密切关系。在你的例子中,你完全可以使用相同的类。这是一个简单的例子来简要解释我的问题。事实上,我使用的是更复杂的实体,有很多与其他实体的链接,等等,我说,这要看情况而定。就我个人而言,我会(并且已经)使用
    动态版本。Duck类型非常强大。