ASP .NET Entity Framework Core 无法访问已释放的对象
ASP .NET Entity Framework Core Cannot access a disposed object
为了进入 .NET Core,我创建了一个 WEB API,它接受文件上传,然后将文件中的事务保存到数据库中 table。我正在使用带有 Entity Framework Core 的 .NET Core 2。我使用 here 中的示例创建了上下文。
我的问题是当它试图保存到我存储库中的上下文对象时出现错误 "System.ObjectDisposedException Cannot access a disposed object"。这是一个简单的堆栈,所以我希望有人能帮助我。
我的容器设置如下所示:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<SyncFinContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<ITransactionProcessor, TransactionProcessor>();
services.AddScoped<ITransactionRepository, TransactionRepository>();
}
我的 DBInitializer 也是从上面的 link 得到的:
public static class DbInitializer
{
public static async Task Initialize(SyncFinContext context)
{
await context.Database.EnsureCreatedAsync();
// Look for any students.
if (context.Transactions.Any())
{
return; // DB has been seeded
}
var ts = new Transaction[]
{
// insert test data here
};
await context.SaveChangesAsync();
}
}
我的数据库上下文:
public class SyncFinContext : DbContext
{
public SyncFinContext(DbContextOptions<SyncFinContext> options) : base(options)
{
}
public DbSet<Transaction> Transactions { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Transaction>().ToTable("Transactions");
}
}
我的控制器看起来像这样:
[Produces("application/json")]
public class TransactionController : Controller
{
ITransactionRepository _transactionRepository { get; set; }
ITransactionProcessor _transactionProcessor { get; set; }
public TransactionController(ITransactionRepository m, ITransactionProcessor p) : base()
{
_transactionRepository = m;
_transactionProcessor = p;
}
// POST: transaction/import
[HttpPost]
public async void Import(List<IFormFile> files)
{
if (files == null || files.Count == 0)
{
throw new FileNotFoundException("No file was received.");
}
// copy file to temp location so that it can be processed
var filepath = Path.GetTempFileName();
using (var stream = new FileStream(filepath, FileMode.Create))
{
await files[0].CopyToAsync(stream);
}
ImportTransactionRequest input = new ImportTransactionRequest
{
FilePath = filepath
};
var transactions = await _transactionProcessor.ReadDocument(filepath);
await _transactionRepository.AddBulk(transactions);
}
}
我的存储库如下所示:
public class TransactionRepository : ITransactionRepository
{
// TODO: move the context
private SyncFinContext _context;
public TransactionRepository(SyncFinContext context)
{
_context = context;
}
public async Task AddBulk(List<Transaction> transactions)
{
foreach(var t in transactions)
{
await _context.Transactions.AddAsync(t);
}
_context.SaveChanges();
}
}
为了完全透明,交易处理器仅从 csv 中获取行列表:
public async Task<List<Transaction>> ReadDocument(string filepath)
{
try
{
var ret = new List<Transaction>();
var lines = await File.ReadAllLinesAsync(filepath);
foreach (var line in lines)
{
var parts = line.Split(',');
var tx = new Transaction
{
PostedDate = DateTime.Parse(parts[0]),
TransactionDate = DateTime.Parse(parts[1]),
Description = parts[2],
Deposit = ParseDecimal(parts[3]),
Withdrawal = ParseDecimal(parts[4]),
Balance = ParseDecimal(parts[5])
};
ret.Add(tx);
}
return ret;
}
catch(Exception e)
{
throw;
}
}
我读过整个堆栈必须是异步的,以便数据库上下文实例可用,而且,除非我做错了,否则我似乎正在这样做,正如您在上面看到的那样。
我的期望是 AddDbContext() 确实会适当地限定整个堆栈中可用的上下文,除非我明确地处理它。我还没有发现任何让我有其他想法的东西。
我也尝试过在我的 DB Initializer 中对数据进行硬编码,据我所知,这可能是一个因素,但这并不能解决问题。不知道还能尝试什么。如果有人能给我一些想法,我将不胜感激。
由于 AddBulk(List<Transaction> transactions)
方法是 public async Task
,如果任何部分 returns 无效(未等待),DbContext 将被释放。
尝试更改 _context.SaveChanges();
到await _context.SaveChangesAsync();
这将确保返回 Task
而不是无效。
Import()
操作方法需要 return 类型 Task
。 MVC 将等待执行 return 类型为 Task
的操作方法。
此外,可能最好养成在操作方法上 returning IActionResult
的习惯。基于任务的等效项是 Task<IActionResult>
。这使您的控制器更易于测试。
为了进入 .NET Core,我创建了一个 WEB API,它接受文件上传,然后将文件中的事务保存到数据库中 table。我正在使用带有 Entity Framework Core 的 .NET Core 2。我使用 here 中的示例创建了上下文。
我的问题是当它试图保存到我存储库中的上下文对象时出现错误 "System.ObjectDisposedException Cannot access a disposed object"。这是一个简单的堆栈,所以我希望有人能帮助我。
我的容器设置如下所示:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<SyncFinContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<ITransactionProcessor, TransactionProcessor>();
services.AddScoped<ITransactionRepository, TransactionRepository>();
}
我的 DBInitializer 也是从上面的 link 得到的:
public static class DbInitializer
{
public static async Task Initialize(SyncFinContext context)
{
await context.Database.EnsureCreatedAsync();
// Look for any students.
if (context.Transactions.Any())
{
return; // DB has been seeded
}
var ts = new Transaction[]
{
// insert test data here
};
await context.SaveChangesAsync();
}
}
我的数据库上下文:
public class SyncFinContext : DbContext
{
public SyncFinContext(DbContextOptions<SyncFinContext> options) : base(options)
{
}
public DbSet<Transaction> Transactions { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Transaction>().ToTable("Transactions");
}
}
我的控制器看起来像这样:
[Produces("application/json")]
public class TransactionController : Controller
{
ITransactionRepository _transactionRepository { get; set; }
ITransactionProcessor _transactionProcessor { get; set; }
public TransactionController(ITransactionRepository m, ITransactionProcessor p) : base()
{
_transactionRepository = m;
_transactionProcessor = p;
}
// POST: transaction/import
[HttpPost]
public async void Import(List<IFormFile> files)
{
if (files == null || files.Count == 0)
{
throw new FileNotFoundException("No file was received.");
}
// copy file to temp location so that it can be processed
var filepath = Path.GetTempFileName();
using (var stream = new FileStream(filepath, FileMode.Create))
{
await files[0].CopyToAsync(stream);
}
ImportTransactionRequest input = new ImportTransactionRequest
{
FilePath = filepath
};
var transactions = await _transactionProcessor.ReadDocument(filepath);
await _transactionRepository.AddBulk(transactions);
}
}
我的存储库如下所示:
public class TransactionRepository : ITransactionRepository
{
// TODO: move the context
private SyncFinContext _context;
public TransactionRepository(SyncFinContext context)
{
_context = context;
}
public async Task AddBulk(List<Transaction> transactions)
{
foreach(var t in transactions)
{
await _context.Transactions.AddAsync(t);
}
_context.SaveChanges();
}
}
为了完全透明,交易处理器仅从 csv 中获取行列表:
public async Task<List<Transaction>> ReadDocument(string filepath)
{
try
{
var ret = new List<Transaction>();
var lines = await File.ReadAllLinesAsync(filepath);
foreach (var line in lines)
{
var parts = line.Split(',');
var tx = new Transaction
{
PostedDate = DateTime.Parse(parts[0]),
TransactionDate = DateTime.Parse(parts[1]),
Description = parts[2],
Deposit = ParseDecimal(parts[3]),
Withdrawal = ParseDecimal(parts[4]),
Balance = ParseDecimal(parts[5])
};
ret.Add(tx);
}
return ret;
}
catch(Exception e)
{
throw;
}
}
我读过整个堆栈必须是异步的,以便数据库上下文实例可用,而且,除非我做错了,否则我似乎正在这样做,正如您在上面看到的那样。
我的期望是 AddDbContext() 确实会适当地限定整个堆栈中可用的上下文,除非我明确地处理它。我还没有发现任何让我有其他想法的东西。
我也尝试过在我的 DB Initializer 中对数据进行硬编码,据我所知,这可能是一个因素,但这并不能解决问题。不知道还能尝试什么。如果有人能给我一些想法,我将不胜感激。
由于 AddBulk(List<Transaction> transactions)
方法是 public async Task
,如果任何部分 returns 无效(未等待),DbContext 将被释放。
尝试更改 _context.SaveChanges();
到await _context.SaveChangesAsync();
这将确保返回 Task
而不是无效。
Import()
操作方法需要 return 类型 Task
。 MVC 将等待执行 return 类型为 Task
的操作方法。
此外,可能最好养成在操作方法上 returning IActionResult
的习惯。基于任务的等效项是 Task<IActionResult>
。这使您的控制器更易于测试。