【问题标题】:How to get visibility to inserted records where DbContext is not saved yet如何查看尚未保存 DbContext 的插入记录
【发布时间】:2016-12-30 15:44:37
【问题描述】:

我正在实现一个服务层,我需要确保跨多个表的特定数量的操作发生在一个事务中。这是工作流程。

  1. 我得到了一个 HistoricalData 对象实例,它需要存储在 HistoricalData 表中。这是在 AddHistoricalData 方法中完成的。
  2. 我需要从 HistoricalData 表中检索所有记录,包括在 #1 中插入的内容,但可以有更多记录。这是在 ProcessAllData 方法中完成的。
  3. 处理完所有这些记录后,结果将存储在另外两个表中,ProcessStatus 和 ProcessResults。如果出现任何问题,我需要回滚事务,包括在操作 #1 中插入的内容。

这就是它的实现方式。

public class HistoricalDataService : IHistoricalDataService
    {
        private MyDbContext dbContext;
        public HistoricalDataService(MyDbContext context)
        {
            this.dbContext = context;
        }

        void AddHistoricalData(HistoricalData hData)
        {
            // insert into HistoricalData table
        }

        void ProcessAllData()
        {
            // Here we process all records from HistoricalData table insert porcessing results into two other tables
        }

        void SaveData()
        {
            this.dbContext.SaveChanges();
        }
    }

这是调用此类方法的方式。

HistoricalDataService service = new HistoricalDataService (dbcontext);
service.AddHistoricalData(HistoricalData instance);
service.ProcessAllData();
service.SaveData();

这种方法的问题是,在调用 AddHistoricalData 方法期间插入到 HistoricalData 表中的任何内容在 ProcessAllData 调用中都不可见,因为 dbContext.SaveChanges 仅在最后被调用。我在想我需要以某种方式在这里引入事务范围,但不确定如何公开应该启动该事务范围的函数?

【问题讨论】:

  • 为什么说HistoricalData不可见?如果您将 HistoricalData 对象插入 dbContext.HistoricalData,那么您应该能够在 ProcessAllData 方法中获取此对象
  • 不知道为什么,但是使用 context.HistoricalData.Add 在 AddHistoricalData 方法中插入的位置在 context.HistoricalData 集合中的 ProcessAllData 方法中可见。我的猜测是我必须在上下文中调用 SaveChanges

标签: c# transactions entity-framework-6 dbcontext service-layer


【解决方案1】:

您可以通过多种方式来做到这一点。试试这个(未经测试,但 POC)

public class HistoricalDataService : IHistoricalDataService
{
    private DbContext dbContext;
    private DbContextTransaction dbContextTransaction;

    public HistoricalDataService(DbContext context)
    {
        this.dbContext = context;
    }

    void AddHistoricalData(HistoricalData hData)
    {
        if (dbContext.Database.CurrentTransaction == null)
        {
            dbContextTransaction = dbContext.Database.BeginTransaction();
        }

        // insert into HistoricalData table
        dbContext.SaveChanges();
    }

    void ProcessAllData()
    {
        // Here we process all records from HistoricalData table insert porcessing results into two other tables
    }

    void Rollback()
    {
        if (dbContextTransaction != null)
        {
            this.dbContextTransaction.Rollback();
        }
    }

    void SaveData()
    {
        this.dbContextTransaction.Commit();
        this.dbContextTransaction.Dispose();
    }
}

这样使用:

HistoricalDataService service = new HistoricalDataService (dbcontext);

try 
{
     service.AddHistoricalData(HistoricalData instance);
     service.ProcessAllData();
     service.SaveData();
}
catch (Exception ex)
{
     service.Rollback();
}

【讨论】:

    【解决方案2】:

    我建议重构代码,使服务具有单一方法(在更高的抽象级别)和单一职责:处理用例。

    那么,客户端类将有

    private readonly IHistoricalDataService _historicalDataService;
    
    _historicalDataService.RearrangeSeatingArrangement(); //High level abstraction
    

    执行上述操作的目的是确保您的服务类的所有操作都发生在单个方法中,如果使用原始 ADO.NET,则使用事务范围包装,如果使用 EF,则使用上下文对象包装。当它可以只调用一个时,不要让客户端类调用三个方法。这首先是服务类的目的:处理用例并将响应返回给客户端类(在您的情况下可能是控制器)。

    现在,当谈到确保代码的某些部分知道已持久化的数据时,它会带来一些额外的问题:在上面的 ProcessAllData() 期间发生的命令,为什么它会精确地拆分数据?拆分的数据是否可以在内存中拆分(在另一个域类中),添加到上下文中,并保存在 SaveChanges() 方法中?这将确保您只对数据库进行一次调用(这是 Entity Framework Unit Of Work 的目的。即:在上下文中累积更改,添加、删除、更新,然后在一次操作中与数据库交谈)。

    【讨论】:

      猜你喜欢
      • 2011-09-05
      • 1970-01-01
      • 2011-06-12
      • 1970-01-01
      • 1970-01-01
      • 2016-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多