Sudah ada DataReader terbuka yang terkait dengan Perintah ini yang harus ditutup terlebih dahulu

641

Saya memiliki permintaan ini dan saya mendapatkan kesalahan dalam fungsi ini:

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

Kesalahan adalah:

Sudah ada DataReader terbuka yang terkait dengan Perintah ini yang harus ditutup terlebih dahulu.

Memperbarui:

tumpukan jejak ditambahkan:

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
DotnetSparrow
sumber

Jawaban:

1288

Ini bisa terjadi jika Anda menjalankan kueri saat mengulangi hasil dari kueri lain. Tidak jelas dari contoh Anda di mana ini terjadi karena contohnya tidak lengkap.

Satu hal yang dapat menyebabkan hal ini adalah pemuatan malas yang dipicu ketika mengulangi hasil beberapa permintaan.

Ini dapat dengan mudah dipecahkan dengan mengizinkan MARS dalam string koneksi Anda. Tambahkan MultipleActiveResultSets=trueke bagian penyedia string koneksi Anda (di mana Sumber Data, Katalog Awal, dll. Ditentukan).

Ladislav Mrnka
sumber
34
Ini berhasil untuk saya. Jika Anda ingin membaca lebih lanjut tentang Mengaktifkan Beberapa Set Hasil Aktif (MARS) lihat msdn.microsoft.com/en-us/library/h32h3abf(v=vs.100).aspx . Pertimbangkan untuk membaca tentang Kerugian MARS juga stackoverflow.com/questions/374444/...
Diganta Kumar
3
Dengan mempertimbangkan kinerja, Anda juga dapat menyelesaikan ini dengan menyertakan System.Data.Entity dan kemudian menggunakan pernyataan Sertakan untuk memastikan data sekunder ini dimuat dalam kueri asli. Jika Anda mengaktifkan MARS, mematikannya untuk memeriksa pemuatan data yang berulang ini dapat membantu mempercepat panggilan pemrosesan data Anda dengan mengurangi perjalanan bolak-balik.
Chris Moschini
70
Mengaktifkan MARS seharusnya hanya dilakukan untuk sebagian kecil dari masalah / kasus penggunaan. Dalam kebanyakan kasus, kesalahan yang dimaksud disebabkan oleh BAD CODE dalam aplikasi panggilan. Lebih detail di sini: devproconnections.com/development/…
Michael K. Campbell
132
Menambahkan .ToList () setelah your.Include (). Where () kemungkinan akan menyelesaikan masalah.
Serj Sagan
2
Untuk membuat perubahan koneksi global SQL lebar untuk satu permintaan adalah konyol. Jawaban yang benar adalah ToList di bawah ini. Perbaikan lokal (yaitu hanya mengubah kueri) untuk masalah yang dilokalkan!
bytedev
218

Anda dapat menggunakan ToList()metode ini sebelum returnpernyataan.

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;
    }
Kazem
sumber
9
Saya telah mengalami kesalahan ini berkali-kali sekarang ... dan setiap kali saya lupa! Jawaban atas pertanyaan adalah selalu menggunakan ToList ().
Cheesus Toast
1
Apakah ada kerugian untuk ini? Jika Anda memiliki 100 ribu baris, saya ragu ini bagus.
Martin Dawson
2
@ MartinMazzaDawson, Anda benar-benar membutuhkan 100 ribu catatan sekaligus eksekusi permintaan ?? Saya pikir, menggunakan pagination adalah ide yang bagus untuk situasi ini
kazem
maaf untuk mengangkat topik lama tetapi saya mengalami kesalahan yang sama saat mengembangkan RepositoryPattern dan saya menyelesaikannya dengan menambahkan ".ToList () atau Single () atau Count ()" ke setiap metode Repositori. Sementara pada awalnya saya baru saja mengembalikan ".AsEnumerable ()". Sekarang pertanyaan saya adalah: apakah repositori mengembalikan "ToList ()", atau apakah ini sesuatu yang harus dituntut kepada konsumen akhir (yaitu: logika layanan / bisnis)
alessalessio
Bekerja untukku. Menambahkan .ToList memecahkan masalah masalah dukungan Desimal di JetEntityFrameworkProvider. Total = storeDb.OF_Carts.Where(x => x.CartId == ShoppingCartId).ToList().Sum(t => t.Quantity * t.Item.UnitPrice);
hubert17
39

