Memecahkan "Instance ObjectContext telah dibuang dan tidak lagi dapat digunakan untuk operasi yang memerlukan koneksi" InvalidOperationException

123

Saya mencoba untuk mengisi GridViewmenggunakan Entity Frameworkm tetapi setiap kali saya mendapatkan kesalahan berikut:

"Pengakses properti 'LoanProduct' pada objek 'COSIS_DAL.MemberLoan' memberikan pengecualian berikut: Contoh ObjectContext telah dibuang dan tidak dapat lagi digunakan untuk operasi yang memerlukan sambungan."

Kode saya adalah:

public List<MemberLoan> GetAllMembersForLoan(string keyword)
{
    using (CosisEntities db = new CosisEntities())
    {
        IQueryable<MemberLoan> query = db.MemberLoans.OrderByDescending(m => m.LoanDate);
        if (!string.IsNullOrEmpty(keyword))
        {
            keyword = keyword.ToLower();
            query = query.Where(m =>
                  m.LoanProviderCode.Contains(keyword)
                  || m.MemNo.Contains(keyword)
                  || (!string.IsNullOrEmpty(m.LoanProduct.LoanProductName) && m.LoanProduct.LoanProductName.ToLower().Contains(keyword))
                  || m.Membership.MemName.Contains(keyword)
                  || m.GeneralMasterInformation.Description.Contains(keyword)

                  );
        }
        return query.ToList();
    }
}


protected void btnSearch_Click(object sender, ImageClickEventArgs e)
{
    string keyword = txtKeyword.Text.ToLower();
    LoanController c = new LoanController();
    List<COSIS_DAL.MemberLoan> list = new List<COSIS_DAL.MemberLoan>();
    list = c.GetAllMembersForLoan(keyword);

    if (list.Count <= 0)
    {
        lblMsg.Text = "No Records Found";
        GridView1.DataSourceID = null;
        GridView1.DataSource = null;
        GridView1.DataBind();
    }
    else
    {
        lblMsg.Text = "";
        GridView1.DataSourceID = null;   
        GridView1.DataSource = list;
        GridView1.DataBind();
    }
}

Kesalahan menyebutkan LoanProductNamekolom dari Gridview. Disebutkan: Saya menggunakan C #, ASP.net, SQL-Server 2008 sebagai DB back end.

Saya cukup baru di Entity Framework. Saya tidak mengerti mengapa saya mendapatkan kesalahan ini. Adakah yang bisa membantu saya?

