我有一个数据访问类,它充当逻辑类和底层数据源之间的中介,可以互换。此类允许您使用lambdas,LINQ样式查询数据源。与源无关的类提供高级功能,由几个基本操作(Add,GetAll,Update,Delete,Commit)提供支持,这些操作由小型适配器类实现,每个源类型一个(SQL,SQlite,XML序列化器,WCF客户端) ,REST客户端,等等。
我的问题是,一些关系数据源(特别是SQLite)在我需要时不够智能加载关系属性;我必须明确要求将它们包括在内。这适用于我的Get
方法;我可以传递一个params
数组表达式来加载我需要的任何东西。但是,对于.Any()
,这感觉有点奇怪 - 如果我在询问是否有Customer
列表包含某个项目的Purchases
条记录,那么我不应该告诉它它加载Purchases
列表;这似乎应该能够解决这个问题。
所以我的Any()
方法需要Expression<Func<T, bool>>
,其中T
显然是我正在操作的类型。在上面的例子中,它将被使用如下:
using (var db = _dataAccessProvider.NewTransaction())
{
return db.Any<Customer>(c => c.Purchases.Contains(someProduct));
}
是否可以使用代表操作Expression<Func<Customer, bool>>
的{{1}}并确定它所引用的属性为c => c.Purchases.Contains(someProduct))
?我该怎么做呢?一个涉及多个属性的lambda怎么样?
答案 0 :(得分:2)
使用ExpressionVisitor
查找引用所需对象属性的所有MemberExpression
表达式。
快速举例:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
class Program
{
sealed class ReferencedPropertyFinder : ExpressionVisitor
{
private readonly Type _ownerType;
private readonly List<PropertyInfo> _properties = new List<PropertyInfo>();
public ReferencedPropertyFinder(Type ownerType)
{
_ownerType = ownerType;
}
public IReadOnlyList<PropertyInfo> Properties
{
get { return _properties; }
}
protected override Expression VisitMember(MemberExpression node)
{
var propertyInfo = node.Member as PropertyInfo;
if(propertyInfo != null && _ownerType.IsAssignableFrom(propertyInfo.DeclaringType))
{
// probably more filtering required
_properties.Add(propertyInfo);
}
return base.VisitMember(node);
}
}
private static IReadOnlyList<PropertyInfo> GetReferencedProperties<T, U>(Expression<Func<T, U>> expression)
{
var v = new ReferencedPropertyFinder(typeof(T));
v.Visit(expression);
return v.Properties;
}
sealed class TestEntity
{
public int PropertyA { get; set; }
public int PropertyB { get; set; }
public int PropertyC { get; set; }
}
static void Main(string[] args)
{
Expression<Func<TestEntity, int>> expression =
e => e.PropertyA + e.PropertyB;
foreach(var property in GetReferencedProperties(expression))
{
Console.WriteLine(property.Name);
}
}
}