There is already an open DataReader associated with this Command which must be closed first

前端 未结 18 2295
孤街浪徒
孤街浪徒 2020-11-22 01:40

I have this query and I get the error in this function:

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


        
相关标签:
18条回答
  • 2020-11-22 02:26

    Most likely this issue happens because of "lazy loading" feature of Entity Framework. Usually, unless explicitly required during initial fetch, all joined data (anything that stored in other database tables) is fetched only when required. In many cases that is a good thing, since it prevents from fetching unnecessary data and thus improve query performance (no joins) and saves bandwidth.

    In the situation described in the question, initial fetch is performed, and during "select" phase missing lazy loading data is requested, additional queries are issued and then EF is complaining about "open DataReader".

    Workaround proposed in the accepted answer will allow execution of these queries, and indeed the whole request will succeed.

    However, if you will examine requests sent to the database, you will notice multiple requests - additional request for each missing (lazy loaded) data. This might be a performance killer.

    A better approach is to tell to EF to preload all needed lazy loaded data during the initial query. This can be done using "Include" statement:

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

    This way, all needed joins will be performed and all needed data will be returned as a single query. The issue described in the question will be solved.

    0 讨论(0)
  • 2020-11-22 02:28

    In my case, I had opened a query from data context, like

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

    ... and then subsequently queried the same...

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

    Adding the .ToList to the first resolved my issue. I think it makes sense to wrap this in a property like:

    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
    

    Where _stores is a private variable, and Filters is also a readonly property that reads from AppSettings.

    0 讨论(0)
  • 2020-11-22 02:29

    In addition to Ladislav Mrnka's answer:

    If you are publishing and overriding container on Settings tab, you can set MultipleActiveResultSet to True. You can find this option by clicking Advanced... and it's going to be under Advanced group.

    0 讨论(0)
  • 2020-11-22 02:30

    use the syntax .ToList() to convert object read from db to list to avoid being re-read again.Hope this would work for it. Thanks.

    0 讨论(0)
  • 2020-11-22 02:30

    This is extracted from a real world scenario:

    • Code works well in a Stage environment with MultipleActiveResultSets is set in the connection string
    • Code published to Production environment without MultipleActiveResultSets=true
    • So many pages/calls work while a single one is failing
    • Looking closer at the call, there is an unnecessary call made to the db and needs to be removed
    • Set MultipleActiveResultSets=true in Production and publish cleaned up code, everything works well and, efficiently

    In conclusion, without forgetting about MultipleActiveResultSets, the code might have run for a long time before discovering a redundant db call that could be very costly, and I suggest not to fully depend on setting the MultipleActiveResultSets attribute but also find out why the code needs it where it failed.

    0 讨论(0)
  • 2020-11-22 02:32

    It appears that you're calling DateLastUpdated from within an active query using the same EF context and DateLastUpdate issues a command to the data store itself. Entity Framework only supports one active command per context at a time.

    You can refactor your above two queries into one like this:

    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);
    

    I also noticed you're calling functions like FormattedAccountNumber and FormattedRecordNumber in the queries. Unless these are stored procs or functions you've imported from your database into the entity data model and mapped correct, these will also throw excepts as EF will not know how to translate those functions in to statements it can send to the data store.

    Also note, calling AsEnumerable doesn't force the query to execute. Until the query execution is deferred until enumerated. You can force enumeration with ToList or ToArray if you so desire.

    0 讨论(0)
提交回复
热议问题