У меня есть класс доступа к данным, который действует как посредник между логическими классами и базовым источником данных, который является взаимозаменяемым. Этот класс позволяет запрашивать источник данных с использованием lambdas, LINQ-style. Класс-агностик обеспечивает высокоуровневую функциональность, основанную на нескольких основных операциях (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>>
который представляет операцию c => c.Purchases.Contains(someProduct))
и решить, что свойство, на которое он ссылается, является c => c.Purchases
? Как мне это сделать? Что относительно лямбда, которая касается нескольких свойств?
Используйте 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);
}
}
}