ASP .NET Entity Framework Core 无法访问已释放的对象。

4
想学习.NET Core,我创建了一个WEB API用于上传文件,然后将文件中的交易保存到数据库表中。我使用的是.NET Core 2和Entity Framework Core。 我使用这里的示例创建了我的上下文。

我的问题是,在尝试保存到存储库中的上下文对象时,会出现“System.ObjectDisposedException无法访问已释放的对象”错误。这是一个简单的堆栈,所以我希望有人能帮帮我。

我的容器设置如下:

    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:

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();
    }
}   

我的DB上下文:

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初始化程序中硬编码数据,因为我读到这可能是一个因素,但这并不能解决问题。不知道还有什么别的方法可以尝试。如果有人能给我一些思路,我会很感激。


2
首先要注意的是Import操作的返回类型为void。您是否尝试过返回一个Task - Brad
将您的方法更改为同步。 - Vivek Nuna
@Brad 就是这样!我没有考虑到这一点,因为它发生在第一个 API 调用之内,而且它从未有机会返回,所以它 不应该 影响上下文,但显然它确实影响了。请将其发布为答案,我会接受它。 - Ben
2个回答

8
Import()动作方法需要返回类型为Task。MVC将等待执行返回类型为Task的动作方法。
此外,最好养成在操作方法中返回IActionResult的习惯。基于任务的等效项是Task<IActionResult>。这使您的控制器更易于测试。

谢谢您解释MVC在返回类型为Task时的等待。现在完全理解了。 - Ben

5

错误出现在我到达之前,出现在 AddAsync()。但是我已经更改了它,但仍然没有起作用。 - Ben

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接