我將非常感謝以下方案的一些幫助。我有以下課程:
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))
參數後才起作用。有沒有辦法做到這一點,或者我必須在過濾價格之前將數據提取到內存中?
由於IQueryable<out T>
接口是協變的 ,傳遞的lambda表達式可以直接與Where
方法一起使用:
var query = queryable.Where(predicate);
唯一的問題是,現在結果查詢的類型是IQueryable<IHasPrice>
。您可以使用Queryable.Cast
方法將其轉回IQueryable<Product>
:
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();