gunakan sintaks .ToList()untuk mengonversi objek yang dibaca dari db ke daftar untuk menghindari dibaca ulang lagi. Semoga ini bisa berhasil. Terima kasih.

Icemark Muturi
sumber
22

Berikut adalah string koneksi yang berfungsi untuk seseorang yang membutuhkan referensi.

  <connectionStrings>
    <add name="IdentityConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\IdentityDb.mdf;Integrated Security=True;MultipleActiveResultSets=true;" providerName="System.Data.SqlClient" />
  </connectionStrings>
Yang Zhang
sumber
15
Mengaktifkan MARS adalah solusi, BUKAN solusi untuk masalah tersebut.
SandRock
5
Dari halaman Dokumentasi MARS: "Operasi MARS tidak aman untuk thread." Itu berarti, jika masalah muncul dari beberapa utas mengakses Konteks, MARS (mungkin) bukan solusi.
marsop
20

Dalam kasus saya, menggunakan Include()menyelesaikan kesalahan ini dan bergantung pada situasinya dapat menjadi jauh lebih efisien daripada mengeluarkan beberapa kueri ketika semuanya dapat ditanyakan sekaligus dengan bergabung.

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);
            }
        }
    }
}
Despertar
sumber
Ini adalah solusi terbaik jika aplikasi Anda tidak memerlukan MARS.
Fred Wilson
7

Saya tidak tahu apakah ini jawaban ganda atau tidak. Jika itu saya minta maaf. Saya hanya ingin memberi tahu orang yang membutuhkan bagaimana saya memecahkan masalah saya menggunakan ToList ().

Dalam kasus saya, saya mendapat pengecualian yang sama untuk kueri di bawah ini.

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

Saya memecahkan seperti di bawah ini

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
}
Ziggler
sumber
5

Tampaknya Anda memanggil DateLastUpdated dari dalam kueri aktif menggunakan konteks EF yang sama dan DateLastUpdate mengeluarkan perintah ke penyimpanan data itu sendiri. Entity Framework hanya mendukung satu perintah aktif per konteks pada suatu waktu.

Anda dapat mengubah dua kueri di atas menjadi satu seperti ini:

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

Saya juga memperhatikan Anda memanggil fungsi seperti FormattedAccountNumber dan FormattedRecordNumber di kueri. Kecuali jika ini disimpan procs atau fungsi yang Anda impor dari basis data Anda ke dalam model data entitas dan dipetakan dengan benar, ini juga akan membuang pengecualian karena EF tidak akan tahu bagaimana menerjemahkan fungsi-fungsi itu ke dalam pernyataan yang dapat dikirim ke penyimpanan data.

Perhatikan juga, memanggil AsEnumerable tidak memaksa permintaan untuk mengeksekusi. Sampai eksekusi permintaan ditunda sampai disebutkan. Anda dapat memaksakan enumerasi dengan ToList atau ToArray jika diinginkan.

James Alexander
sumber
Jika mau, Anda dapat menolak permintaan yang Anda lakukan untuk mengarahkan DateLastUpdated langsung ke proyeksi Pilih Anda untuk permintaan Laporan Akun dan mendapatkan efek yang diinginkan tanpa kesalahan.
James Alexander
Saya mendapatkan kesalahan yang sama setelah memasukkan kode fungsi di dalam permintaan utama
DotnetSparrow
2

Selain jawaban Ladislav Mrnka :

Jika Anda menerbitkan dan mengganti wadah pada tab Pengaturan , Anda dapat mengatur MultipleActiveResultSet ke True. Anda dapat menemukan opsi ini dengan mengklik Advanced ... dan itu akan berada di bawah grup Advanced .

Alexander Troshchenko
sumber
2

Bagi mereka yang menemukan ini melalui Google;
Saya mendapatkan kesalahan ini karena, seperti yang disarankan oleh kesalahan, saya gagal menutup SqlDataReader sebelum membuat yang lain di SqlCommand yang sama, keliru dengan menganggap bahwa itu akan menjadi sampah yang dikumpulkan ketika meninggalkan metode yang telah dibuat.

