Apprezzerò molto l'aiuto con il seguente scenario. Ho le seguenti classi:
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();
}
}
}
}
Voglio abilitare l'utilizzo di ProductProvider
in un modo in cui è possibile selezionare solo per cliente, ma è anche possibile filtrare il prezzo in qualsiasi modo (e solo sul prezzo). Questo esempio non funziona in quanto queryable.Where
aspetta un parametro con typeof Expression(Func(Product, bool))
. C'è un modo per farlo o devo recuperare i dati in memoria prima del filtraggio del prezzo?
Poiché l' IQueryable<out T>
è covariante , l'espressione lambda passata può essere utilizzata direttamente con il metodo Where
:
var query = queryable.Where(predicate);
L'unico problema è che ora il tipo di query dei risultati è IQueryable<IHasPrice>
. Puoi tornare a IQueryable<Product>
utilizzando il metodo Queryable.Cast
:
var query = db.Products.Where(p => p.CustomerId == customerId);
if (predicate != null)
query = query.Where(predicate).Cast<Product>(); // <--
return query.ToList();
Testato e funzionante con l'ultimo EF Core 2.2 (potrebbe non riuscire in alcune versioni precedenti).
Una soluzione alternativa è convertire l' Expression<Func<IHasPrice, bool>>
attesa Expression<Func<Product, bool>>
con "invocandolo":
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();