在某些情况下,我已经在我的存储库函数中使用事务,因为我有时需要同时向两个表中插入数据,如果其中一个插入失败,我希望整个操作失败。
现在我遇到了一种情况,我必须在另一个事务中 Package 对多个存储库/函数的调用,但是当其中一个函数已经在内部使用事务时,我将得到错误The connection is already in a transaction and cannot participate in another transaction
。
我不想从存储库函数中删除事务,因为这意味着我必须知道哪些存储库函数需要事务,然后我必须在服务层中实现事务。另一方面,当存储库函数已经在内部使用事务时,我似乎不能在事务中使用它们。下面是一个例子,说明我在哪里遇到了这个问题:
// Reverse engineered classes
public partial class TblProject
{
public TblProject()
{
TblProjectStepSequences = new HashSet<TblProjectStepSequence>();
}
public int ProjectId { get; set; }
public virtual ICollection<TblProjectStepSequence> TblProjectStepSequences { get; set; }
}
public partial class TblProjectTranslation
{
public int ProjectId { get; set; }
public string Language { get; set; }
public string ProjectName { get; set; }
public virtual TblProject Project { get; set; }
}
public partial class TblProjectStepSequence
{
public int SequenceId { get; set; }
public int ProjectId { get; set; }
public int StepId { get; set; }
public int SequencePosition { get; set; }
public virtual TblStep Step { get; set; }
public virtual TblProject Project { get; set; }
}
// Creating a project in the ProjectRepository
public async Task<int> CreateProjectAsync(TblProject project, ...)
{
using (var transaction = this.Context.Database.BeginTransaction())
{
await this.Context.TblProjects.AddAsync(project);
await this.Context.SaveChangesAsync();
// Insert translations... (project Id is required for this)
await this.Context.SaveChangesAsync();
transaction.Commit();
return entity.ProjectId;
}
}
// Creating the steps for a project in the StepRepository
public async Task<IEnumerable<int>> CreateProjectStepsAsync(int projectId, IEnumerable<TblProjectStepSequence> steps)
{
await this.Context.TblProjectStepSequences.AddRangeAsync(steps);
await this.Context.SaveChangesAsync();
return steps.Select(step =>
{
return step.SequenceId;
}
);
}
// Creating a project with its steps in the service layer
public async Task<int> CreateProjectWithStepsAsync(TblProject project, IEnumerable<TblProjectStepSequence> steps)
{
// This is basically a wrapper around Database.BeginTransaction() and IDbContextTransaction
using (Transaction transaction = await transactionService.BeginTransactionAsync())
{
int projectId = await projectRepository.CreateProjectAsync(project);
await stepRepository.CreateProjectStepsAsync(projectId, steps);
return projectId;
}
}
有没有一种方法可以让多个事务嵌套在彼此内部,而不知道内部事务中可能有一个外部事务?
我知道从技术的Angular 来看,实际上可能不可能嵌套这些事务,但我仍然需要一个解决方案,该解决方案使用存储库的内部事务或外部事务(如果存在的话),因此我不可能不小心忘记将事务用于需要事务的存储库功能。
4条答案
按热度按时间s71maibg1#
您可以检查CurrentTransaction属性并执行以下操作:
如果已经有一个事务使用它,否则开始一个新的事务...
编辑:删除了Using块,请参阅注解。提交/回滚事务需要更多的逻辑...
oaxa6hgo2#
我正在回答你问的问题“如何在EF Core 6中嵌套交易?”“
说了这么多,我们继续主题:
尝试使用这个helper函数来创建一个新的事务:
使用Northwind数据库作为示例数据库,您可以像这样使用它:
然后你可以从另一个函数调用它,比如:
要使用LinqPad运行这个,你需要一个入口点(当然,通过F4添加NUGET包EntityFramework 6.x,然后创建一个EntityFramework Core连接):
这只是一个简单的示例-它不会检查是否存在同名的任何产品,它只会创建一个新产品。你可以很容易地实现它,我已经在类别的函数
CreateProjectWithStepsAsync
中展示了它:首先,它按名称查询类别(通过
GetCategoryId(...)
),如果结果是null
,它将创建一个新类别(通过CreateCategoryAsync(...)
)。另外,你需要考虑isolation level:查看
System.Transactions.IsolationLevel
,看看这里使用的(ReadCommitted)是否适合您(这是默认设置)。它所做的是显式地创建一个事务,注意这里我们有一个事务中的事务。
注:
using
的两种方式我都用过-旧的和新的。挑一个你更喜欢的。fzwojiic3#
我就像这样...
所以我用这样的代码调用它
ps.建议您也为同步代码创建一个重载...
pbpqsu0x4#
只是 * 不要 * 多次调用
SaveChanges
。问题是由于多次调用
SaveChanges
来提交对DbContext所做的更改,而不是在最后只调用一次。根本不需要。DbContext是一个多实体工作单元。它甚至不保持与数据库的开放连接。通过消除交叉连接阻塞,整个应用程序的吞吐量提高了100 - 1000倍。DbContext跟踪对它跟踪的对象所做的所有修改,并在使用内部事务调用
SaveChanges
时持久化/提交它们。要放弃更改,只需释放DbContext。这就是为什么所有示例都显示在using
块中使用DbContext的原因-这实际上是工作单元“事务”的范围。不需要先“保存”父对象。EF Core将在
SaveChanges
内部处理此问题。使用EF Core documentation tutorial中的Blog/Posts示例:
下面的
Program.cs
将添加一个包含5篇文章的博客,但最后只调用SaveChanges
一次:代码从不指定或检索ID。
Add
是一个内存操作,所以没有理由使用AddAsync
。Add
在Inserted
状态下开始跟踪blog和相关的Post
s。其后各表的内容为:
执行两次代码将添加另一个博客,其中包含另外5篇文章。
使用SQL Server XEvents事件探查器显示进行了以下SQL调用:
不寻常的SELECT和MERGE用于确保IDENTITY值以插入对象的顺序返回,因此EF Core可以将它们分配给对象属性。调用
SaveChanges
后,所有Blog和Post对象都将具有正确的数据库生成的ID