首页 > 解决方案 > Entity Framework Core - 使用接口作为参数的表达式树

问题描述

我将非常感谢以下情况的一些帮助。我有以下课程:

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))。有没有办法做到这一点,或者我必须在过滤价格之前将数据提取到内存中?

标签: c#entity-frameworkentity-framework-coreexpression-trees

解决方案


由于IQueryable<out T>interface 是covariant,因此传递的 lambda 表达式可以直接与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();

推荐阅读