【问题标题】:There is already an open DataReader associated with this Command which must be closed first已经有一个打开的 DataReader 与此命令关联,必须先关闭
【发布时间】:2011-08-29 01:33:07
【问题描述】:

我有这个查询,我在这个函数中得到错误:

var accounts = from account in context.Accounts
               from guranteer in account.Gurantors
               select new AccountsReport
               {
                   CreditRegistryId = account.CreditRegistryId,
                   AccountNumber = account.AccountNo,
                   DateOpened = account.DateOpened,
               };

 return accounts.AsEnumerable()
                .Select((account, index) => new AccountsReport()
                    {
                        RecordNumber = FormattedRowNumber(account, index + 1),
                        CreditRegistryId = account.CreditRegistryId,
                        DateLastUpdated = DateLastUpdated(account.CreditRegistryId, account.AccountNumber),
                        AccountNumber = FormattedAccountNumber(account.AccountType, account.AccountNumber)
                    })
                .OrderBy(c=>c.FormattedRecordNumber)
                .ThenByDescending(c => c.StateChangeDate);


public DateTime DateLastUpdated(long creditorRegistryId, string accountNo)
{
    return (from h in context.AccountHistory
            where h.CreditorRegistryId == creditorRegistryId && h.AccountNo == accountNo
            select h.LastUpdated).Max();
}

错误是:

已经有一个打开的 DataReader 与此命令关联,必须先关闭它。

更新:

添加了堆栈跟踪:

InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.]
   System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command) +5008639
   System.Data.SqlClient.SqlConnection.ValidateConnectionForExecute(String method, SqlCommand command) +23
   System.Data.SqlClient.SqlCommand.ValidateCommand(String method, Boolean async) +144
   System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) +87
   System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) +32
   System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) +141
   System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) +12
   System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior) +10
   System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior) +443

[EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details.]
   System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior) +479
   System.Data.Objects.Internal.ObjectQueryExecutionPlan.Execute(ObjectContext context, ObjectParameterCollection parameterValues) +683
   System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption) +119
   System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() +38
   System.Linq.Enumerable.Single(IEnumerable`1 source) +114
   System.Data.Objects.ELinq.ObjectQueryProvider.<GetElementFunction>b__3(IEnumerable`1 sequence) +4
   System.Data.Objects.ELinq.ObjectQueryProvider.ExecuteSingle(IEnumerable`1 query, Expression queryRoot) +29
   System.Data.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute(Expression expression) +91
   System.Data.Entity.Internal.Linq.DbQueryProvider.Execute(Expression expression) +69
   System.Linq.Queryable.Max(IQueryable`1 source) +216
   CreditRegistry.Repositories.CreditRegistryRepository.DateLastUpdated(Int64 creditorRegistryId, String accountNo) in D:\Freelance Work\SuperExpert\CreditRegistry\CreditRegistry\Repositories\CreditRegistryRepository.cs:1497
   CreditRegistry.Repositories.CreditRegistryRepository.<AccountDetails>b__88(AccountsReport account, Int32 index) in D:\Freelance Work\SuperExpert\CreditRegistry\CreditRegistry\Repositories\CreditRegistryRepository.cs:1250
   System.Linq.<SelectIterator>d__7`2.MoveNext() +198
   System.Linq.Buffer`1..ctor(IEnumerable`1 source) +217
   System.Linq.<GetEnumerator>d__0.MoveNext() +96

【问题讨论】:

  • 我有一个查询填充 ASP.NET MVC Kendo UI 数据网格时遇到此错误,我不小心对初始查询的每一行都有额外的查询。 See my similar answer here。解决方案是摆脱每行的额外查询。这也大大提高了性能。

标签: c# entity-framework entity-framework-4


【解决方案1】:

如果您在迭代另一个查询的结果时执行查询,就会发生这种情况。从您的示例中不清楚发生这种情况的位置,因为示例不完整。

可能导致这种情况的原因之一是在迭代某些查询的结果时触发了延迟加载。

这可以通过在连接字符串中允许 MARS 轻松解决。将MultipleActiveResultSets=true 添加到连接字符串的提供者部分(其中指定了数据源、初始目录等)。

【讨论】:

  • 这对我有用。如果您想了解有关启用多个活动结果集 (MARS) 的更多信息,请参阅 msdn.microsoft.com/en-us/library/h32h3abf(v=vs.100).aspx。考虑阅读 MARS 的缺点stackoverflow.com/questions/374444/…
  • 考虑到性能,您还可以通过包含 System.Data.Entity 然后使用 Include 语句来确保在原始查询中加载此辅助数据来解决此问题。如果您启用 MARS,将其关闭以检查这些重复的数据加载可以通过减少往返次数来帮助加快数据处理调用。
  • 启用 MARS 应该只针对非常小的问题/用例子集。在大多数情况下,有问题的错误是由调用应用程序中的 BAD CODE 引起的。更多细节在这里:devproconnections.com/development/…
  • 在 your.Include().Where() 之后添加 .ToList() 可能会解决问题。
  • 为一个查询进行全局 SQL 连接范围的更改是荒谬的。正确的答案应该是下面的 ToList 之一。针对本地化问题的本地修复(即只需更改查询)!
【解决方案2】:

您可以在return 语句之前使用ToList() 方法。

var accounts =
from account in context.Accounts
from guranteer in account.Gurantors

select new AccountsReport
{
    CreditRegistryId = account.CreditRegistryId,
    AccountNumber = account.AccountNo,
    DateOpened = account.DateOpened,
};

return accounts.AsEnumerable()
       .Select((account, index) => new AccountsReport()
       {
           RecordNumber = FormattedRowNumber(account, index + 1),
           CreditRegistryId = account.CreditRegistryId,
           DateLastUpdated = DateLastUpdated(account.CreditRegistryId, account.AccountNumber),
            AccountNumber = FormattedAccountNumber(account.AccountType, account.AccountNumber)
       })
       .OrderBy(c=>c.FormattedRecordNumber)
       .ThenByDescending(c => c.StateChangeDate)
       .ToList();


public DateTime DateLastUpdated(long creditorRegistryId, string accountNo)
{
    var dateReported = (from h in context.AccountHistory
                        where h.CreditorRegistryId == creditorRegistryId && h.AccountNo == accountNo
                        select h.LastUpdated).Max();
    return dateReported;
}

【讨论】:

  • 我现在已经多次遇到这个错误......而且每次我都忘记了!问题的答案总是使用 ToList()。
  • 这有什么缺点吗?如果你有 100k 行,我怀疑这会不会很好。
  • @MartinMazzaDawson ,您真的需要一次查询执行 10 万条记录吗?我认为,在这种情况下使用分页是个好主意
  • 很抱歉提出一个老话题,但我在开发 RepositoryPattern 时遇到了同样的错误,我通过在存储库。一开始我只是返回“.AsEnumerable()”。现在我的问题是:存储库是否应该返回 "ToList()" ,或者这是最终消费者需要的东西(即:服务/业务逻辑)
  • 为我工作。添加 .ToList 解决了 JetEntityFrameworkProvider 中的小数支持问题。 Total = storeDb.OF_Carts.Where(x =&gt; x.CartId == ShoppingCartId).ToList().Sum(t =&gt; t.Quantity * t.Item.UnitPrice);
【解决方案3】:

使用语法.ToList() 将从数据库读取的对象转换为列表以避免再次被重新读取。希望这对它有用。谢谢。

【讨论】:

    【解决方案4】:

    这是一个工作连接字符串,供需要参考的人参考。

    <connectionStrings>
      <add name="IdentityConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\IdentityDb.mdf;Integrated Security=True;MultipleActiveResultSets=true;" providerName="System.Data.SqlClient" />
    </connectionStrings>
    

    【讨论】:

    • 启用 MARS 是一种解决方法,而不是问题的解决方案。
    • 来自 MARS 文档页面:“MARS 操作不是线程安全的。”这意味着,如果问题是由访问 Context 的多个线程引起的,那么 MARS(可能)不是解决方案。
    • "MultipleActiveResultSets=true" 就像一个魅力。
    【解决方案5】:

    在我的例子中,使用Include() 解决了这个错误,并且根据情况可能比发出多个查询要高效得多,因为它可以通过连接一次查询。

    IEnumerable<User> users = db.Users.Include("Projects.Tasks.Messages");
    
    foreach (User user in users)
    {
        Console.WriteLine(user.Name);
        foreach (Project project in user.Projects)
        {
            Console.WriteLine("\t"+project.Name);
            foreach (Task task in project.Tasks)
            {
                Console.WriteLine("\t\t" + task.Subject);
                foreach (Message message in task.Messages)
                {
                    Console.WriteLine("\t\t\t" + message.Text);
                }
            }
        }
    }
    

    【讨论】:

    • 如果您的应用程序不需要 MARS,这是最好的解决方案。
    【解决方案6】:

    我不知道这是否是重复的答案。如果是我很抱歉。我只是想让有需要的人知道我是如何使用ToList() 解决我的问题的。

    在我的情况下,我在下面的查询中遇到了同样的异常。

    int id = adjustmentContext.InformationRequestOrderLinks.Where(
                 item => item.OrderNumber == irOrderLinkVO.OrderNumber 
                      && item.InformationRequestId == irOrderLinkVO.InformationRequestId)
                 .Max(item => item.Id);
    

    我像下面这样解决了

    List<Entities.InformationRequestOrderLink> links = 
          adjustmentContext.InformationRequestOrderLinks
               .Where(item => item.OrderNumber == irOrderLinkVO.OrderNumber 
                           && item.InformationRequestId == irOrderLinkVO.InformationRequestId)
               .ToList();
    
    int id = 0;
    
    if (links.Any())
    {
      id = links.Max(x => x.Id);
    }
    if (id == 0)
    {
    //do something here
    }
    

    【讨论】:

    • 如果您已经遇到 MARS 问题,我推荐这种方法。
    【解决方案7】:

    您似乎正在使用相同的 EF 上下文从活动查询中调用 DateLastUpdated,并且 DateLastUpdate 向数据存储本身发出命令。 Entity Framework 一次只支持每个上下文一个活动命令。

    您可以像这样将上述两个查询重构为一个:

    return accounts.AsEnumerable()
           .Select((account, index) => new AccountsReport()
           {
             RecordNumber = FormattedRowNumber(account, index + 1),
             CreditRegistryId = account.CreditRegistryId,
             DateLastUpdated = (
                 from h in context.AccountHistory 
                 where h.CreditorRegistryId == creditorRegistryId && h.AccountNo == accountNo 
                 select h.LastUpdated
             ).Max(),
             AccountNumber = FormattedAccountNumber(account.AccountType, account.AccountNumber)
           })
           .OrderBy(c=>c.FormattedRecordNumber)
           .ThenByDescending(c => c.StateChangeDate);
    

    我还注意到您在查询中调用了 FormattedAccountNumberFormattedRecordNumber 之类的函数。除非这些是您从数据库导入实体数据模型并正确映射的存储过程或函数,否则这些也会抛出异常,因为 EF 不知道如何将这些函数转换为它可以发送到数据存储的语句。

    另外请注意,调用AsEnumerable 不会强制执行查询。直到查询执行被推迟到枚举为止。如果您愿意,可以使用 ToListToArray 强制枚举。

    【讨论】:

    • 如果您愿意,您可以重构您正在执行的查询,将 DateLastUpdated 直接放入您的 Select 投影中以进行 Accounts Report 查询,并在没有错误的情况下获得所需的效果。
    • 将函数代码放入主查询后出现同样的错误
    【解决方案8】:

    在我的例子中,我从数据上下文中打开了一个查询,比如

        Dim stores = DataContext.Stores _
            .Where(Function(d) filter.Contains(d.code)) _
    

    ...然后随后查询相同的...

        Dim stores = DataContext.Stores _
            .Where(Function(d) filter.Contains(d.code)).ToList
    

    .ToList 添加到第一个解决了我的问题。我认为将其包装在如下属性中是有意义的:

    Public ReadOnly Property Stores As List(Of Store)
        Get
            If _stores Is Nothing Then
                _stores = DataContext.Stores _
                    .Where(Function(d) Filters.Contains(d.code)).ToList
            End If
            Return _stores
        End Get
    End Property
    

    其中 _stores 是私有变量,Filters 也是从 AppSettings 读取的只读属性。

    【讨论】:

      【解决方案9】:

      除了Ladislav Mrnka的回答:

      如果您在 Settings 选项卡上发布和覆盖容器,则可以将 MultipleActiveResultSet 设置为 True。您可以通过单击 Advanced... 找到此选项,它将位于 Advanced 组下。

      【讨论】:

        【解决方案10】:

        我通过更改解决了这个问题 等待 _accountSessionDataModel.SaveChangesAsync(); 到 _accountSessionDataModel.SaveChanges(); 在我的 Repository 类中。

         public async Task<Session> CreateSession()
            {
                var session = new Session();
        
                _accountSessionDataModel.Sessions.Add(session);
                await _accountSessionDataModel.SaveChangesAsync();
             }
        

        改成:

         public Session CreateSession()
            {
                var session = new Session();
        
                _accountSessionDataModel.Sessions.Add(session);
                _accountSessionDataModel.SaveChanges();
             }
        

        问题是我在创建会话后(在代码中)更新了前端的会话,但是由于 SaveChangesAsync 异步发生,获取会话导致此错误,因为显然 SaveChangesAsync 操作尚未准备好。

        【讨论】:

          【解决方案11】:

          对于那些通过 Google 找到此内容的人;
          我收到此错误是因为,正如错误所暗示的,我未能在同一个 SqlCommand 上创建另一个 SqlDataReader 之前关闭一个 SqlDataReader,错误地假设在离开创建它的方法时它会被垃圾收集。

          我在创建第二个阅读器之前通过调用sqlDataReader.Close(); 解决了这个问题。

          【讨论】:

            【解决方案12】:

            附带说明...当 SQL 对象的(内部)数据映射出现问题时,也会发生这种情况。

            比如……

            我创建了一个SQL Scalar Function意外地返回了一个VARCHAR...然后...用它在VIEW 中生成了一个列。 VIEW 已正确映射到 DbContext...所以 Linq 可以正常调用它。但是,Entity 期望 DateTime? 并且 VIEW 正在返回 String

            奇怪的抛出...

            "已经有一个打开的 DataReader 与此命令关联 必须先关闭”

            很难弄清楚...但是在我更正了返回参数后...一切都很好

            【讨论】:

              【解决方案13】:

              当我尝试在读取循环中更新一些记录时,我遇到了同样的错误。 我尝试了投票最多的答案MultipleActiveResultSets=true,发现这只是解决下一个错误的方法

              不允许新事务,因为有其他线程在运行 在会话中

              适用于大型结果集的最佳方法是使用块并为每个块打开单独的上下文,如中所述 SqlException from Entity Framework - New transaction is not allowed because there are other threads running in the session

              【讨论】:

                【解决方案14】:

                对我来说,这是我自己的错误。当我应该使用SqlCommand.ExecuteNonQuery() 时,我试图使用SqlCommand.executeReader() 运行INSERT。它被打开并且从未关闭,导致错误。当心这种疏忽。

                【讨论】:

                • 我这边也是同样的问题。我需要 SqlCommand.executeReader() 因为我得到了插入的行 ID。所以:我使用了 SqlDataReader.Close(); Sql Command.Dispose();谢谢@Andrew Taylor
                【解决方案15】:

                这是从现实世界场景中提取的:

                • 代码在连接字符串中设置了 MultipleActiveResultSets 的 Stage 环境中运行良好
                • 在没有 MultipleActiveResultSets=true 的情况下发布到生产环境的代码
                • 很多页面/调用都在工作,而一个页面/调用却失败了
                • 仔细观察调用,对数据库进行了不必要的调用,需要删除
                • 在生产环境中设置 MultipleActiveResultSets=true 并发布清理后的代码,一切运行良好且高效

                总之,不要忘记 MultipleActiveResultSets,代码可能已经运行了很长时间,然后才发现可能非常昂贵的冗余 db 调用,我建议不要完全依赖于设置 MultipleActiveResultSets 属性,还要找出为什么代码在失败的地方需要它

                【讨论】:

                  【解决方案16】:

                  这个问题很可能是由于实体框架的“延迟加载”功能而发生的。通常,除非在初始获取期间明确要求,否则仅在需要时获取所有连接数据(存储在其他数据库表中的任何内容)。在许多情况下,这是一件好事,因为它可以防止获取不必要的数据,从而提高查询性能(无连接)并节省带宽。

                  在问题中描述的情况下,执行初始获取,并且在“选择”阶段缺少延迟加载数据请求,发出额外的查询,然后 EF 抱怨“打开 DataReader”。

                  接受的答案中提出的解决方法将允许执行这些查询,实际上整个请求都会成功。

                  但是,如果您检查发送到数据库的请求,您会注意到多个请求 - 每个丢失(延迟加载)数据的附加请求。这可能是性能杀手。

                  更好的方法是告诉 EF 在初始查询期间预加载所有需要的延迟加载数据。这可以使用“包含”语句来完成:

                  using System.Data.Entity;
                  
                  query = query.Include(a => a.LazyLoadedProperty);
                  

                  这样,所有需要的连接都将被执行,所有需要的数据都将作为单个查询返回。问题中描述的问题将得到解决。

                  【讨论】:

                  • 这是一个有效的答案,因为我从使用 Include 转到使用 EntityEntry.Collection().Load(),而我的解决方案从工作变成了损坏。不幸的是,包含一个泛型不能“然后包含”另一个泛型,所以我仍在尝试使 EntityEntry.Collection().Load() 工作。
                  【解决方案17】:

                  我在我的工具中使用 Web 服务,这些服务获取存储过程。当更多数量的客户端工具获取 Web 服务时,就会出现这个问题。我已经通过为这些函数指定 Synchronized 属性来获取存储过程。现在它工作正常,错误从未出现在我的工具中。

                   [MethodImpl(MethodImplOptions.Synchronized)]
                   public static List<t> MyDBFunction(string parameter1)
                    {
                    }
                  

                  此属性允许一次处理一个请求。所以这解决了这个问题。

                  【讨论】:

                    【解决方案18】:

                    就我而言,我必须在连接字符串中将MultipleActiveResultSets 设置为True
                    然后出现了另一个错误(真正的错误),即无法在同一数据上下文中同时运行 2 个(SQL)命令! (EF Core,代码优先)
                    所以我的解决方案是寻找任何其他异步命令执行并将它们转换为同步,因为我只有一个 DbContext 用于两个命令。

                    希望对你有帮助

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2012-02-19
                      • 1970-01-01
                      • 2017-07-15
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多