Entity Framework Core - использовать дерево выражений с интерфейсом в качестве параметра - PullRequest
0 голосов
/ 11 февраля 2019

Я буду очень признателен за помощь в следующем сценарии.У меня есть следующие классы:

public class Product : IHasPrice
{
    public string Title { get; set; }
    public int Price { get; set; }
    public string CustomerId { get; set; }

}

public interface IHasPrice
{
    int Price { get; set; }
}

public class ProductProvider
{
    public ProductProvider()
    {
    }

    public IEnumerable<Product> GetByCustomer(string customerId, Expression<Func<IHasPrice, bool>> predicate = null)
    {
        using (var db = new ApplicationDbContext())
        {
            var queryable = db.Products.Where(p => p.CustomerId == customerId);
            if (predicate != null)
            {
                return queryable.Where(predicate).ToList();
            }
            else
            {
                return queryable.ToList();
            }
        }
    }
}

Я хочу разрешить использование ProductProvider таким образом, чтобы вы могли выбирать только по клиенту, но вы также можете фильтровать цену любым удобным для вас способом (и только по цене).Этот пример не работает, так как queryable.Where ожидает параметр с typeof Expression(Func(Product, bool)).Есть ли способ сделать это, или я должен получить данные в память до фильтрации цены?

1 Ответ

0 голосов
/ 11 февраля 2019

Поскольку интерфейс IQueryable<out T> равен ковариант , переданное лямбда-выражение можно использовать напрямую с методом Where:

var query = queryable.Where(predicate);

Единственная проблема заключается в том, что теперь типрезультат запроса IQueryable<IHasPrice>.Вы можете вернуть его к IQueryable<Product>, используя Queryable.Cast метод:

var query = db.Products.Where(p => p.CustomerId == customerId);
if (predicate != null)
    query = query.Where(predicate).Cast<Product>(); // <--
return query.ToList();

Протестировано и работает с последней версией EF Core 2.2 (может не работать в некоторых более ранних версиях).


Альтернативное решение - преобразовать Expression<Func<IHasPrice, bool>> в ожидаемый Expression<Func<Product, bool>>, "вызвав" его:

var query = db.Products.Where(p => p.CustomerId == customerId);
if (predicate != null)
{
    var parameter = Expression.Parameter(typeof(Product), "p");
    var body = Expression.Invoke(predicate, parameter);
    var newPredicate = Expression.Lambda<Func<Product, bool>>(body, parameter);
    query = query.Where(newPredicate);
}
return query.ToList();
...