Получить Expression <Func <T, object >> для каждого свойства, к которому обращается Expression <Func <T, U >>

1

У меня есть класс доступа к данным, который действует как посредник между логическими классами и базовым источником данных, который является взаимозаменяемым. Этот класс позволяет запрашивать источник данных с использованием 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? Как мне это сделать? Что относительно лямбда, которая касается нескольких свойств?

Теги:
linq
lambda
properties

1 ответ

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);
        }
    }
}
  • 0
    Круто, это выглядит многообещающе. Я проверю это ...
  • 0
    Если у нас есть метод, который вызывает find (), и мы передаем параметры, такие как Find (p1 => p1.City == "myCity" && p1.Name == "myName", p2 => p2.Street == "myStreet") , Теперь мы можем получить имя свойства и значения, такие как «myCity», «myName»?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню