wir*_*_in 4 entity-framework entity-framework-core .net-core
我在 .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..."
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我在将 Blog 类映射到数据库模式时故意犯了一个错误,因此当我.ToList()在dbContext.Blogs实体框架上执行此操作时,会抛出无效操作错误,例如An exception occurred while reading a database value for Blog.Name因为Name在nvarchar(max)数据库中,但int在我的Blog类中。
因此,现在我的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
Run Code Online (Sandbox Code Playgroud)
这是一个非常简单的例子,我意识到这里没有什么可提交的。实际上,我正在运行一个 ASP.NET Core 应用程序,其中有一个全局事务包装器,因此某些请求可能只是读取,但其他请求可能是读取和更新。
我在这里做错了什么吗?据我所知,这是相当标准的,但我已经用谷歌搜索了两天,没有发现任何人有同样的问题。
似乎 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();
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
以及一个最小的 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();
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
有趣的是,此错误不会在 EF6 上重现,因为在 EF6 中 DbContext.Database.BeginTransaction 使用 EntityTransaction,其中 EF 核心在 SqlClient 的 SqlTransation 上使用轻量级包装器,这具有这种不幸的行为。
要解决此问题,请添加MultipleActiveResultsets=true到连接字符串(这在 EF 中很有用,因为它允许您在读取结果时运行其他查询)。或者使用 TSQL 回滚事务,如下所示:
db.Database.ExecuteSqlCommand("if @@trancount > 0 rollback;");
Run Code Online (Sandbox Code Playgroud)
我打开了一个 GitHub 问题来跟踪此问题:https ://github.com/aspnet/EntityFrameworkCore/issues/9658
| 归档时间: |
|
| 查看次数: |
1499 次 |
| 最近记录: |