我正在寻找实现缓存,以便检查是否已经执行了特定查询,如果已经执行,则使用内存缓存返回数据。我可以利用EntityFramework通过DBSet的Local属性公开的内存视图,而不是实现缓存本身。
因此,我想将整个查询传递给一个方法,该方法将针对数据库和/或内存缓存运行它。但是,我让我的Expression / Func / IQueryable混淆了,并且无法看到如何传递查询/表达式:
所以基类会是这样的(这显然还没有......):
public abstract class ServiceBase<TEntity> where TEntity : class
{
Entities _context; //instantiated in the constructor
protected List<TEntity> GetData<TEntity>(Expression<Func<TEntity, TEntity>> query)
{
if (!HasQueryExecuted(query))
{
_context.Set<TEntity>().Select(query).Load();
AddQueryToExecutedList(query);
}
return _context.Set<TEntity>().Local.AsQueryable().Select(query).ToList();
}
}
我会有多个类来定义他们的查询:
public class CustomersService : ServiceBase<Customer>
{
public List<Customer> GetCustomersWithOrders()
{
return GetData(c => c.Where(c => c.OrderCount > 0));
}
public List<Customer> GetLargestCustomersByOrder(int TopCount)
{
return GetData(c => c.OrderBy(c=>c.OrderCount).Take(TopCount));
}
}
public class ProductsService : ServiceBase<Product>
{
public List<Customer> GetAllProducts()
{
return GetData(p => p);
}
public List<Customer> GetMostOrderedProducts(int minimumOrders)
{
return GetData(p => p.Where(p=> p.OrderCount > minimumOrders)
.OrderByDescending(p=>p.OrderCount));
}
public List<Customer> GetAllProducts()
{
return GetData(p => p);
}
}
这些只是人为的例子,但重点是查询可以变化,不仅限于where子句,而是利用IQueryable上的所有标准扩展方法来查询底层数据。
首先,这可能吗? GetData方法可以定义一个可以接受任何查询的参数吗?在上面的例子中,我声明query
参数接受与IQueryable扩展Select方法相同的类型,但是当我尝试使用简单的Where子句或OrderBy子句调用它时,我会遇到各种编译错误,例如as:
无法隐式转换类型&#39; bool&#39;到&#39; Entities.Customer
或
无法隐式转换类型&#39; System.Linq.IOrderedEnumerable&#39;到&#39; Entities.Customer&#39;
然而,如果我直接针对上下文运行相同的查询,它编译得很好。那么我做错了什么?
答案 0 :(得分:0)
您的问题是Expression<Func<TEntity, TEntity>> query
简单地说,你的表达式是一个函数类型,它采用TEntity
并返回TEntity
。由于您的课程使用Entities.Customer
键入,因此预计会有一个Entities.Customer
的函数并返回Entities.Customer
。
如果您查看服务类p => p
将正常工作,但p => p.Where()
不会,因为Where
会返回IEnumerable。
我认为你应该采取不同的方法并利用延迟加载。
public IQueryable<TEntity> GetData<TEntity>() where TEntity : class
{
return _context.Set<TEntity>();
}
public IQueryable<Customer> GetAllProducts()
{
return GetData();
}
public IQueryable<Customer> GetMostOrderedProducts(int minimumOrders)
{
return GetData().Where(p => p.OrderCount > minimumOrders)
.OrderByDescending(p=>p.OrderCount));
}
您不需要将查询构建到GetData
,因为查询可以在任何时候构建,直到它被评估为止。如果你真的想要你可以从这里返回List<T>
,但你不应该这样做。
否则,这应该可以解决当前代码的问题。
protected List<TEntity> GetData<TEntity>(Func<IEnumerable<TEntity>,
IEnumerable<TEntity>> query) where TEntity : class
{
if (!HasQueryExecuted(query))
{
AddQueryToExecutedList(query);
return query(_context.Set<TEntity>()).ToList();
}
return query(_context.Set<TEntity>().Local).ToList();
}