如何获得对尚未保存 DbContext 的插入记录的可见性
How to get visibility to inserted records where DbContext is not saved yet
我正在实施一个服务层,我需要确保在一个事务中发生跨多个 table 的特定数量的操作。这是工作流程。
- 我得到一个需要存储在 HistoricalData 中的 HistoricalData 对象的实例 table。这是在 AddHistoricalData 方法中完成的。
- 我需要从 HistoricalData table 中检索所有记录,其中包括 #1 中插入的内容,但可以有更多记录。这是在 ProcessAllData 方法中完成的。
- 处理所有这些记录后,结果存储在另外两个 table 中,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();
}
}
下面是 class 方法的调用方式。
HistoricalDataService service = new HistoricalDataService (dbcontext);
service.AddHistoricalData(HistoricalData instance);
service.ProcessAllData();
service.SaveData();
此方法的问题在于,在调用 AddHistoricalData 方法期间在 HistoricalData table 中插入的任何内容在 ProcessAllData 调用中都不可见,因为 dbContext.SaveChanges 仅在最后调用。我在想我需要以某种方式将事务范围引入这里,但不确定如何在应该启动该事务范围的地方公开一个函数?
您可以通过不同的方式执行此操作。试试这个(未经测试,但 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();
}
我建议重构代码,使服务具有单一方法(在更高的抽象级别)和单一职责:处理用例。
那么,客户 class 会
private readonly IHistoricalDataService _historicalDataService;
_historicalDataService.RearrangeSeatingArrangement(); //High level abstraction
执行上述操作的目的是确保您的服务 class 的所有操作都在一个方法中发生,如果使用原始 ADO.NET 则使用事务范围包装,如果使用原始 ADO.NET 则使用上下文对象包装英孚。不要让客户端 class 在它只能调用一个时调用三个方法。这首先是服务 class 的目的:处理用例和 return 对客户端 class 的响应(在您的情况下可能是控制器)。
现在,当谈到确保您的代码的某些部分知道已持久化的数据时,它会带来一些额外的问题:在上面的 ProcessAllData() 期间发生的命令,为什么它会拆分数据确切地?拆分后的数据能否在内存中拆分(在另一个域 class 中),添加到上下文中,并保存在 SaveChanges() 方法中?这将确保您只对数据库进行一次调用(这是 Entity Framework 工作单元的目的。即:累积上下文中的更改、添加、删除、更新,然后在一次操作中与数据库对话) .
我正在实施一个服务层,我需要确保在一个事务中发生跨多个 table 的特定数量的操作。这是工作流程。
- 我得到一个需要存储在 HistoricalData 中的 HistoricalData 对象的实例 table。这是在 AddHistoricalData 方法中完成的。
- 我需要从 HistoricalData table 中检索所有记录,其中包括 #1 中插入的内容,但可以有更多记录。这是在 ProcessAllData 方法中完成的。
- 处理所有这些记录后,结果存储在另外两个 table 中,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();
}
}
下面是 class 方法的调用方式。
HistoricalDataService service = new HistoricalDataService (dbcontext);
service.AddHistoricalData(HistoricalData instance);
service.ProcessAllData();
service.SaveData();
此方法的问题在于,在调用 AddHistoricalData 方法期间在 HistoricalData table 中插入的任何内容在 ProcessAllData 调用中都不可见,因为 dbContext.SaveChanges 仅在最后调用。我在想我需要以某种方式将事务范围引入这里,但不确定如何在应该启动该事务范围的地方公开一个函数?
您可以通过不同的方式执行此操作。试试这个(未经测试,但 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();
}
我建议重构代码,使服务具有单一方法(在更高的抽象级别)和单一职责:处理用例。
那么,客户 class 会
private readonly IHistoricalDataService _historicalDataService;
_historicalDataService.RearrangeSeatingArrangement(); //High level abstraction
执行上述操作的目的是确保您的服务 class 的所有操作都在一个方法中发生,如果使用原始 ADO.NET 则使用事务范围包装,如果使用原始 ADO.NET 则使用上下文对象包装英孚。不要让客户端 class 在它只能调用一个时调用三个方法。这首先是服务 class 的目的:处理用例和 return 对客户端 class 的响应(在您的情况下可能是控制器)。
现在,当谈到确保您的代码的某些部分知道已持久化的数据时,它会带来一些额外的问题:在上面的 ProcessAllData() 期间发生的命令,为什么它会拆分数据确切地?拆分后的数据能否在内存中拆分(在另一个域 class 中),添加到上下文中,并保存在 SaveChanges() 方法中?这将确保您只对数据库进行一次调用(这是 Entity Framework 工作单元的目的。即:累积上下文中的更改、添加、删除、更新,然后在一次操作中与数据库对话) .