gpt4 book ai didi

entity-framework - 回滚事务抛出 "There is already an open DataReader associated with this Command..."错误

转载 作者:行者123 更新时间:2023-12-04 08:31:32 35 4
gpt4 key购买 nike

我在 .NET Core 2.0 上使用 Entity Framework Core 2.0。为了重现这个问题,我制作了一个简单的控制台应用程序。

// Program.cs
static void Main(string[] args)
{
using (var dbContext = new MyDbContext())
{
using (var transaction = dbContext.Database.BeginTransaction())
{
try
{
var blogs = dbContext.Blogs
.ToList(); // throws error because of schema mismatch in my Blog class

// other stuff that may or may not make db changes

dbContext.SaveChanges();

transaction.Commit();
}

catch (Exception ex)
{
transaction.Rollback(); // throws second error that hides the initial error: "There is already an open DataReader..."
}
}
}
}

我在将我的博客类映射到数据库模式时故意犯了一个错误,所以当我做 .ToList() 时在 dbContext.Blogs Entity Framework 引发无效操作错误,类似于 An exception occurred while reading a database value for Blog.Name因为 Namenvarchar(max)在数据库中,但是 int在我的 Blog类(class)。

所以现在我的 catch当事务期间发生任何错误时,语句尝试回滚事务,但该回滚会导致另一个错误,即最终被记录的错误,隐藏了初始错误。
System.InvalidOperationException occurred
HResult=0x80131509
Message=There is already an open DataReader associated with this Command which must be closed first.
Source=<Cannot evaluate the exception source>
StackTrace:
at System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command)
at System.Data.SqlClient.SqlInternalTransaction.Rollback()
at System.Data.SqlClient.SqlTransaction.Rollback()
at Microsoft.EntityFrameworkCore.Storage.RelationalTransaction.Rollback()
at ConsoleApp.Program.Main(String[] args) in C:\Users\mitch\OneDrive\Dev\EFCore\ConsoleApp\ConsoleApp\Program.cs:line 28

现在这是一个非常简单的例子,我意识到这里没有什么可提交的。实际上,我正在运行一个 ASP.NET 核心应用程序,其中我有一个全局事务包装器,因此某些请求可能只是读取,而其他请求可能是读取和更新。

我在这里做错了吗?从我读到的内容来看,这是非常标准的,但我已经用谷歌搜索了两天没有发现任何人有同样的问题。

看起来EF在查询数据库中的博客时打开了一个DataReader,然后在执行查询的过程中发生了异常,因此DataReader保持打开状态,因此在此之后对连接执行任何操作时,我收到打开DataReader错误。如果是这种情况,我应该如何处理查询期间发生的错误?我需要确保回滚可能发生的任何更新,并且我需要处理事务和连接。

最佳答案

这是 (IMO) System.Data.SqlClient.SqlTransaction 中的一个错误,它也在 .NET Framework 上重现。这也是 EF Core 中的一个错误,因为它不会在 EF6 上重现。我不确定这是否是一个已知错误,对于 EF Core 和 SqlTransaction,它的分类可能不同。最终您的 Using 块将回滚事务。

这是一个最小的 ADO.NET 重现:

using System;
using System.Data.SqlClient;

namespace ConsoleApp8
{
class Program
{
static void Main(string[] args)
{
using (var con = new SqlConnection("Server=localhost;database=tempdb;Integrated Security=true;MultipleActiveResultsets=false"))
{
con.Open();
using (var tran = con.BeginTransaction())
{
var cmd = new SqlCommand("select * from sys.objects", con, tran);
var rdr = cmd.ExecuteReader();
rdr.Read();

tran.Rollback();
}
}
}
}
}

和一个最小的 EF Core 重现:
using Microsoft.EntityFrameworkCore;
using System;
using System.Data.SqlClient;
using System.Linq;

namespace ConsoleApp8
{

public class Foo
{
public int Id { get; set; }
}
public class Db : DbContext
{
public DbSet<Foo> Foos { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=localhost;database=EfCoreTest;Integrated Security=true;MultipleActiveResultsets=false");
base.OnConfiguring(optionsBuilder);
}

}
class Program
{
static void Main(string[] args)
{
using (var db = new Db())
{
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
db.Foos.Add(new Foo());
db.SaveChanges();
}

using (var db = new Db())
{
var tran = db.Database.BeginTransaction();
foreach (var foo in db.Foos)
{
tran.Rollback();
}

}

}
}
}

有趣的是,此错误不会在 EF6 上重现,因为在 EF6 中 DbContext.Database.BeginTransaction 使用 EntityTransaction,其中 EF 核心在 SqlClient 的 SqlTransation 上使用轻量级包装器,它具有这种不幸的行为。

要解决此问题,请添加 MultipleActiveResultsets=true到您的连接字符串(这在 EF 中很有用,因为它允许您在读取结果时运行其他查询)。或者像这样使用 TSQL 回滚您的事务:
db.Database.ExecuteSqlCommand("if @@trancount > 0 rollback;");

我打开了一个 GitHub 问题来跟踪这个: https://github.com/aspnet/EntityFrameworkCore/issues/9658

关于entity-framework - 回滚事务抛出 "There is already an open DataReader associated with this Command..."错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45971968/

35 4 0