有没有办法将 Func<T1, bool> 映射到 Func<T2, bool>?
Is there a way to map Func<T1, bool> to Func<T2, bool>?
所以,我想知道是否可以用 c# 做下一件事:
我有一个数据库模型 - 假设它是 Car
:
public class Car {
public string Id {get;set;}
public string Name {get;set}
}
以及 someDbContext
中此类型的 DbSet:
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);
有什么想法吗?
如果您只想处理重写 属性 访问,您可以使用 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
。
你有一大堆选择:
从上下文 class 中导出 Dto class。这样你就可以正常使用多态性了。
提取接口并在您的 Dto 和上下文 classes 中实现它。然后同上,使用多态。
使用鸭子打字。在 C# 中,这是通过 dynamic
关键字完成的。您失去了 Intellisense 和编译时错误检查,但您的代码将正常工作。
反思。它有很多代码,速度很慢,实际上是 #3 的一个更糟糕的版本,但如果你真的尝试,你可以将它拼凑起来。
Automapper 之类的东西可以帮助您将上下文分段映射到 Dto,但它不会帮助您翻译 lambda 函数过滤器。
所以,我想知道是否可以用 c# 做下一件事:
我有一个数据库模型 - 假设它是 Car
:
public class Car {
public string Id {get;set;}
public string Name {get;set}
}
以及 someDbContext
中此类型的 DbSet:
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);
有什么想法吗?
如果您只想处理重写 属性 访问,您可以使用 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
。
你有一大堆选择:
从上下文 class 中导出 Dto class。这样你就可以正常使用多态性了。
提取接口并在您的 Dto 和上下文 classes 中实现它。然后同上,使用多态。
使用鸭子打字。在 C# 中,这是通过
dynamic
关键字完成的。您失去了 Intellisense 和编译时错误检查,但您的代码将正常工作。反思。它有很多代码,速度很慢,实际上是 #3 的一个更糟糕的版本,但如果你真的尝试,你可以将它拼凑起来。
Automapper 之类的东西可以帮助您将上下文分段映射到 Dto,但它不会帮助您翻译 lambda 函数过滤器。