我希望在当前项目中使用多个上下文/模式,但我不确定如何正确地将对上下文的写访问包装在单个事务中。我的理解是有两种方法可以实现这一点:
例如:假设以下模型/上下文:
假设有第二个模型/上下文(Model/Context):
我最担心的是找不到一个关于如何在“内部”上下文中重用“外部”上下文连接的示例。文档中说事务是“外部的”,但我迄今为止找到的示例中,事务并没有被用于在上下文之间传递。要么使用事务来运行
换句话说:我想知道是否在误用可能不打算以这种方式使用的功能。这也可能暗示着既不能直接将连接和事务传递给“内部”上下文,而必须使用它们的“基础”版本。
这里我主要关注于 MSDTC (Microsoft Distributed Transaction Coordinator),即避免将事务升级为分布式事务。
如果满足以下所有条件,则似乎事务不会被升级:
DbContext.Database.BeginTransaction()
和 TransactionScope
。我不确定我是否正确地使用了它们,或者是否还有其他方法可以做到这一点。例如:假设以下模型/上下文:
public class A
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
}
public class ContextA : DbContext
{
public ContextA() : base( "MultipleContextsTest" ) { }
public DbSet<A> SetA { get; set; }
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
modelBuilder.HasDefaultSchema( "SchemaA" );
base.OnModelCreating( modelBuilder );
}
}
假设有第二个模型/上下文(Model/Context):
public class B
{
[Key]
public int Id { get; set; }
public string OtherName { get; set; }
}
public class ContextB : DbContext
{
public ContextB() : base( "MultipleContextsTest" ) { }
public ContextB( DbConnection conn, bool ownsConnection = false )
: base( conn, ownsConnection ) { }
public DbSet<B> SetB { get; set; }
protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
modelBuilder.HasDefaultSchema( "SchemaB" );
base.OnModelCreating( modelBuilder );
}
}
那么,使用 DbContext.Database.BeginTransaction()
方法的我的方法如下:
void Using_BeginTransaction()
{
using( ContextA contextA = new ContextA() )
{
using( var transaction = contextA.Database.BeginTransaction() )
{
contextA.SetA.Add( new A { Name = "Name" } );
contextA.SaveChanges();
using( ContextB contextB
= new ContextB( transaction.UnderlyingTransaction.Connection ) )
{
contextB.Database.UseTransaction( transaction.UnderlyingTransaction );
contextB.SetB.Add( new B() { OtherName = "OtherName" } );
contextB.SaveChanges();
}
transaction.Commit();
}
}
}
我最担心的是找不到一个关于如何在“内部”上下文中重用“外部”上下文连接的示例。文档中说事务是“外部的”,但我迄今为止找到的示例中,事务并没有被用于在上下文之间传递。要么使用事务来运行
SQLCommand
,要么从外部显式地创建并接收SqlConnection
的事务。换句话说:我想知道是否在误用可能不打算以这种方式使用的功能。这也可能暗示着既不能直接将连接和事务传递给“内部”上下文,而必须使用它们的“基础”版本。
我使用TransactionScope
的方法如下:
void Using_TransactionScope()
{
using( TransactionScope transaction = new TransactionScope() )
{
using( ContextA contextA = new ContextA() )
{
contextA.SetA.Add( new A { Name = "Name" } );
contextA.SaveChanges();
}
using( ContextB contextB = new ContextB() )
{
contextB.SetB.Add( new B() { OtherName = "OtherName" } );
contextB.SaveChanges();
}
transaction.Complete();
}
}
这里我主要关注于 MSDTC (Microsoft Distributed Transaction Coordinator),即避免将事务升级为分布式事务。
如果满足以下所有条件,则似乎事务不会被升级:
- 只使用单个数据库
- 上下文使用相同的连接字符串
- 上下文使用相同的连接(不确定是否100%正确)
能否保证上下文使用相同的连接,还是可以实现这一点?
然而,我最担心的是在 Stack Overflow 问题One transaction with multiple dbcontexts的接受答案下面的评论。这些评论可能意味着事务被升级为“轻量级”分布式事务(不知道这是什么)。无论如何,评论者都认为这种方法非常危险。这正确吗?但是,首先 TransactionScope
的目的是什么?
顺便说一下:我进行了一些简单的性能测试(对每个以上两种方法进行了5000次调用),发现:
BeginTransaction()
比TransactionScope
稍快。- 两个版本都比不使用事务的情况明显快。我没有想到。也许是因为只需要创建一个连接?