实体框架未拾取事务范围
本文关键字:事务 范围 框架 实体 | 更新日期: 2023-09-27 17:58:48
我有相当标准的EF 6.1"在数据库中创建对象"代码封装在事务范围中。无论出于何种原因,在事务失败(无法完成)后,数据都会保留在数据库中。
代码:
using (var db = this.Container.Resolve<SharedDataEntities>()) // << new instance of DbContext
{
using (TransactionScope ts = new TransactionScope())
{
SubscriptionTypes st = this.SubscriptionType.Value;
if (st == SubscriptionTypes.Lite && this.ProTrial)
st = SubscriptionTypes.ProTrial;
Domain domain = new Domain()
{
Address = this.Address.Trim(),
AdminUserId = (Guid)user.ProviderUserKey,
AdminUserName = user.UserName,
Description = this.Description.TrimSafe(),
DomainKey = Guid.NewGuid(),
Enabled = !masterSettings.DomainEnableControlled.Value,
Name = this.Name.Trim(),
SubscriptionType = (int)st,
Timezone = this.Timezone,
Website = this.Website.TrimSafe(),
IsPrivate = this.IsPrivate
};
foreach (var countryId in this.Countries)
{
domain.DomainCountries.Add(new DomainCountry() { CountryId = countryId, Domain = domain });
}
db.Domains.Add(domain);
db.SaveChanges(); // << This is the Saving that should not be commited until we call 'ts.Complete()'
this.ResendActivation(domain); // << This is where the Exception occurs
using (TransactionScope ts2 = new TransactionScope(TransactionScopeOption.Suppress))
{
this.DomainMembership.CreateDomainUser(domain.Id, (Guid)user.ProviderUserKey, user.UserName, DomainRoles.DomainSuperAdmin | DomainRoles.Driver);
ts2.Complete();
}
this.Enabled = domain.Enabled;
ts.Complete(); // << Transaction commit never happens
}
}
SaveChanges()之后,ResendActivation(…)内部引发异常,因此不应保存更改。然而,这些记录保留在数据库中。
没有其他TransactionScope包装我粘贴的代码,它是由MVC Action调用触发的。
经过更多的调查,发现可能是实体框架升级或数据库更新过程导致
Enlist=false;
到数据库连接字符串中。这有效地阻止了EF收购交易范围。
所以解决方案是将其设置为true,或者删除它,我认为默认情况下它是true
尝试使用来自db实例it self的事务,db。Database.BeginTransaction(),如果我正确调用它而不是使用事务范围。
using (var ts = db.Database.BeginTransaction())
{
..
}
假设数据库是您的实体框架上下文。
Context类默认支持事务。但是对于上下文类的每一个新实例,都会创建一个新事务。这个新事务是一个嵌套事务,一旦调用关联上下文类上的SaveChanges(),它就会被提交。
在给定的代码中,我们似乎调用了一个负责创建域用户的方法,即CreateDomainUser,它可能有自己的上下文对象。因此这个问题。
如果是这种情况(这个方法有自己的上下文),也许我们在这里甚至不需要TransactionScope。我们可以简单地将相同的上下文(在调用之前使用)传递给创建域用户的函数。然后我们可以检查这两个操作的结果,如果它们都成功了,我们只需要调用SaveChanges()方法。
当我们将ADO.NET调用与实体框架混合使用时,通常需要TransactionScope。我们也可以将它与上下文一起使用,但这将是一种过度的做法,因为上下文类已经有了一个事务,我们可以简单地使用相同的上下文来管理事务。如果在上面的代码中是这种情况,那么解决这个问题的诀窍是让上下文类知道你想将它与自己的事务一起使用。由于事务与作用域中的连接对象相关联,因此我们需要将上下文类用于与事务作用域相关联的连接。此外,我们需要让上下文类知道它不能拥有连接,因为它由调用代码拥有。所以我们可以做一些类似的事情:
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
using (var conn = new SqlConnection("..."))
{
conn.Open();
var sqlCommand = new SqlCommand();
sqlCommand.Connection = conn;
sqlCommand.CommandText =
@"UPDATE Blogs SET Rating = 5" +
" WHERE Name LIKE '%Entity Framework%'";
sqlCommand.ExecuteNonQuery();
using (var context =
new BloggingContext(conn, contextOwnsConnection: false))
{
var query = context.Posts.Where(p => p.Blog.Rating > 5);
foreach (var post in query)
{
post.Title += "[Cool Blog]";
}
context.SaveChanges();
}
}
scope.Complete();
}
请参阅:http://msdn.microsoft.com/en-us/data/dn456843.aspx
您应该能够将TransactionScope
与EF一起使用,我知道我们的项目是这样做的。然而,我认为您希望在事务范围内实例化EF上下文——也就是说,我认为需要交换最外层/前两个using
语句,正如@rahuls的答案所示。
即使它以另一种方式工作。。。如果您有一个需要更新多个表的服务/应用程序/业务层方法,并且您希望这些更新是原子的,那么您需要这样做。因此,为了一致性(以及您自己的理智),我建议首先考虑事务范围,其次考虑上下文。