在我们的Web应用程序中,需要从各种表中获取数据是非常常见的。今天你可能会发现为了一个请求而串行执行5或6个数据库查询。由于这些查询彼此之间并不依赖数据,因此它们非常适合并行执行。问题是众所周知的DbConcurrencyException
,当多个查询针对同一上下文执行时会抛出该异常。
通常我们每个请求使用一个单独的上下文,然后拥有一个存储库类,以便可以在不同的项目中重复使用查询。最后,在控制器被释放时,我们会销毁上下文。
以下是一个使用并行性的示例,但仍存在问题!
var fileTask = new Repository().GetFile(id);
var filesTask = new Repository().GetAllFiles();
var productsTask = AllProducts();
var versionsTask = new Repository().GetVersions();
var termsTask = new Repository().GetTerms();
await Task.WhenAll(fileTask, filesTask, productsTask, versionsTask, termsTask);
每个仓库都在内部创建自己的上下文,但目前它们没有被处理。这是一个问题。我知道我可以在创建每个仓库时调用Dispose,但这会很快使代码变得混乱。我可以为每个查询创建一个包装函数,该函数使用自己的上下文,但这感觉很凌乱,对于解决问题来说也不是长期的好方法。
如何解决这个问题?我希望客户端/消费者在执行多个查询时无需担心处理每个仓库/上下文。
我现在唯一的想法是遵循类似工厂模式的方法,除了我的工厂将跟踪它创建的所有对象。然后,一旦知道我的查询已完成,就可以处理工厂,并且工厂可以在内部处理每个仓库/上下文。
我很惊讶在并行和Entity Framework方面看到如此少的讨论,所以希望社区能提出更多想法。
编辑:
以下是我们的存储库的简单示例:
public class Repository : IDisposable {
public Repository() {
this.context = new Context();
this.context.Configuration.LazyLoadingEnabled = false;
}
public async Task<File> GetFile(int id) {
return await this.context.Files.FirstOrDefaultAsync(f => f.Id == id);
}
private bool disposed = false;
protected virtual void Dispose(bool disposing) {
if (!this.disposed) {
if (disposing) {
context.Dispose();
}
}
this.disposed = true;
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
}
正如您所见,每个存储库都有自己的上下文。这意味着每个存储库都需要被处理。就我上面给出的示例而言,这意味着我需要调用 Dispose()
函数 4 次。
对于这个问题,我的想法是采用工厂模式,具体如下:
public class RepositoryFactory : IDisposable {
private List<IRepository> repositories;
public RepositoryFactory() {
this.repositories = new List<IRepository>();
}
public IRepository CreateRepository() {
var repo = new Repository();
this.repositories.Add(repo);
return repo;
}
#region Dispose
private bool disposed = false;
protected virtual void Dispose(bool disposing) {
if (!this.disposed) {
if (disposing) {
foreach (var repo in repositories) {
repo.Dispose();
}
}
}
this.disposed = true;
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
这个工厂将负责创建我的仓库实例,同时它还将跟踪它创建的所有实例。一旦这个单一的工厂类被处理掉,它将内部负责处理每个已创建的仓库的处理。
IDisposable
接口,我希望能够开发一种方法来消除可能发生的未知情况。 - Justin Helgerson