10

我正在尝试将 lambda 表达式中的参数类型从一种类型替换为另一种类型。

我在 stackoverflow 上找到了其他答案,即这个答案,但我没有运气。

想象一下,您有一个域对象和一个可以从中检索域对象的存储库。

但是存储库必须处理自己的数据传输对象,然后映射并返回域对象:

ColorDto.cs

public class DtoColour {

    public DtoColour(string name)
    {
        Name = name;
    }

    public string Name { get; set; }
}

域颜色.cs

public class DomainColour {

    public DomainColour(string name)
    {
        Name = name;
    }

    public string Name { get; set; }
}

存储库.cs

public class ColourRepository {
    ...
    public IEnumerable<DomainColour> GetWhere(Expression<Func<DomainColour, bool>> predicate)
    {
        // Context.Colours is of type ColourDto
        return Context.Colours.Where(predicate).Map().ToList();
    }
}

如您所见,这将不起作用,因为谓词用于域模型,并且存储库中的 Collection 是数据传输对象的集合。

我曾尝试使用 anExpressionVisitor来执行此操作,但无法弄清楚如何在ParameterExpression不引发异常的情况下更改类型,例如:

测试场景

public class ColourRepository {
    ...
    public IEnumerable<DomainColour> GetWhere(Expression<Func<DomainColour, bool>> predicate)
    {
        var visitor = new MyExpressionVisitor();
        var newPredicate = visitor.Visit(predicate) as Expression<Func<ColourDto, bool>>;
        return Context.Colours.Where(newPredicate.Complie()).Map().ToList();
    }
}


public class MyExpressionVisitor : ExpressionVisitor
{
    protected override Expression VisitParameter(ParameterExpression node)
    {
        return Expression.Parameter(typeof(ColourDto), node.Name);
    }
}

最后是例外:

System.ArgumentException:没有为类型“ColourDto”定义属性“System.String Name”

希望有人可以提供帮助。

编辑:这是一个dotnetfiddle

还是不行。

编辑:这是一个工作dotnetfiddle

谢谢伊莱·阿贝尔

4

2 回答 2

13

你需要做一些事情才能让它工作:

  • Expression.Lambda在它们出现在正文中的 和任何地方替换参数实例- 并为两者使用相同的实例。
  • 更改 lambda 的委托类型。
  • 替换属性表达式。

这是代码,添加了泛型:

public static Func<TTarget, bool> Convert<TSource, TTarget>(Expression<Func<TSource, bool>> root)
{
    var visitor = new ParameterTypeVisitor<TSource, TTarget>();
    var expression = (Expression<Func<TTarget, bool>>)visitor.Visit(root);
    return expression.Compile();
}

public class ParameterTypeVisitor<TSource, TTarget> : ExpressionVisitor
{
    private ReadOnlyCollection<ParameterExpression> _parameters;

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return _parameters?.FirstOrDefault(p => p.Name == node.Name) ?? 
            (node.Type == typeof(TSource) ? Expression.Parameter(typeof(TTarget), node.Name) : node);
    }

    protected override Expression VisitLambda<T>(Expression<T> node)
    {
        _parameters = VisitAndConvert<ParameterExpression>(node.Parameters, "VisitLambda");
        return Expression.Lambda(Visit(node.Body), _parameters);
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Member.DeclaringType == typeof(TSource))
        {
            return Expression.Property(Visit(node.Expression), node.Member.Name);
        }
        return base.VisitMember(node);
    }
}
于 2016-07-13T07:47:10.540 回答
1

每种类型的属性都是单独定义的。

发生该错误是因为您无法DomainColour从 type 的值中获取由定义的属性的值ColourDto

您需要访问每个MemberExpression使用该参数的参数,并MemberExpression从新类型返回一个使用该属性的新参数。

于 2016-07-11T21:41:19.117 回答