Wanting to get into .NET Core, I created a WEB API that takes a file upload and then saves the transactions in the file into a DB table. I'm using .NET Core 2 with Entity Framework Core. I created my context using the example from here.
My problem is that I get the error "System.ObjectDisposedException Cannot access a disposed object" when it tries to save to the context object in my repository. It's a simple stack, so I'm hoping someone can help me out.
My container setup looks like this:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddDbContext<SyncFinContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddScoped<ITransactionProcessor, TransactionProcessor>(); services.AddScoped<ITransactionRepository, TransactionRepository>(); } My DBInitializer which I also got from the link above:
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(); } } My DB Context:
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"); } } My Controller looks like this:
[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); } } And my repository looks like this:
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(); } } For full transparency, the transaction Processor just gets a list of rows from a 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; } } I've read where the whole stack must be async in order for the db context instance to be available, and, unless I'm doing it wrong, I seem to be doing that, as you can see above.
My expectations are that AddDbContext() will indeed properly scope the context to be available throughout the stack unless I explicitly dispose of it. I have not found anything to make me think otherwise.
I've tried hard-coding data in my DB Initializer also, as I read that may be a factor, but that does not solve the problem. Not sure what else to try. If someone can give me some ideas I would appreciate it.
Importaction return type is void. Have you tried returning aTask?