Как изменить тип дерева выражений и правильно обновить вложенные выборки в нем?

Я использую Entity Framework 6.3 и Visual Studio 2017. Допустим, у меня есть следующие классы.

class Store
{
     //Some other fields 
     List<Toy> Toys = new List<Toy>();
     List<Item> SoldItems = new List<Item>();
}

class Toy
{
    //Some other fields
    List<Order> Orders = new List<Order>();
}

class Order
{
    //Some other fields
}

class Item 
{
    //Some other fields
}

Я знаю, что эти занятия не имеют особого смысла, но, надеюсь, их будет достаточно, чтобы выявить проблему.

У меня также есть метод, который выглядит следующим образом:

IEnumerable<TEntity> FindByAndInclude(Expression<Func<TEntity, bool>> predicate, 
               params Expression<Func<TEntity, object>>[] includeProperties)
{
        var query = includeProperties.Aggregate(_dbSet.AsQueryable(), (current, includeProperty) => current.Include(includeProperty));
        return query.Where(predicate).AsNoTracking().ToList(); 
}

Где _dbSet определен как DbSet , а TEntity является одним из классов, определенных в верхнем фрагменте. И в коде я называю это так:

Stores.FindByAndInclude(x => x.StoreName.Equals(_StoreName),
                t => t.toys.
                Select(o => o.Orders),
                i => i.SoldItems));

И это прекрасно работает. Проблема возникла, когда я попытался ввести новый набор классов, назовем их Store1, Toy1, Order1, Item1 с одинаковыми полями (поля имеют одинаковые имена, но соответствующие типы). Пример:

class Toy
{
    List<Order1> Orders = new List<Order1>();
} 

Эти классы содержат мои аннотации Entity Framework, поэтому я хочу использовать их для запроса и сохранения данных, а также хочу использовать классы, определенные в приведенном выше фрагменте кода клиента.

Поэтому теперь метод FindByInclude не будет работать, поскольку я передаю выражение >, а _dbSet определен как DbSet , где SEntity является одним из новых классов.

До сих пор мне удалось преобразовать предикат из Expression > в Expression >, изменив его, используя приведенный ниже фрагмент кода:

   class ExpressionFixer<T> : ExpressionVisitor
{
    ParameterExpression _parameter;

    public ExpressionFixer(ParameterExpression parameter)
    {
        _parameter = parameter;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return _parameter;
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Member.MemberType == System.Reflection.MemberTypes.Property)
        {
            MemberExpression memberExpression = null;
            var memberName = node.Member.Name;
            var otherMember = typeof(T).GetProperty(memberName);
            memberExpression = Expression.Property(Visit(node.Expression), otherMember);
            return memberExpression;
        }
        else
        {
            return base.VisitMember(node);
        }
    }
}

И я использую его в своем коде так:

var param = Expression.Parameter(typeof(SEntity), typeof(SEntity).ToString());
var result = new ExpressionFixer<SEntity>(param).Visit(source.Body);
Expression<Func<SEntity, bool>> expression = Expression.Lambda<Func<SEntity, bool>>(result, param);
_dbSet.Where(expression).ToList();

Опять же, это работает, но я не могу понять, как сделать то же самое для includeProperties. Код, который я нашел для моего метода FindByInclude, теперь выглядит следующим образом:

List<Expression<Func<SEntity, object>>> tempList = new List<Expression<Func<SEntity, object>>>();
        foreach(var property in includeProperties)
        {
            var param = Expression.Parameter(typeof(SEntity), typeof(SEntity).ToString());
            var result = new ExpressionFixer<SEntity>(param).Visit(source.Body);
            Expression<Func<SEntity, bool>> expression =Expression.Lambda<Func<SEntity, object>>(result, param);
            tempList.Add(expression);
        }

        Expression<Func<SEntity, object>>[] fixedIncludeProperties = tempList.ToArray();

        var query = fixedIncludeProperties.Aggregate(_dbSet.AsQueryable(), (current, includeProperty) => current.Include(includeProperty));
// Executing the query and mapping results back to TEntity type

Но этот код вызывает исключение System.ArgumentException:

'ParameterExpression типа'Toy1'нельзя использовать для параметра делегата типа'Order''

когда я пытаюсь запустить его для:

Stores.FindByAndInclude(x => x.StoreName.Equals(_StoreName),
            t => t.Toys.
            Select(o => o.Orders);

Я понимаю, что мне нужно каким-то образом обновить типы заказов, но я просто не знаю, как, потому что я не знаю, как хорошо ориентироваться в выражениях. Итак, наконец, мой вопрос, как это выполнимо? (А поскольку я впервые пытаюсь что-то подобное), что может быть лучше? Суть задачи состоит в том, чтобы иметь две версии классов, поэтому менять их нельзя.

0 ответов

Другие вопросы по тегам