2017-07-02 38 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