Saya memecahkan masalah dengan menelepon sqlDataReader.Close();sebelum membuat pembaca kedua.

penentu waktu
sumber
2

Dalam kasus saya, saya telah membuka kueri dari konteks data, seperti

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

... dan kemudian menanyakan hal yang sama ...

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

Menambahkan .ToListke yang pertama menyelesaikan masalah saya. Saya pikir masuk akal untuk membungkus ini di properti seperti:

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

Di mana _stores adalah variabel pribadi, dan Filter juga properti baca yang dibaca dari AppSettings.

Adam Cox
sumber
1

Saya memiliki kesalahan yang sama, ketika saya mencoba memperbarui beberapa catatan dalam loop baca. Saya sudah mencoba jawaban yang paling banyak memilih MultipleActiveResultSets=truedan menemukan, bahwa itu hanya solusi untuk mendapatkan kesalahan berikutnya 

Transaksi baru tidak diizinkan karena ada utas lain yang berjalan di sesi

Pendekatan terbaik, yang akan bekerja untuk ResultSets yang sangat besar adalah dengan menggunakan potongan dan membuka konteks terpisah untuk setiap potongan seperti yang dijelaskan dalam  SqlException dari Entity Framework - Transaksi baru tidak diperbolehkan karena ada utas lain yang berjalan di sesi

Michael Freidgeim
sumber
1

Saya memecahkan masalah ini dengan mengubah tunggu _accountSessionDataModel.SaveChangesAsync (); ke _accountSessionDataModel.SaveChanges (); di kelas Repositori saya.

 public async Task<Session> CreateSession()
    {
        var session = new Session();

        _accountSessionDataModel.Sessions.Add(session);
        await _accountSessionDataModel.SaveChangesAsync();
     }

Mengubahnya menjadi:

 public Session CreateSession()
    {
        var session = new Session();

        _accountSessionDataModel.Sessions.Add(session);
        _accountSessionDataModel.SaveChanges();
     }

Masalahnya adalah saya memperbarui Sesi di frontend setelah membuat sesi (dalam kode), tetapi karena SaveChangesAsync terjadi secara tidak sinkron, mengambil sesi menyebabkan kesalahan ini karena tampaknya operasi SaveChangesAsync belum siap.

woutercx
sumber
1

Bagi saya itu adalah bug saya sendiri. Saya mencoba menjalankan INSERTpenggunaan SqlCommand.executeReader()ketika saya seharusnya menggunakan SqlCommand.ExecuteNonQuery(). Itu dibuka dan tidak pernah ditutup, menyebabkan kesalahan. Hati-hati dengan pengawasan ini.

Andrew Taylor
sumber
Itu masalah yang sama dari sisi saya. Saya membutuhkan SqlCommand.executeReader () karena saya mendapatkan ID baris yang dimasukkan. Jadi: Saya menggunakan SqlDataReader.Close (); Sql Command.Dispose (); Terima kasih @Andrew Taylor
Fuat
1

Ini diambil dari skenario dunia nyata:

  • Kode berfungsi dengan baik di lingkungan Panggung dengan MultipleActiveResultSets diatur dalam string koneksi
  • Kode diterbitkan ke lingkungan Produksi tanpa MultipleActiveResultSets = true
  • Begitu banyak halaman / panggilan berfungsi saat satu saja gagal
  • Melihat lebih dekat pada panggilan itu, ada panggilan yang tidak perlu dilakukan ke db dan perlu dihapus
  • Set MultipleActiveResultSets = true dalam Produksi dan publikasikan kode pembersihan, semuanya bekerja dengan baik dan efisien

Sebagai kesimpulan, tanpa melupakan MultipleActiveResultSets, kode tersebut mungkin telah berjalan lama sebelum menemukan panggilan db yang berlebihan yang bisa sangat mahal, dan saya menyarankan untuk tidak sepenuhnya bergantung pada pengaturan atribut MultipleActiveResultSets tetapi juga mencari tahu mengapa kode membutuhkannya. dimana gagal .

BermanfaatBee
sumber
1

