首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >ExecuteReader要求命令具有一个事务

ExecuteReader要求命令具有一个事务
EN

Stack Overflow用户
提问于 2016-06-28 17:00:59
回答 2查看 5.1K关注 0票数 1

当将事务传递给EF时,我们得到了一个奇怪的异常:

引发的异常:“System.InvalidOperationException”在System.Data.dll中 附加信息:当分配给命令的连接位于挂起的本地事务中时,ExecuteReader要求命令具有事务。命令的事务属性尚未初始化。

代码语言:javascript
运行
复制
this.DbContext = this.DbContextFactory.CreateContext<TContext>(connection);
this.DbContext.Database.UseTransaction(transaction);

EF会捕获此异常,因为只有在“抛出时中断”时才会显示该异常。它是预期的行为,还是我们做了什么潜在的错误?

下面是调用堆栈的样子:

代码语言:javascript
运行
复制
System.Data.dll!System.Data.SqlClient.SqlCommand.ValidateCommand(string method, bool async)
System.Data.dll!System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior cmdBehavior, System.Data.SqlClient.RunBehavior runBehavior, bool returnStream, string method, System.Threading.Tasks.TaskCompletionSource<object> completion, int timeout, out System.Threading.Tasks.Task task, bool asyncWrite)
System.Data.dll!System.Data.SqlClient.SqlCommand.RunExecuteReader(System.Data.CommandBehavior cmdBehavior, System.Data.SqlClient.RunBehavior runBehavior, bool returnStream, string method)
System.Data.dll!System.Data.SqlClient.SqlCommand.ExecuteReader(System.Data.CommandBehavior behavior, string method)
EntityFramework.dll!System.Data.Entity.Infrastructure.Interception.InternalDispatcher<System.Data.Entity.Infrastructure.Interception.IDbCommandInterceptor>.Dispatch<System.Data.Common.DbCommand, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext<System.Data.Common.DbDataReader>, System.Data.Common.DbDataReader>(System.Data.Common.DbCommand target, System.Func<System.Data.Common.DbCommand, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext<System.Data.Common.DbDataReader>, System.Data.Common.DbDataReader> operation, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext, System.Action<System.Data.Entity.Infrastructure.Interception.IDbCommandInterceptor, System.Data.Common.DbCommand, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext<System.Data.Common.DbDataReader>> executing, System.Action<System.Data.Entity.Infrastructure.Interception.IDbCommandInterceptor, System.Data.Common.DbCommand, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext<System.Data.Common.DbDataReader>> executed)
EntityFramework.dll!System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.Reader(System.Data.Common.DbCommand command, System.Data.Entity.Infrastructure.Interception.DbCommandInterceptionContext interceptionContext)
EntityFramework.SqlServer.dll!System.Data.Entity.SqlServer.SqlVersionUtils.GetServerType(System.Data.Common.DbConnection connection)
EntityFramework.SqlServer.dll!System.Data.Entity.SqlServer.SqlProviderServices.QueryForManifestToken(System.Data.Common.DbConnection conn)
EntityFramework.SqlServer.dll!System.Data.Entity.SqlServer.SqlProviderServices.GetDbProviderManifestToken.AnonymousMethod__9(System.Data.Common.DbConnection conn)
EntityFramework.SqlServer.dll!System.Data.Entity.SqlServer.SqlProviderServices.UsingConnection.AnonymousMethod__32()
EntityFramework.SqlServer.dll!System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute.AnonymousMethod__0()
EntityFramework.SqlServer.dll!System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute<object>(System.Func<object> operation)
EntityFramework.SqlServer.dll!System.Data.Entity.SqlServer.SqlProviderServices.GetDbProviderManifestToken(System.Data.Common.DbConnection connection)
EntityFramework.dll!System.Data.Entity.Core.Common.DbProviderServices.GetProviderManifestToken(System.Data.Common.DbConnection connection)
EntityFramework.dll!System.Data.Entity.Utilities.DbProviderServicesExtensions.GetProviderManifestTokenChecked(System.Data.Entity.Core.Common.DbProviderServices providerServices, System.Data.Common.DbConnection connection)
mscorlib.dll!System.Collections.Concurrent.ConcurrentDictionary<System.Tuple<System.Type, string, string>, string>.GetOrAdd(System.Tuple<System.Type, string, string> key, System.Func<System.Tuple<System.Type, string, string>, string> valueFactory)
EntityFramework.dll!System.Data.Entity.Utilities.DbConnectionExtensions.GetProviderInfo(System.Data.Common.DbConnection connection, out System.Data.Entity.Core.Common.DbProviderManifest providerManifest)
EntityFramework.dll!System.Data.Entity.DbModelBuilder.Build(System.Data.Common.DbConnection providerConnection)
EntityFramework.dll!System.Data.Entity.Internal.LazyInternalContext.CreateModel(System.Data.Entity.Internal.LazyInternalContext internalContext)
EntityFramework.dll!System.Data.Entity.Internal.RetryLazy<System.Data.Entity.Internal.LazyInternalContext, System.Data.Entity.Infrastructure.DbCompiledModel>.GetValue(System.Data.Entity.Internal.LazyInternalContext input)
EntityFramework.dll!System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
EntityFramework.dll!System.Data.Entity.Internal.LazyInternalContext.GetObjectContextWithoutDatabaseInitialization()
EntityFramework.dll!System.Data.Entity.Database.UseTransaction(System.Data.Common.DbTransaction transaction)

