Çözme "ObjectContext örneği elden çıkarıldı ve artık bağlantı gerektiren işlemler için kullanılamaz" InvalidOperationException


123

GridViewEntity Frameworkm kullanarak doldurmaya çalışıyorum ama her seferinde aşağıdaki hatayı alıyorum:

"'COSIS_DAL.MemberLoan' nesnesindeki özellik erişimcisi 'LoanProduct' şu istisnayı attı: ObjectContext örneği atıldı ve artık bağlantı gerektiren işlemler için kullanılamaz."

Benim kodum:

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

Hata söz edilmektedir LoanProductNamesütun Gridview. Bahsedilen: Arka uç DB olarak C #, ASP.net, SQL-Server 2008 kullanıyorum.

Entity Framework'te oldukça yeniyim. Bu hatayı neden aldığımı anlayamıyorum. Biri bana yardım edebilir mi lütfen?


1
Izgara görünümünde herhangi bir gezinme özelliğine erişiyor musunuz? Bunu yaparsanız, sorguya bu gezinme tablolarını da eklemeniz gerekir. Likequery.Include("SomeOtherTable")
Nilesh

Varlığınızı barındırmak için bir proxy sınıfı oluşturmayı veya en azından anonim bir nesne döndürmeyi deneyin. Benim bakış açıma göre ef kullanmak, mantığınızı uygulamak için proxy sınıfları oluşturmayı gerektirir, edmx'i tıpkı db erişim katmanı gibi değil, iş olarak kullanın.
Gonzix

evet ızgara görünümünde ayrıca başka bir tablo sütunu alıyorum. Hangi LoanProviderName.
barsan

1
db.MemberLoans.Include("LoanProduct").OrderByDescending()Sözdizimini kontrol etmeyi deneyin çünkü önümde VS yok.
Nilesh

3
Bağlam dışında eriştiğiniz tüm gezinme özelliklerini eklemeye devam etmeniz yeterlidir db.MemberLoans.Include("LoanProduct").Include("SomeOtherTable). @Tragedian ve @lazyberezovsky tarafından cevaplarını kontrol
Nilesh

Yanıtlar:


175

Varsayılan olarak Entity Framework, gezinti özellikleri için geç yükleme kullanır. Bu nedenle, bu özellikler sanal olarak işaretlenmelidir - EF varlığınız için proxy sınıfı oluşturur ve geç yüklemeye izin vermek için gezinme özelliklerini geçersiz kılar. Örneğin, bu varlığa sahipseniz:

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

Entity Framework, bu varlıktan devralınan proxy'yi döndürür ve üyeliğin daha sonra geç yüklenmesine izin vermek için bu proxy'ye DbContext örneği sağlar:

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

Dolayısıyla, varlık, varlığı yüklemek için kullanılan DbContext örneğine sahiptir. Bu senin problemin. usingCosisEntities kullanımıyla ilgili bloğunuz var . Varlıklar döndürülmeden önce bağlamı ortadan kaldırır. Bazı kodlar daha sonra geç yüklenen gezinti özelliğini kullanmaya çalıştığında, içerik o anda düzenlendiği için başarısız olur.

Bu davranışı düzeltmek için, daha sonra ihtiyaç duyacağınız navigasyon özelliklerinin istekli yüklenmesini kullanabilirsiniz:

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

Bu, tüm üyelikleri önceden yükleyecek ve geç yükleme kullanılmayacaktır. Ayrıntılar için MSDN'deki İlgili Varlıkları Yükleme makalesine bakın.


Faydalı açıklamanız ve cevabınız için çok teşekkür ederiz. Aslında burada üç tabloyu dahil ediyorum, bu yüzden üç tabloyu INCLUDE ile nasıl ekleyebileceğimi bilmiyorum. Lütfen bana bu konuda yardım eder misin?
barsan

8
@barsan sadece tek tek tüm gezinme özelliklerini içerir. Örneğin db.MemberLoans.Include(m => m.Membership).Include(m => m.LoanProduct).OrderByDescending(m => m.LoanDate);bu, JOIN sorgusu oluşturacak ve tüm verileri bir kerede döndürecektir.
Sergey Berezovskiy

1
Çok teşekkürler tembelberezovsky. Sana minnettarım Beni neredeyse bir gün kurtardın. Sizden açıklamadan Entity Framework hakkında daha fazla şey öğreniyorum. Teşekkür ederim arkadaşım.
barsan

Teşekkürler dostum, mükemmel. Tembel yüklemeyi sınırlayan bir kullanım deyimim vardı. Mükemmel cevap.
ncbl

4
Ya sorguma bu ilgili varlıkları dahil etmek istemezsem?
Ortund

32

CosisEntitiesSınıf senin olduğunu DbContext. Bir usingblokta bir bağlam oluşturduğunuzda , veri odaklı operasyonunuzun sınırlarını belirliyorsunuz.

Kodunuzda, bir yöntemden bir sorgunun sonucunu yayınlamaya ve ardından yöntem içindeki bağlamı sonlandırmaya çalışıyorsunuz. Sonucu ilettiğiniz işlem, ızgara görünümünü doldurmak için varlıklara erişmeye çalışır. Izgaraya bağlanma sürecinde bir yerde, tembel yüklenen bir özelliğe erişiliyor ve Entity Framework değerleri elde etmek için bir arama gerçekleştirmeye çalışıyor. Başarısız olur, çünkü ilişkili bağlam zaten sona ermiştir.

İki sorunun var:

  1. Izgaraya bağlandığınızda tembel yüklenen varlıklarsınız. Bu, SQL Server'a her şeyi yavaşlatacak birçok ayrı sorgu işlemi yaptığınız anlamına gelir. Bu sorunu, ilgili özellikleri varsayılan olarak istekli yaparak veya Entity Framework'ten Includeuzantı yöntemini kullanarak bu sorgunun sonuçlarına dahil etmesini isteyerek çözebilirsiniz .

  2. Bağlamınızı vaktinden önce bitiriyorsunuz: a DbContext, gerçekleştirilen çalışma birimi boyunca mevcut olmalı, yalnızca elinizdeki işi bitirdiğinizde elden çıkarılmalıdır. ASP.NET durumunda, bir iş birimi genellikle işlenen HTTP isteğidir.


Yararlı bilgiler ve sorunun güzel açıklaması için çok teşekkür ederim. Aslında Entity Framework'te ve Linq'te çok yeniyim, bu yüzden bu bilgi gerçekten öğrenmem için harika bir ders.
barsan

20

Sonuç olarak

Kodunuz, tembel yükleme etkinken varlık çerçevesi aracılığıyla verileri (varlıkları) aldı ve DbContext atıldıktan sonra, kodunuz açıkça talep edilmeyen özelliklere (ilgili / ilişki / gezinme varlıkları) başvuruyor.

Daha Özel Olarak

InvalidOperationExceptionBu mesajla hep aynı şey gelir: DBContext bertaraf edildikten sonra Varlık-framework verileri (kişiler) talep ediyorlar.

Basit bir durum:

(bu sınıflar bu yanıttaki tüm örnekler için kullanılacak ve tüm gezinme özelliklerinin doğru şekilde yapılandırıldığını ve veritabanında ilişkili tablolara sahip olduğunu varsayar)

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

Son satır InvalidOperationException, dbContext geç yüklemeyi devre dışı bırakmadığından ve kod, Bağlam using deyimi tarafından elden çıkarıldıktan sonra Pet gezinme özelliğine eriştiğinden bunu atar .

Hata ayıklama

Bu istisnanın kaynağını nasıl buluyorsunuz? Tam olarak meydana geldiği konuma fırlatılacak olan istisnanın kendisine bakmanın yanı sıra, Visual Studio'da hata ayıklamanın genel kuralları geçerlidir: stratejik kesme noktaları yerleştirin ve değişkenlerinizi , ya fareyi adlarının üzerine getirerek, bir ( Hızlı) Pencereyi izleyin veya Yereller ve Otomobiller gibi çeşitli hata ayıklama panellerini kullanın.

Referansın nerede ayarlanıp ayarlanmadığını öğrenmek istiyorsanız, adına sağ tıklayın ve "Tüm Referansları Bul" u seçin. Daha sonra veri isteyen her konuma bir kesme noktası yerleştirebilir ve programınızı hata ayıklayıcı eklenmiş olarak çalıştırabilirsiniz. Hata ayıklayıcı böyle bir kesme noktasında her bozulduğunda, gezinme özelliğinizin doldurulup doldurulmaması gerektiğini veya istenen verilerin gerekli olup olmadığını belirlemeniz gerekir.

Kaçınmanın Yolları

Geç Yüklemeyi Devre Dışı Bırak

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

Artıları: InvalidOperationException oluşturmak yerine, özellik null olacaktır. Null özelliklerine erişmek veya bu özelliğin özelliklerini değiştirmeye çalışmak, bir NullReferenceException oluşturur .

İhtiyaç duyulduğunda nesne nasıl açıkça talep edilir:

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

Önceki örnekte Entity Framework, Kişiye ek olarak Pet'i gerçekleştirecektir. Bu avantajlı olabilir çünkü bu, veritabanına tek bir çağrıdır. (Bununla birlikte, döndürülen sonuçların sayısına ve istenen gezinme özelliklerinin sayısına bağlı olarak büyük performans sorunları da olabilir, bu durumda, her iki örnek de yalnızca tek bir kayıt ve tek bir birleştirme olduğu için performans cezası olmayacaktır).

veya

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

Önceki örnekte Entity Framework, veritabanına ek bir çağrı yaparak Pet'i Kişiden bağımsız olarak gerçekleştirecektir. Varsayılan olarak, Entity Framework veritabanından aldığı nesneleri izler ve eşleşen gezinme özellikleri bulursa bu varlıkları otomatik olarak büyülü bir şekilde doldurur. Çünkü bu durumda PetIdilgili Personnesnenin maçları Pet.Id, Varlık Çerçeve atar Person.Petiçin Petdeğer pet değişkene atanmadan önce, çekilen değere.

Programcıları Entity Framework aracılığıyla kodun ne zaman ve nasıl istendiğini anlamaya zorladığı için her zaman bu yaklaşımı öneririm. Kod bir varlığın özelliğine boş bir referans istisnası attığında, neredeyse her zaman bu verileri açıkça talep etmediğinizden emin olabilirsiniz.


13

Çok geç bir cevap ama tembel yüklemeyi kapatarak sorunu çözdüm:

db.Configuration.LazyLoadingEnabled = false;

Bana göre, StackOverflow bir gömlekle harikalar yaratıyor. Ve bu benim için yaptı, size şeref!
Harold_Finch

Dezavantajı, navigasyon özelliklerini yüklemek için .Include ve bunun gibi şeyler kullanmanız gerektiğidir.
boylec1986

1

Benim durumumda, tüm modelleri 'Kullanıcılar'ı sütuna geçiriyordum ve doğru şekilde eşlenmemişti, bu yüzden' Kullanıcılar.Adı'nı geçtim ve düzeltti.

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

1

Diğer cevapların çoğu istekli yüklemeye işaret ediyor, ancak başka bir çözüm buldum.

Benim durumumda InventoryItem, InvActivityalt nesnelerden oluşan bir koleksiyona sahip bir EF nesnem vardı .

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

Ve bir bağlam sorgusu (with IQueryable) yerine alt nesne koleksiyonundan çekim yaptığım için , Include()işlev istekli yüklemeyi uygulamak için mevcut değildi. Bunun yerine benim çözümüm, kullandığım yerden GetLatestActivity()ve attach()döndürülen nesneden bir bağlam oluşturmaktı :

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";
}

Böylece istekli yükleme ile sıkışıp kalmazsınız.


Bu temelde istekli bir yükleme, nesneyi bir bağlam aracılığıyla yüklediniz. Yalnızca iki seçenek vardır; istekli yükleme ve tembel yükleme.
Erik Philips

@ErikPhilips doğru, yeni bir veri bağlamıyla tembel yükleniyor
Zorgarath

1
@ErikPhilips - Ayrıca Açık Yükleme de var - docs.microsoft.com/en-us/ef/ef6/querying/…
Dave Black

1

ASP.NET Core kullanıyorsanız ve neden bu iletiyi zaman uyumsuz denetleyici yöntemlerinden birinde aldığınızı merak ediyorsanız, Taskyerine a döndürdüğünüzden emin olun.void ASP.NET Core enjekte edilmiş bağlamları attığınızdan .

(Bu yanıtı, bu soru, bu istisna mesajının arama sonuçlarında üst sıralarda yer aldığından ve ince bir sorun olduğu için gönderiyorum - belki de Google'ı arayanlar için yararlıdır.)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.