我有一个正在使用EF Core 3.0的应用程序(目前预览8)。有一个数据集很少更改,但查询非常频繁。实际执行数据操作的服务是通过一个非常简单的界面隔离的。
interface IDataProvider
{
ValueTask<IQueryable<DataRow>> GetDataAsync();
}
到目前为止,只有一个基于实际SQL数据库的数据提供程序实现。因此,如果我们有一个DbSet<DataRow> Data
的DbContext:
class LiveDataProvider : IDataProvider
{
private readonly DataDbContext _dbContext;
public LiveDataProvider(DataDbContext dbContext)
{
_dbContext = dbContext;
}
public ValueTask<IQueryable<DataRow>> GetDataAsync() =>
new ValueTask<IQueryable<DataRow>>(_dbContext.Data.AsNoTracking());
}
现在,我负责向该应用程序添加缓存。因此,我们的想法是将整个Data
集存储在内存中,因为在数据库上进行检索是相当昂贵的查询,但是数据量仅为几百MB。由于很少进行更新,因此绝大多数请求都会到达缓存。有了这个想法,实现似乎就很简单了:
class InMemoryDataCache : IDataProvider
{
private readonly IPicksDbContext _picksDbContext;
private readonly IMemoryCache _memoryCache;
public async ValueTask<IQueryable<DataRow>> GetDataAsync()
{
var entryKey = GetEntryKey(); // Implementation is irrelevant.
if (!_memoryCache.TryGetValue(entryKey, out IQueryable<DataRow> data))
{
data = (await _dbContext.Data.AsNoTracking().ToListAsync()).AsQueryable();
_memoryCache.Set(entryKey, data);
}
return data;
}
InMemoryDataCache(DataDbContext dbContext, IMemoryCache memoryCache)
{
_dbContext = dbContext;
_memoryCache = memoryCache;
}
}
听起来不错,没用-服务实现广泛使用IQueryable
上的异步操作,因此ToListAsync()
,AnyAsync()
等。在已缓存的{{1 }}将导致EF Core抛出List
和消息InvalidOperationException
该错误非常熟悉,因为对服务进行单元测试也引发了同样的事情。在那里,我模拟了Only sources that implement IAsyncEnumerable can be used for Entity Framework asynchronous operations.
的实现(对MockQueryable的喊叫),所以在这里我可以做同样的事情。
或者,我可以使用{p>的实现为IAsyncQueryProvider
,ToListAsync
等编写自己的扩展方法
AnyAsync
对于如此简单的事情,两者都需要大量的工作和测试。有没有更强大的解决方案?还是我首先将服务与EntityFrameworkQueryableExtensions绑定在一起而陷入困境,而我无法透明地使用内存提供程序。