Barsan
sumber
1
Apakah Anda mengakses properti navigasi dalam tampilan kisi. Jika Anda melakukannya, Anda juga perlu menyertakan tabel navigasi tersebut dalam kueri. Sepertiquery.Include("SomeOtherTable")
Nilesh
Cobalah membuat kelas proxy untuk menghosting entitas Anda atau setidaknya mengembalikan objek anonim. Dari sudut pandang saya, menggunakan ef memang memerlukan pembuatan kelas proxy untuk mengimplementasikan logika Anda, gunakan edmx seperti lapisan akses db bukan sebagai bisnis.
Gonzix
ya di gridview saya mendapatkan kolom tabel lain juga. Yang merupakan LoanProviderName.
Barsan
1
Coba db.MemberLoans.Include("LoanProduct").OrderByDescending()periksa sintaksnya karena saya tidak memiliki VS di depan saya.
Nilesh
3
Anda hanya perlu terus memasukkan semua properti navigasi yang Anda akses di luar konteks seperti db.MemberLoans.Include("LoanProduct").Include("SomeOtherTable). Periksa jawaban oleh @Tragedian dan @lazyberezovsky
Nilesh

Jawaban:

175

Secara default, Entity Framework menggunakan pemuatan lambat untuk properti navigasi. Itulah mengapa properti ini harus ditandai sebagai virtual - EF membuat kelas proxy untuk entitas Anda dan mengganti properti navigasi untuk memungkinkan pemuatan lambat. Misalnya jika Anda memiliki entitas ini:

public class MemberLoan
{
   public string LoandProviderCode { get; set; }
   public virtual Membership Membership { get; set; }
}

Kerangka Kerja Entitas akan mengembalikan proxy yang diwarisi dari entitas ini dan memberikan instance DbContext ke proxy ini untuk memungkinkan pemuatan keanggotaan yang lambat nanti:

public class MemberLoanProxy : MemberLoan
{
    private CosisEntities db;
    private int membershipId;
    private Membership membership;

    public override Membership Membership 
    { 
       get 
       {
          if (membership == null)
              membership = db.Memberships.Find(membershipId);
          return membership;
       }
       set { membership = value; }
    }
}

Jadi, entitas memiliki turunan DbContext yang digunakan untuk memuat entitas. Itu masalahmu. Anda telah usingmemblokir penggunaan CosisEntities. Yang membuang konteks sebelum entitas dikembalikan. Ketika beberapa kode kemudian mencoba menggunakan properti navigasi yang dimuat lambat, gagal, karena konteks dibuang pada saat itu.

Untuk memperbaiki perilaku ini, Anda dapat menggunakan eager loading dari properti navigasi yang Anda perlukan nanti:

IQueryable<MemberLoan> query = db.MemberLoans.Include(m => m.Membership);

Itu akan memuat semua keanggotaan dan pemuatan lambat tidak akan digunakan. Untuk detailnya lihat Memuat artikel Entitas Terkait di MSDN.

Sergey Berezovskiy
sumber
Terima kasih banyak atas penjelasan dan jawaban Anda yang bermanfaat. Sebenarnya di sini saya memasukkan tiga tabel jadi saya tidak tahu bagaimana saya bisa menambahkan tiga tabel dengan TERMASUK. bisakah Anda membantu saya dalam hal ini.
Barsan
8
@barsan cukup menyertakan semua properti navigasi satu per satu. Misalnya db.MemberLoans.Include(m => m.Membership).Include(m => m.LoanProduct).OrderByDescending(m => m.LoanDate);yang akan menghasilkan kueri JOIN dan mengembalikan semua data sekaligus.
Sergey Berezovskiy
1
Terima kasih banyak lazyberezovsky. Saya sangat berterima kasih kepada Anda. Anda menyelamatkan saya hampir sehari. Dari penjelasan Anda, saya belajar lebih banyak tentang Entity Framework. Terimakasih temanku.
Barsan
Terima kasih sobat, sempurna. Saya memiliki pernyataan penggunaan yang membatasi pemuatan lambat. Jawaban yang bagus.
ncbl
4
Bagaimana jika saya sama sekali tidak ingin menyertakan entitas terkait tersebut dalam kueri saya?
Ortund
32

The CosisEntitieskelas Anda DbContext. Saat Anda membuat konteks dalam usingblok, Anda menentukan batas untuk operasi berorientasi data Anda.

Di kode Anda, Anda mencoba mengeluarkan hasil kueri dari suatu metode dan kemudian mengakhiri konteks di dalam metode tersebut. Operasi yang Anda berikan hasilnya untuk kemudian mencoba mengakses entitas untuk mengisi tampilan kisi. Di suatu tempat dalam proses pengikatan ke kisi, properti yang dimuat lambat sedang diakses dan Entity Framework mencoba melakukan pencarian untuk mendapatkan nilai. Gagal, karena konteks terkait telah berakhir.

Anda memiliki dua masalah:

  1. Anda malas memuat entitas saat mengikat ke kisi. Ini berarti Anda melakukan banyak operasi kueri terpisah ke SQL Server, yang akan memperlambat semuanya. Anda dapat memperbaiki masalah ini dengan membuat properti terkait eager-loaded secara default, atau meminta Entity Framework untuk menyertakannya dalam hasil kueri ini dengan menggunakan Includemetode ekstensi.

  2. Anda mengakhiri konteks Anda sebelum waktunya: a DbContextharus tersedia di seluruh unit pekerjaan yang sedang dilakukan, hanya membuangnya saat Anda selesai dengan pekerjaan yang ada. Dalam kasus ASP.NET, unit kerja biasanya menangani permintaan HTTP.

Paul Turner
sumber
Terima kasih banyak atas informasi yang berguna dan penjelasan masalah yang bagus. Sebenarnya saya sangat baru dalam Entity Framework dan juga di Linq jadi informasi ini benar-benar pelajaran yang bagus untuk saya pelajari.
Barsan
20

Intinya

Kode Anda telah mengambil data (entitas) melalui kerangka-entitas dengan pemuatan lambat diaktifkan dan setelah DbContext dibuang, kode Anda mereferensikan properti (entitas terkait / hubungan / navigasi) yang tidak diminta secara eksplisit.

Lebih spesifik

Dengan InvalidOperationExceptionpesan ini selalu berarti hal yang sama: Anda meminta data (entitas) dari kerangka-entitas setelah DbContext telah dibuang.

Kasus sederhana:

(kelas-kelas ini akan digunakan untuk semua contoh dalam jawaban ini, dan menganggap semua properti navigasi telah dikonfigurasi dengan benar dan memiliki tabel terkait dalam database)

public class Person
{
  public int Id { get; set; }
  public string name { get; set; }
  public int? PetId { get; set; }
  public Pet Pet { get; set; }
}

public class Pet 
{
  public string name { get; set; }
}

using (var db = new dbContext())
{
  var person = db.Persons.FirstOrDefaultAsync(p => p.id == 1);
}

Console.WriteLine(person.Pet.Name);

Baris terakhir akan menampilkan InvalidOperationExceptionkarena dbContext belum menonaktifkan pemuatan lambat dan kode mengakses properti navigasi Pet setelah Context dibuang oleh pernyataan using.

Debugging

Bagaimana Anda menemukan sumber pengecualian ini? Selain melihat pengecualian itu sendiri, yang akan dilemparkan tepat di lokasi di mana itu terjadi, aturan umum debugging di Visual Studio berlaku: tempatkan breakpoint strategis dan periksa variabel Anda , baik dengan mengarahkan mouse ke atas namanya, membuka ( Cepat) Jendela Tonton atau menggunakan berbagai panel debugging seperti Lokal dan Mobil.

Jika Anda ingin mengetahui di mana referensi ditetapkan atau tidak, klik kanan namanya dan pilih "Temukan Semua Referensi". Anda kemudian dapat menempatkan breakpoint di setiap lokasi yang meminta data, dan menjalankan program Anda dengan debugger terpasang. Setiap kali debugger berhenti pada breakpoint seperti itu, Anda perlu menentukan apakah properti navigasi Anda seharusnya sudah terisi atau jika data yang diminta diperlukan.

Cara untuk Menghindari

Nonaktifkan Pemuatan Lambat

public class MyDbContext : DbContext
{
  public MyDbContext()
  {
    this.Configuration.LazyLoadingEnabled = false;
  }
}

Pro: Alih-alih melempar InvalidOperationException, properti akan menjadi null. Mengakses properti null atau mencoba mengubah properti properti ini akan memunculkan NullReferenceException .

Cara meminta objek secara eksplisit saat diperlukan:

using (var db = new dbContext())
{
  var person = db.Persons
    .Include(p => p.Pet)
    .FirstOrDefaultAsync(p => p.id == 1);
}
Console.WriteLine(person.Pet.Name);  // No Exception Thrown

Pada contoh sebelumnya, Entity Framework akan mewujudkan Pet selain Person. Ini bisa menguntungkan karena ini adalah panggilan tunggal database. (Namun, bisa juga ada masalah performa yang sangat besar tergantung pada jumlah hasil yang dikembalikan dan jumlah properti navigasi yang diminta, dalam hal ini, tidak akan ada penalti performa karena kedua instance hanya satu record dan satu gabungan).

atau

using (var db = new dbContext())
{
  var person = db.Persons.FirstOrDefaultAsync(p => p.id == 1);

  var pet = db.Pets.FirstOrDefaultAsync(p => p.id == person.PetId);
}
Console.WriteLine(person.Pet.Name);  // No Exception Thrown

Pada contoh sebelumnya, Entity Framework akan mewujudkan Pet secara independen dari Orang dengan melakukan panggilan tambahan ke database. Secara default, Entity Framework melacak objek yang telah diambil dari database dan jika menemukan properti navigasi yang cocok, maka secara otomatis akan mengisi entitas ini. Dalam hal ini karena PetIdpada Personobjek sesuai dengan Pet.Id, Entity Framework akan menetapkan Person.Petke Petnilai diambil, sebelum nilai ditugaskan untuk variabel hewan peliharaan.

Saya selalu merekomendasikan pendekatan ini karena memaksa pemrogram untuk memahami kapan dan bagaimana kode meminta data melalui Entity Framework. Ketika kode melontarkan pengecualian referensi null pada properti entitas, Anda hampir selalu dapat memastikan bahwa Anda tidak secara eksplisit meminta data tersebut.

Erik Philips
sumber
13

Ini jawaban yang sangat terlambat tetapi saya menyelesaikan masalah mematikan pemuatan lambat:

db.Configuration.LazyLoadingEnabled = false;
Ricardo Pontual
sumber
Bagi saya, StackOverflow bekerja dengan sangat baik dengan satu liner. Dan ini berhasil untuk saya, pujian untuk Anda!
Harold_Finch
Kelemahannya adalah Anda harus menggunakan .Include dan hal-hal seperti itu untuk memuat properti navigasi.
boylec1986
1

Dalam kasus saya, saya melewatkan semua model 'Users' ke kolom dan itu tidak dipetakan dengan benar, jadi saya hanya meneruskan 'Users.Name' dan itu memperbaikinya.

var data = db.ApplicationTranceLogs 
             .Include(q=>q.Users)
             .Include(q => q.LookupItems) 
             .Select(q => new { Id = q.Id, FormatDate = q.Date.ToString("yyyy/MM/dd"), ***Users = q.Users,*** ProcessType = q.ProcessType, CoreProcessId = q.CoreProcessId, Data = q.Data }) 
             .ToList();

var data = db.ApplicationTranceLogs 
             .Include(q=>q.Users).Include(q => q.LookupItems) 
             .Select(q => new { Id = q.Id, FormatDate = q.Date.ToString("yyyy/MM/dd"), ***Users = q.Users.Name***, ProcessType = q.ProcessType, CoreProcessId = q.CoreProcessId, Data = q.Data }) 
             .ToList();
Michael Mora Montero
sumber
1

Sebagian besar jawaban lain mengarah ke eager loading, tetapi saya menemukan solusi lain.

Dalam kasus saya, saya memiliki objek EF InventoryItemdengan koleksi InvActivityobjek anak.

class InventoryItem {
...
   // EF code first declaration of a cross table relationship
   public virtual List<InvActivity> ItemsActivity { get; set; }

   public GetLatestActivity()
   {
       return ItemActivity?.OrderByDescending(x => x.DateEntered).SingleOrDefault();
   }
...
}

Dan karena saya menarik dari kumpulan objek anak alih-alih kueri konteks (dengan IQueryable), Include()fungsi itu tidak tersedia untuk mengimplementasikan eager loading. Jadi, solusi saya adalah membuat konteks dari tempat saya menggunakan GetLatestActivity()dan attach()objek yang dikembalikan:

using (DBContext ctx = new DBContext())
{
    var latestAct = _item.GetLatestActivity();

    // attach the Entity object back to a usable database context
    ctx.InventoryActivity.Attach(latestAct);

    // your code that would make use of the latestAct's lazy loading
    // ie   latestAct.lazyLoadedChild.name = "foo";
}

Dengan demikian Anda tidak terjebak dengan eager loading.

Zorgarath
sumber
Ini pada dasarnya adalah pemuatan bersemangat, Anda telah memuat objek melalui konteks. Hanya ada dua pilihan; eager loading dan lazy loading.
Erik Philips
@ErikPhilips benar, ini pemuatan
lambat dengan
1
@ErikPhilips - ada juga Pemuatan Eksplisit - docs.microsoft.com/en-us/ef/ef6/querying/…
Dave Black
1

Jika Anda menggunakan ASP.NET Core dan bertanya-tanya mengapa Anda mendapatkan pesan ini di salah satu metode pengontrol async Anda, pastikan Anda mengembalikan Taskdaripada void- ASP.NET Core membuang konteks yang diinjeksi.

(Saya memposting jawaban ini karena pertanyaan ini berada di urutan teratas dalam hasil pencarian untuk pesan pengecualian itu dan ini adalah masalah yang halus - mungkin berguna bagi orang yang Google untuk itu.)

John
sumber