替换 lambda 表达式中的参数类型
Replace parameter type in lambda expression
我正在尝试将 lambda 表达式中的参数类型从一种类型替换为另一种类型。
我在 Whosebug 上找到了其他答案,即 this one 但我没有找到它们。
想象一下,您有一个域对象和一个可以从中检索域对象的存储库。
但是存储库必须处理自己的数据传输对象,然后映射和 return 域对象:
ColourDto.cs
public class DtoColour {
public DtoColour(string name)
{
Name = name;
}
public string Name { get; set; }
}
DomainColour.cs
public class DomainColour {
public DomainColour(string name)
{
Name = name;
}
public string Name { get; set; }
}
Repository.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();
}
}
如您所见,这将不起作用,因为谓词是针对域模型的,而存储库中的集合是数据传输对象的集合。
我曾尝试使用 ExpressionVisitor
来执行此操作,但无法弄清楚如何在不抛出异常的情况下仅更改 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 : Property 'System.String Name' is not
defined for type 'ColourDto'
希望有人能提供帮助。
编辑:这是一个 dotnetfiddle
还是不行。
编辑:这是一个有效的 dotnetfiddle
属性是为每种类型单独定义的。
发生该错误是因为您无法从 ColourDto
.
类型的值中获取由 DomainColour
定义的 属性 的值
您需要访问每个使用参数的 MemberExpression
和 return 使用新类型 属性 的新 MemberExpression
。
你需要做一些事情才能让它起作用:
- 在
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);
}
}
我正在尝试将 lambda 表达式中的参数类型从一种类型替换为另一种类型。
我在 Whosebug 上找到了其他答案,即 this one 但我没有找到它们。
想象一下,您有一个域对象和一个可以从中检索域对象的存储库。
但是存储库必须处理自己的数据传输对象,然后映射和 return 域对象:
ColourDto.cs
public class DtoColour {
public DtoColour(string name)
{
Name = name;
}
public string Name { get; set; }
}
DomainColour.cs
public class DomainColour {
public DomainColour(string name)
{
Name = name;
}
public string Name { get; set; }
}
Repository.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();
}
}
如您所见,这将不起作用,因为谓词是针对域模型的,而存储库中的集合是数据传输对象的集合。
我曾尝试使用 ExpressionVisitor
来执行此操作,但无法弄清楚如何在不抛出异常的情况下仅更改 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 : Property 'System.String Name' is not defined for type 'ColourDto'
希望有人能提供帮助。
编辑:这是一个 dotnetfiddle
还是不行。
编辑:这是一个有效的 dotnetfiddle
属性是为每种类型单独定义的。
发生该错误是因为您无法从 ColourDto
.
DomainColour
定义的 属性 的值
您需要访问每个使用参数的 MemberExpression
和 return 使用新类型 属性 的新 MemberExpression
。
你需要做一些事情才能让它起作用:
- 在
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);
}
}