我有一個應用程序爲客戶保存文檔信息。我正在嘗試添加功能以允許用戶上傳PDF並將其保存到磁盤。我使用推薦的代碼here和here。在ASP.NET Core中異步保存文件,導致DbContext出現異常
當代碼正常工作時,一切看起來不錯 - 文檔實體保存到數據庫,上傳的文件保存到磁盤。然而,我正在拋出零星的例外 - 例如System.ObjectDisposedException ("Cannot access a disposed object")
,有時DbUpdate異常說引用爲空等,我可以重現該問題,但不是每次100%,而不是每次都有相同的異常。
我懷疑這是由於在保存文件時使用CopyToAsync
造成的。我對異步代碼不太熟練,而且我可能會做出明顯錯誤的事情。如果我改變方法來改爲使用同步CopyTo
,它似乎解決了這個問題。我試圖理解爲什麼會發生這種情況。
我使用AutoFac注入我的repo和DbContext。以下是控制器調用的服務方法的代碼:
public async Task<int> SaveDocument(DocumentDetailDto dto, string user, string webRoot)
{
documentToSave = new Document();
// Code not shown that maps dto to new document and adds it to context
if (dto.FileUpload != null && dto.FileUpload.Length > 0)
{
var fileName = $"{dto.CustomerId}-{dto.DocumentTypeId}-{DateTime.Now.Ticks}.pdf";
var filePath = Path.Combine(webRoot, "uploads");
using (var fileStream = new FileStream(Path.Combine(filePath, fileName), FileMode.Create))
{
await dto.FileUpload.CopyToAsync(fileStream);
}
}
_repo.Save(user);
return documentToSave.Id;
}
您是否發現此設置有任何明顯錯誤?在調用保存上下文之前,在使用FileStream的異步時需要做什麼特殊處理?我仍然在嘗試調試這些錯誤,但它幾乎看起來像是打開的FileStream和試圖寫入數據庫的DbContext之間的某種衝突。任何想法嘗試是最受歡迎的!
編輯以添加一些額外的代碼:
這裏是我如何Startup.cs註冊的DbContext:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Add DbContext
var connection = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<DocContext>(
options => options.UseSqlServer(connection, b => b.MigrationsAssembly("InfrastructureLayer")));
// Add repository
services.AddScoped<IRepository, EntityFrameworkRepository<DocContext>>();
services.AddTransient<IResolveUserService, ResolveUserService>();
// Autofac setup
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule<ServiceLayer.AutofacModule>();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
這裏是Save
方法回購:
public virtual void Save(string user = "")
{
var modifiedEntries = Context.ChangeTracker.Entries<IEntity>()
.Where(x => x.State == EntityState.Modified)
.Select(x => x.Entity)
.ToList();
foreach (var entity in modifiedEntries)
{
entity.ModifiedDate = DateTime.UtcNow;
entity.ModifiedBy = user;
}
var newEntries = Context.ChangeTracker.Entries<IEntity>()
.Where(x => x.State == EntityState.Added)
.Select(x => x.Entity)
.ToList();
foreach (var entity in newEntries)
{
entity.CreatedDate = DateTime.UtcNow;
entity.CreatedBy = user;
}
Context.SaveChanges();
}
這裏是如何從控制器調用SaveDocument
方法:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Save(DocumentDetailDto dto, [FromServices]IResolveUserService userService, [FromServices]IHostingEnvironment environment)
{
_service.SaveDocument(dto, userService.GetUser(), environment.WebRootPath);
return RedirectToAction("Detail", "Customers", new { id = dto.CustomerId });
}
謝謝!
沒有看到你的其他數據庫相關的代碼很難說。可能你正在'DbContext'上使用'(...)',或者你把'DbContext'註冊爲Singleton – Tseng
這可能就是Tseng所說的。你也可以顯示你如何調用'SaveDocument()'方法嗎?如果請求在你到達'_repo.Save(user)'之前完成,你的'DbContext'將會離開它的生命週期並被丟棄。確保沒有發生。 –
@Tseng - 謝謝,我添加了一些代碼,也許這有助於診斷...可以使用「AddScoped」註冊回購時導致這種情況? – Jim