我有一个应用程序为客户保存文档信息。我正在尝试添加功能以允许用户上传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