2017-07-02 84 views
1

我有一个应用程序为客户保存文档信息。我正在尝试添加功能以允许用户上传PDF并将其保存到磁盘。我使用推荐的代码herehere在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 }); 
} 

谢谢!

+0

没有看到你的其他数据库相关的代码很难说。可能你正在'DbContext'上使用'(...)',或者你把'DbContext'注册为Singleton – Tseng

+1

这可能就是Tseng所说的。你也可以显示你如何调用'SaveDocument()'方法吗?如果请求在你到达'_repo.Save(user)'之前完成,你的'DbContext'将会离开它的生命周期并被丢弃。确保没有发生。 –

+0

@Tseng - 谢谢,我添加了一些代码,也许这有助于诊断...可以使用“AddScoped”注册回购时导致这种情况? – Jim

回答

3

您应该等待_service.SaveDocument完成。目前你开始它,并不等待其结果。相反,您向用户发送成功的响应,处理请求,使用的资源可能随时处理。

改变你的控制器动作:

public async Task<IActionResult> Save(... 
{ 
    await _service.SaveDocument(... 

    return RedirectToAction(... 
} 
+0

这很有道理。我并没有想到我需要追溯到SaveDocument的初始调用 - 感谢您的帮助,最终我会得到这个异步的东西! – Jim

相关问题