事务在会话的Open方法中创建:

代码语言:javascript
运行
复制
public virtual void Open(IsolationLevel isolation, string user)
{
    ValidateState(false);

    this.m_user = user;
    this.m_connection = this.m_database.CreateConnection();
    this.m_connection.Open();
    if (IsolationLevel.Unspecified != isolation)
    {
        this.m_transaction = this.m_connection.BeginTransaction(isolation);
    }
}

然后,该方法在支持EF的类中被过度使用:

代码语言:javascript
运行
复制
public override void Open(System.Data.IsolationLevel isolation, string user)
{
    if (isolation == System.Data.IsolationLevel.Unspecified)
    {
        throw new InvalidOperationException("Isolation level 'Unspecified' is not supported");
    }
    base.Open(isolation, user);
    this.DbContext = this.DbContextFactory.CreateContext<TContext>(this.Connection);
    this.DbContext.Database.UseTransaction(this.Transaction);
}
EN

回答 2

Stack Overflow用户

发布于 2021-04-05 00:13:53

在初始化期间,DdContext将确定Server的版本。此过程将尝试连接到底层服务器,并使用提供的连接查询select cast (serverproperty ('EngineEdition') as int),或者在没有连接的情况下,从配置的连接字符串创建一个新的连接。

这将只发生一次,在第一次初始化之后。

因此,如果您在应用程序生命周期中第一次使用DbContext来自一个事务,这将导致DbContext尝试使用此连接并导致观察到的错误。

如果您确保首次对DbContext (用于查询的DbContext构造和使用)进行非事务性调用,则将避免此行为。

代码语言:javascript
运行
复制
    private static object _syncRoot = new object();
    private static bool _Initialized = false;

    private MyDbContext(string connectionString) : base(connectionString)
    {
        Database.SetInitializer<MyDbContext>(null);
    }

    private MyDbContext(DbTransaction dbTransaction) : base(dbTransaction.Connection, false)
    {
        Database.SetInitializer<MyDbContext>(null);
        Database.UseTransaction(dbTransaction);
    }

    public static MyDbContext Factory()
    {
        return new MyDbContext(Tools.GetDefaultConnectionString());
    }

    public static MyDbContext Factory(DbTransaction dbTransaction)
    {
        if(_Initialized==false)
        {
            lock(_syncRoot)
            {
                if(_Initialized==false)
                {
                    using (MyDbContext tempDbContext = new MyDbContext(dbTransaction.Connection.ConnectionString))
                    using (System.Data.Common.DbTransaction temptransaction = tempDbContext.BeginTransaction())
                    {
                        var mySampleData = tempDbContext.OneRowTable.AsNoTracking().ToList();
                        temptransaction.Commit();
                        _Initialized = true;
                    }
                }
            }
        }
        MyDbContext finalContext = new MyDbContext(dbTransaction);
        return finalContext;
    }

这是一个解决方案,在一个场景中,您不能或不希望在软件中使用TransactionScope

票数 3
EN

Stack Overflow用户

发布于 2021-03-26 00:32:21

这个问题出现在第一次访问底层ObjectContext时(试图设置事务访问底层上下文)。使用TransactionScope很好;当使用已经与其关联的本地事务的连接时,就会出现此问题。

虽然有点难看,像下面这样的方法确实可以解决这个问题。具体的实现细节,如确定notCreateYet,留作练习。

代码语言:javascript
运行
复制
if (notCreatedYet)
{
    // Create a second session/connection to not touch the incoming connection.
    // Using a TransactionScope works and proper enlistment is done.
    // Attempts to open a manual transaction prior to creation will fail.
    using (new TransactionScope(TransactionScopeOption.RequiresNew))
    using (var initConn = new SqlConnection(connection.ConnectionString))
    {
        initConn.Open();
        var tempContext = this.DbContextFactory.CreateContext<TContext>(connection);
        // Touch the object context.
        if (tempContext is IObjectContextAdapter contextAdapter)
        {
            _ = contextAdapter.ObjectContext;
        }
    }
}

// Then later on this is fine.
this.DbContext = this.DbContextFactory.CreateContext<TContext>(connection);
this.DbContext.Database.UseTransaction(transaction);
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38082180

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档