Kemungkinan besar masalah ini terjadi karena fitur "lazy loading" dari Entity Framework. Biasanya, kecuali diperlukan secara eksplisit selama pengambilan awal, semua data yang digabungkan (apa pun yang disimpan dalam tabel basis data lain) diambil hanya jika diperlukan. Dalam banyak kasus itu adalah hal yang baik, karena mencegah mengambil data yang tidak perlu dan dengan demikian meningkatkan kinerja kueri (tidak ada yang bergabung) dan menghemat bandwidth.

Dalam situasi yang diuraikan dalam pertanyaan, pengambilan awal dilakukan, dan selama fase "pilih" data pemuatan malas diminta, pertanyaan tambahan dikeluarkan dan kemudian EF mengeluh tentang "buka DataReader".

Solusi yang diajukan dalam jawaban yang diterima akan memungkinkan pelaksanaan pertanyaan ini, dan memang seluruh permintaan akan berhasil.

Namun, jika Anda akan memeriksa permintaan yang dikirim ke database, Anda akan melihat beberapa permintaan - permintaan tambahan untuk setiap data yang hilang (malas dimuat). Ini mungkin pembunuh kinerja.

Pendekatan yang lebih baik adalah memberi tahu EF untuk melakukan preload semua data malas yang diperlukan selama permintaan awal. Ini dapat dilakukan dengan menggunakan pernyataan "Sertakan":

using System.Data.Entity;

query = query.Include(a => a.LazyLoadedProperty);

Dengan cara ini, semua gabungan yang dibutuhkan akan dilakukan dan semua data yang dibutuhkan akan dikembalikan sebagai satu permintaan. Masalah yang dijelaskan dalam pertanyaan akan diselesaikan.

Illidan
sumber
Ini adalah jawaban yang valid, karena saya beralih dari menggunakan Sertakan ke menggunakan EntityEntry.Collection (). Load (), dan solusi saya berubah dari bekerja menjadi rusak. Sayangnya, termasuk untuk generik tidak dapat "ThenInclude" generik lain, jadi saya masih mencoba untuk membuat EntityEntry.Collection (). Load () berfungsi.
AndrewBenjamin
0

Saya menggunakan layanan web di alat saya, di mana layanan itu mengambil prosedur yang tersimpan. sementara lebih banyak alat klien mengambil layanan web, masalah ini muncul. Saya telah memperbaiki dengan menetapkan atribut Sinkronisasi untuk fungsi-fungsi itu mengambil prosedur yang tersimpan. sekarang berfungsi dengan baik, kesalahan tidak pernah muncul di alat saya.

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

Atribut ini memungkinkan untuk memproses satu permintaan pada suatu waktu. jadi ini memecahkan Masalah.

Pranesh Janarthanan
sumber
0

Sebagai catatan ... ini juga bisa terjadi ketika ada masalah dengan (internal) pemetaan data dari SQL Objects.

Contohnya...

Saya membuat SQL Scalar Functionyang secara tidak sengaja mengembalikan VARCHAR... dan kemudian ... menggunakannya untuk menghasilkan kolom di a VIEW. Itu VIEWdipetakan dengan benar di DbContext... jadi Linq memanggilnya baik-baik saja. Namun, Entitas mengharapkan DateTime? dan StringVIEW kembali .

Yang Anehnya ...

"Sudah ada DataReader terbuka yang terkait dengan Perintah ini yang harus ditutup terlebih dahulu"

Sulit untuk mengetahuinya ... tapi setelah saya koreksi kembali parameter ... semuanya baik-baik saja

Tahanan NOL
sumber
0

Dalam kasus saya, saya harus mengatur MultipleActiveResultSetske Truedalam string koneksi.
Kemudian muncul kesalahan lain (yang asli) tentang tidak dapat menjalankan 2 (SQL) perintah pada saat yang sama dalam konteks data yang sama! (EF Core, Code first)
Jadi solusi bagi saya adalah mencari eksekusi perintah asinkron lainnya dan mengubahnya menjadi sinkron , karena saya hanya punya satu DbContext untuk kedua perintah.

Saya harap ini membantu Anda

Dr TJ
sumber