【发布时间】:2016-11-24 18:04:19
【问题描述】:
感谢previous question 上的一些答案,我可以成功替换 lambda 表达式中的简单参数类型,但我不知道如何将传入 lambda 中的参数替换为嵌套参数。
考虑以下对象:
public class DtoColour {
public DtoColour(string name)
{
Name = name;
}
public string Name { get; set; }
public ICollection<DtoFavouriteColour> FavouriteColours { get; set; }
}
public class DtoPerson
{
public DtoPerson(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
FavouriteColours = new Collection<DtoFavouriteColour>();
}
public string FirstName { get; private set; }
public string LastName { get; private set; }
public ICollection<DtoFavouriteColour> FavouriteColours { get; set; }
}
public class DtoFavouriteColour
{
public DtoColour Colour { get; set; }
public DtoPerson Person { get; set; }
}
public class DomainColour {
public DomainColour(string name)
{
Name = name;
}
public string Name { get; set; }
public ICollection<DomainPerson> People { get; set; }
}
public class DomainPerson {
public DomainPerson(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
Colours = new Collection<DomainColour>();
}
public string FirstName { get; private set; }
public string LastName { get; private set; }
public ICollection<DomainColour> Colours { get; set; }
}
和一个存储库:
public class ColourRepository {
private IList<DtoColour> Colours { get; set; }
public ColourRepository()
{
var favColours = new Collection<DtoFavouriteColour>
{
new DtoFavouriteColour() { Person = new DtoPerson("Peter", "Parker") },
new DtoFavouriteColour() { Person = new DtoPerson("John", "Smith") },
new DtoFavouriteColour() { Person = new DtoPerson("Joe", "Blogs") }
};
Colours = new List<DtoColour>
{
new DtoColour("Red") { FavouriteColours = favColours },
new DtoColour("Blue"),
new DtoColour("Yellow")
};
}
public IEnumerable<DomainColour> GetWhere(Expression<Func<DomainColour, bool>> predicate)
{
var coonvertedPred = MyExpressionVisitor.Convert(predicate);
return Colours.Where(coonvertedPred).Select(c => new DomainColour(c.Name)).ToList();
}
}
最后是一个表达式访问者,它应该将谓词转换为 Dto 模型的正确谓词
public class MyExpressionVisitor : ExpressionVisitor
{
private ReadOnlyCollection<ParameterExpression> _parameters;
public static Func<DtoColour, bool> Convert<T>(Expression<T> root)
{
var visitor = new MyExpressionVisitor();
var expression = (Expression<Func<DtoColour, bool>>)visitor.Visit(root);
return expression.Compile();
}
protected override Expression VisitParameter(ParameterExpression node)
{
var param = _parameters?.FirstOrDefault(p => p.Name == node.Name);
if (param != null)
{
return param;
}
if(node.Type == typeof(DomainColour))
{
return Expression.Parameter(typeof(DtoColour), node.Name);
}
if (node.Type == typeof(DomainPerson))
{
return Expression.Parameter(typeof(DtoFavouriteColour), node.Name);
}
return 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)
{
var exp = Visit(node.Expression);
if (node.Member.DeclaringType == typeof(DomainColour))
{
if (node.Type == typeof(ICollection<DomainPerson>))
{
return Expression.MakeMemberAccess(exp, typeof(DtoColour).GetProperty("FavouriteColours"));
}
return Expression.MakeMemberAccess(exp, typeof(DtoColour).GetProperty(node.Member.Name));
}
if (node.Member.DeclaringType == typeof(DomainPerson))
{
var nested = Expression.MakeMemberAccess(exp, typeof(DtoFavouriteColour).GetProperty("Person"));
return Expression.MakeMemberAccess(nested, typeof(DtoPerson).GetProperty(node.Member.Name));
}
return base.VisitMember(node);
}
}
目前我得到以下异常
[System.ArgumentException: 类型表达式 'System.Collections.Generic.ICollection
1[ExpressionVisitorTests.DtoFavouriteColour]' cannot be used for parameter of type 'System.Collections.Generic.IEnumerable1[ExpressionVisitorTests.DomainPerson]' 方法的布尔值 Any[DomainPerson](System.Collections.Generic.IEnumerable1[ExpressionVisitorTests.DomainPerson], System.Func2[ExpressionVisitorTests.DomainPerson,System.Boolean])']
这里有一个dotnetfiddle,它不起作用。
提前感谢您的帮助。
【问题讨论】:
-
DtoColour和DtoPerson都有一个名为FavoriteColours的属性,这有点令人困惑。这些集合应该同步吗?无论如何,对象具有实际上不是对象属性的属性是没有意义的。喜欢特定颜色的人的集合不是该颜色的属性。一个人喜欢的颜色的集合可能是一个人的属性,但我也会删除它,并且只有一个所有“人喜欢颜色”关系的公共集合。 -
dto 对象代表数据库的模式,最喜欢的颜色 dto 是为域模型提供关系的链接表。我知道这是一个奇怪的例子,但不要太担心。
标签: c# lambda expression visitor-pattern