如何将 Expression<Func<TEntity, bool>> 映射到 Expression<Func<TDbEntity, bool>>
How to map Expression<Func<TEntity, bool>> to Expression<Func<TDbEntity, bool>>
如何映射
来自:Expression<Func<TEntity, bool>>
至:Expression<Func<TDbEntity, bool>>
where TEntity: class, new() and TDbEntity: class, new()
TEntity 来自域,TDbEntity 来自基础设施层,但具有相同的属性。
有可能吗?
对于相对简单的情况(我想在你的情况下表达式很简单)你可以使用 ExpressionVisitor
只有几个覆盖。例如:
public static class ExpressionExtensions {
public static Expression<Func<TTo, bool>> ReplaceParameter<TFrom, TTo>(this Expression<Func<TFrom, bool>> target) {
return (Expression<Func<TTo, bool>>) new WhereReplacerVisitor<TFrom, TTo>().Visit(target);
}
private class WhereReplacerVisitor<TFrom, TTo> : ExpressionVisitor {
private readonly ParameterExpression _parameter = Expression.Parameter(typeof(TTo), "c");
protected override Expression VisitLambda<T>(Expression<T> node) {
// replace parameter here
return Expression.Lambda(Visit(node.Body), _parameter);
}
protected override Expression VisitMember(MemberExpression node) {
// replace parameter member access with new type
if (node.Member.DeclaringType == typeof(TFrom) && node.Expression is ParameterExpression) {
return Expression.PropertyOrField(_parameter, node.Member.Name);
}
return base.VisitMember(node);
}
}
}
用法是:
Expression<Func<ErrorModel, bool>> where = (c) => c.Created <= DateTime.UtcNow && c.ErrorCode == "code";
var replaced = where.ReplaceParameter<ErrorModel, Error>();
我前段时间遇到过同样的问题,我找到的解决方案是将主要实体转换为目标实体。但只有当属性具有相同的名称时它才有效,否则你将有额外的工作来映射过滤器上的属性。
所以让我们开始编写代码,尝试这样做:
首先创建一个扩展class
public static class ExtensionToExpression
{
public static Expression<Func<TTo, bool>> Converter<TFrom, TTo>(this Expression<Func<TFrom, bool>> expression, TTo type) where TTo : TFrom
{
// here we get the expression parameter the x from (x) => ....
var parameterName = expression.Parameters.First().Name;
// create the new parameter from the correct type
ParameterExpression parameter = Expression.Parameter(typeof(TTo), parameterName);
// asigne to allow the visit from or visitor
Expression body = new ConvertVisitor(parameter).Visit(expression.Body);
// recreate the expression
return Expression.Lambda<Func<TTo, bool>>(body, parameter);
}
}
现在创建访客 class
public class ConvertVisitor : ExpressionVisitor
{
private ParameterExpression Parameter;
public Visitante(ParameterExpression parameter)
{
Parameter = parameter;
}
protected override Expression VisitParameter(ParameterExpression item)
{
// we just check the parameter to return the new value for them
if(!item.Name.Equals(Parameter.Name))
return item;
return Parameter;
}
}
并使用:
// 添加 using 以使用您的扩展方法
public void YouMethod(Expression<Func<TEntity, bool>> filter)
{
var whereExpression = filter.Convert(default(TDbEntity));
var result = yourContext.Where(whereExpression).ToArray();
// do something
}
希望对您有所帮助
正如 Evk
所写,您需要 ExpressionVisitor.
对于比 Evk 支持的表达式更复杂的表达式,例如多参数,使用构造函数和成员初始化列表 (new { Prop1 = true }
):
public class TypeReplacer : ExpressionVisitor
{
public readonly Dictionary<Type, Type> Conversions = new Dictionary<Type, Type>();
private readonly Dictionary<Expression, Expression> ParameterConversions = new Dictionary<Expression, Expression>();
protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)
{
Type to;
if (Conversions.TryGetValue(node.Member.DeclaringType, out to))
{
var member = ConvertMember(node.Member, to);
node = Expression.Bind(member, node.Expression);
}
return base.VisitMemberAssignment(node);
}
public override Expression Visit(Expression node)
{
if (node.NodeType == ExpressionType.Lambda)
{
var lambda = (LambdaExpression)node;
var parameters = lambda.Parameters.ToArray();
for (int i = 0; i < parameters.Length; i++)
{
ParameterExpression parameter = parameters[i];
Type to;
if (Conversions.TryGetValue(parameter.Type, out to))
{
var oldParameter = parameter;
parameter = Expression.Parameter(to, parameter.Name);
ParameterConversions.Add(oldParameter, parameter);
}
}
var body = base.Visit(lambda.Body);
for (int i = 0; i < parameters.Length; i++)
{
var parameter = (ParameterExpression)base.Visit(parameters[i]);
parameters[i] = parameter;
}
// Handling of the delegate type
var arguments = node.Type.GetGenericArguments();
{
Type to;
for (int i = 0; i < arguments.Length; i++)
{
if (Conversions.TryGetValue(arguments[i], out to))
{
arguments[i] = to;
}
}
}
var delegateType = node.Type.GetGenericTypeDefinition().MakeGenericType(arguments);
node = Expression.Lambda(delegateType, body, parameters);
return node;
}
return base.Visit(node);
}
protected override Expression VisitConstant(ConstantExpression node)
{
Type to;
if (Conversions.TryGetValue(node.Type, out to))
{
node = Expression.Constant(node.Value, to);
}
return base.VisitConstant(node);
}
protected override Expression VisitNew(NewExpression node)
{
Type to;
if (Conversions.TryGetValue(node.Type, out to))
{
var constructor = node.Constructor;
BindingFlags bf = (constructor.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) |
BindingFlags.Instance;
var parameters = constructor.GetParameters();
var types = Array.ConvertAll(parameters, x => x.ParameterType);
var constructor2 = to.GetConstructor(bf, null, types, null);
if (node.Members != null)
{
// Shouldn't happen. node.Members != null with anonymous types
IEnumerable<MemberInfo> members = node.Members.Select(x => ConvertMember(x, to));
node = Expression.New(constructor2, node.Arguments, members);
}
else
{
node = Expression.New(constructor2, node.Arguments);
}
}
return base.VisitNew(node);
}
protected override Expression VisitMember(MemberExpression node)
{
Type to = null;
Expression expression = null;
if (node.Expression != null)
{
if (ParameterConversions.TryGetValue(node.Expression, out expression))
{
to = expression.Type;
}
}
if (to != null || (node.Expression == null && Conversions.TryGetValue(node.Member.DeclaringType, out to)))
{
MemberInfo member = ConvertMember(node.Member, to);
node = Expression.MakeMemberAccess(expression, member);
}
return base.VisitMember(node);
}
protected override Expression VisitParameter(ParameterExpression node)
{
Expression to;
if (ParameterConversions.TryGetValue(node, out to))
{
node = (ParameterExpression)to;
}
return base.VisitParameter(node);
}
// Conversion of method/property/field accessor (supported indexers)
private static MemberInfo ConvertMember(MemberInfo member, Type to)
{
switch (member.MemberType)
{
case MemberTypes.Field:
{
var field = (FieldInfo)member;
BindingFlags bf = (field.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) |
(field.IsStatic ? BindingFlags.Static : BindingFlags.Instance);
var field2 = to.GetField(member.Name, bf);
return field2;
}
case MemberTypes.Property:
{
var prop = (PropertyInfo)member;
var method = prop.GetMethod ?? prop.SetMethod;
BindingFlags bf = (method.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) |
(method.IsStatic ? BindingFlags.Static : BindingFlags.Instance);
var indexes = prop.GetIndexParameters();
var types = Array.ConvertAll(indexes, x => x.ParameterType);
var property2 = to.GetProperty(member.Name, bf, null, prop.PropertyType, types, null);
return property2;
}
case MemberTypes.Method:
{
var method = (MethodInfo)member;
BindingFlags bf = (method.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) |
(method.IsStatic ? BindingFlags.Static : BindingFlags.Instance);
var parameters = method.GetParameters();
var types = Array.ConvertAll(parameters, x => x.ParameterType);
var method2 = to.GetMethod(member.Name, bf, null, types, null);
return method2;
}
default:
throw new NotSupportedException(member.MemberType.ToString());
}
}
}
然后你这样使用:
Expression<Func<Class1a, Class1b, bool>> exp1 = (x, y) => x.Prop1;
var visitor = new TypeReplacer();
visitor.Conversions.Add(typeof(Class1a), typeof(Class2a));
visitor.Conversions.Add(typeof(Class1b), typeof(Class2b));
var result = (Expression<Func<Class2a, Class2b, bool>>)visitor.Visit(exp1);
请注意,此代码显示了在最复杂的情况下进行此转换会带来多大的痛苦...此代码不完整...有许多极端情况这里不涉及! (例如,这不受支持:x.SomeMethod(new ClassOriginal()) -> x.SomeMethod(new ClassConverted()
.
添加了对以下内容的支持:
x => x;
x => null;
(他们需要特殊处理)
我会将那些 "same properties" 提取到接口中,然后让 类 实现它...
如何映射
来自:Expression<Func<TEntity, bool>>
至:Expression<Func<TDbEntity, bool>>
where TEntity: class, new() and TDbEntity: class, new()
TEntity 来自域,TDbEntity 来自基础设施层,但具有相同的属性。
有可能吗?
对于相对简单的情况(我想在你的情况下表达式很简单)你可以使用 ExpressionVisitor
只有几个覆盖。例如:
public static class ExpressionExtensions {
public static Expression<Func<TTo, bool>> ReplaceParameter<TFrom, TTo>(this Expression<Func<TFrom, bool>> target) {
return (Expression<Func<TTo, bool>>) new WhereReplacerVisitor<TFrom, TTo>().Visit(target);
}
private class WhereReplacerVisitor<TFrom, TTo> : ExpressionVisitor {
private readonly ParameterExpression _parameter = Expression.Parameter(typeof(TTo), "c");
protected override Expression VisitLambda<T>(Expression<T> node) {
// replace parameter here
return Expression.Lambda(Visit(node.Body), _parameter);
}
protected override Expression VisitMember(MemberExpression node) {
// replace parameter member access with new type
if (node.Member.DeclaringType == typeof(TFrom) && node.Expression is ParameterExpression) {
return Expression.PropertyOrField(_parameter, node.Member.Name);
}
return base.VisitMember(node);
}
}
}
用法是:
Expression<Func<ErrorModel, bool>> where = (c) => c.Created <= DateTime.UtcNow && c.ErrorCode == "code";
var replaced = where.ReplaceParameter<ErrorModel, Error>();
我前段时间遇到过同样的问题,我找到的解决方案是将主要实体转换为目标实体。但只有当属性具有相同的名称时它才有效,否则你将有额外的工作来映射过滤器上的属性。
所以让我们开始编写代码,尝试这样做:
首先创建一个扩展class
public static class ExtensionToExpression { public static Expression<Func<TTo, bool>> Converter<TFrom, TTo>(this Expression<Func<TFrom, bool>> expression, TTo type) where TTo : TFrom { // here we get the expression parameter the x from (x) => .... var parameterName = expression.Parameters.First().Name; // create the new parameter from the correct type ParameterExpression parameter = Expression.Parameter(typeof(TTo), parameterName); // asigne to allow the visit from or visitor Expression body = new ConvertVisitor(parameter).Visit(expression.Body); // recreate the expression return Expression.Lambda<Func<TTo, bool>>(body, parameter); } }
现在创建访客 class
public class ConvertVisitor : ExpressionVisitor
{
private ParameterExpression Parameter;
public Visitante(ParameterExpression parameter)
{
Parameter = parameter;
}
protected override Expression VisitParameter(ParameterExpression item)
{
// we just check the parameter to return the new value for them
if(!item.Name.Equals(Parameter.Name))
return item;
return Parameter;
}
}
并使用: // 添加 using 以使用您的扩展方法
public void YouMethod(Expression<Func<TEntity, bool>> filter)
{
var whereExpression = filter.Convert(default(TDbEntity));
var result = yourContext.Where(whereExpression).ToArray();
// do something
}
希望对您有所帮助
正如 Evk
所写,您需要 ExpressionVisitor.
对于比 Evk 支持的表达式更复杂的表达式,例如多参数,使用构造函数和成员初始化列表 (new { Prop1 = true }
):
public class TypeReplacer : ExpressionVisitor
{
public readonly Dictionary<Type, Type> Conversions = new Dictionary<Type, Type>();
private readonly Dictionary<Expression, Expression> ParameterConversions = new Dictionary<Expression, Expression>();
protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)
{
Type to;
if (Conversions.TryGetValue(node.Member.DeclaringType, out to))
{
var member = ConvertMember(node.Member, to);
node = Expression.Bind(member, node.Expression);
}
return base.VisitMemberAssignment(node);
}
public override Expression Visit(Expression node)
{
if (node.NodeType == ExpressionType.Lambda)
{
var lambda = (LambdaExpression)node;
var parameters = lambda.Parameters.ToArray();
for (int i = 0; i < parameters.Length; i++)
{
ParameterExpression parameter = parameters[i];
Type to;
if (Conversions.TryGetValue(parameter.Type, out to))
{
var oldParameter = parameter;
parameter = Expression.Parameter(to, parameter.Name);
ParameterConversions.Add(oldParameter, parameter);
}
}
var body = base.Visit(lambda.Body);
for (int i = 0; i < parameters.Length; i++)
{
var parameter = (ParameterExpression)base.Visit(parameters[i]);
parameters[i] = parameter;
}
// Handling of the delegate type
var arguments = node.Type.GetGenericArguments();
{
Type to;
for (int i = 0; i < arguments.Length; i++)
{
if (Conversions.TryGetValue(arguments[i], out to))
{
arguments[i] = to;
}
}
}
var delegateType = node.Type.GetGenericTypeDefinition().MakeGenericType(arguments);
node = Expression.Lambda(delegateType, body, parameters);
return node;
}
return base.Visit(node);
}
protected override Expression VisitConstant(ConstantExpression node)
{
Type to;
if (Conversions.TryGetValue(node.Type, out to))
{
node = Expression.Constant(node.Value, to);
}
return base.VisitConstant(node);
}
protected override Expression VisitNew(NewExpression node)
{
Type to;
if (Conversions.TryGetValue(node.Type, out to))
{
var constructor = node.Constructor;
BindingFlags bf = (constructor.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) |
BindingFlags.Instance;
var parameters = constructor.GetParameters();
var types = Array.ConvertAll(parameters, x => x.ParameterType);
var constructor2 = to.GetConstructor(bf, null, types, null);
if (node.Members != null)
{
// Shouldn't happen. node.Members != null with anonymous types
IEnumerable<MemberInfo> members = node.Members.Select(x => ConvertMember(x, to));
node = Expression.New(constructor2, node.Arguments, members);
}
else
{
node = Expression.New(constructor2, node.Arguments);
}
}
return base.VisitNew(node);
}
protected override Expression VisitMember(MemberExpression node)
{
Type to = null;
Expression expression = null;
if (node.Expression != null)
{
if (ParameterConversions.TryGetValue(node.Expression, out expression))
{
to = expression.Type;
}
}
if (to != null || (node.Expression == null && Conversions.TryGetValue(node.Member.DeclaringType, out to)))
{
MemberInfo member = ConvertMember(node.Member, to);
node = Expression.MakeMemberAccess(expression, member);
}
return base.VisitMember(node);
}
protected override Expression VisitParameter(ParameterExpression node)
{
Expression to;
if (ParameterConversions.TryGetValue(node, out to))
{
node = (ParameterExpression)to;
}
return base.VisitParameter(node);
}
// Conversion of method/property/field accessor (supported indexers)
private static MemberInfo ConvertMember(MemberInfo member, Type to)
{
switch (member.MemberType)
{
case MemberTypes.Field:
{
var field = (FieldInfo)member;
BindingFlags bf = (field.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) |
(field.IsStatic ? BindingFlags.Static : BindingFlags.Instance);
var field2 = to.GetField(member.Name, bf);
return field2;
}
case MemberTypes.Property:
{
var prop = (PropertyInfo)member;
var method = prop.GetMethod ?? prop.SetMethod;
BindingFlags bf = (method.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) |
(method.IsStatic ? BindingFlags.Static : BindingFlags.Instance);
var indexes = prop.GetIndexParameters();
var types = Array.ConvertAll(indexes, x => x.ParameterType);
var property2 = to.GetProperty(member.Name, bf, null, prop.PropertyType, types, null);
return property2;
}
case MemberTypes.Method:
{
var method = (MethodInfo)member;
BindingFlags bf = (method.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic) |
(method.IsStatic ? BindingFlags.Static : BindingFlags.Instance);
var parameters = method.GetParameters();
var types = Array.ConvertAll(parameters, x => x.ParameterType);
var method2 = to.GetMethod(member.Name, bf, null, types, null);
return method2;
}
default:
throw new NotSupportedException(member.MemberType.ToString());
}
}
}
然后你这样使用:
Expression<Func<Class1a, Class1b, bool>> exp1 = (x, y) => x.Prop1;
var visitor = new TypeReplacer();
visitor.Conversions.Add(typeof(Class1a), typeof(Class2a));
visitor.Conversions.Add(typeof(Class1b), typeof(Class2b));
var result = (Expression<Func<Class2a, Class2b, bool>>)visitor.Visit(exp1);
请注意,此代码显示了在最复杂的情况下进行此转换会带来多大的痛苦...此代码不完整...有许多极端情况这里不涉及! (例如,这不受支持:x.SomeMethod(new ClassOriginal()) -> x.SomeMethod(new ClassConverted()
.
添加了对以下内容的支持:
x => x;
x => null;
(他们需要特殊处理)
我会将那些 "same properties" 提取到接口中,然后让 类 实现它...