Решение "экземпляр ObjectContext был удален и больше не может использоваться для операций, требующих подключения" InvalidOperationException



Я пытаюсь заполнить a GridView используя Entity Frameworkm но каждый раз, когда я получаю следующую ошибку:




"свойство доступа' LoanProduct 'на объект' COSIS_DAL.MemberLoan'
бросил следующее исключение: экземпляр ObjectContext был
утилизируется и больше не может использоваться для операций, требующих
соединение."




мой код:



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


}


ошибка упоминания LoanProductName колонка Gridview. Упомянуто: я использую C#, ASP.net, SQL-Server 2008 в качестве серверной БД.



Я совершенно новичок в Entity Framework. Я не могу понять, почему я получаю эту ошибку. Кто-нибудь может мне помочь, пожалуйста?

789   6  

6 ответов:

по умолчанию Entity Framework использует отложенную загрузку для свойств навигации. Вот почему эти свойства должны быть помечены как virtual-EF создает прокси-класс для вашей сущности и переопределяет свойства навигации, чтобы разрешить ленивую загрузку. Например, если у вас есть эта сущность:

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

Entity Framework вернет прокси, унаследованный от этого объекта, и предоставит экземпляр DbContext этому прокси, чтобы разрешить ленивую загрузку членства позже:

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

таким образом, юридическое лицо имеет экземпляр DbContext, который использовался для загрузки объекта. Это твоя проблема. У вас есть using блок вокруг использования CosisEntities. Который размещает контекст перед возвращением сущностей. Когда какой-то код позже пытается использовать свойство навигации с отложенной загрузкой, он терпит неудачу, потому что контекст удаляется в этот момент.

чтобы исправить это поведение, вы можете использовать нетерпеливую загрузку свойств навигации, которые вам понадобятся позже:

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

это будет предварительно загрузить все членства и ленивая загрузка не будет использоваться. Подробнее см. Загрузка Связанных Объектов статья на MSDN.

The CosisEntities класс-это ваш DbContext. Когда вы создаете контекст в using блок, вы определяете границы для своей операции, ориентированной на данные.

в коде вы пытаетесь выдать результат запроса из метода, а затем завершить контекст внутри метода. Операция, которой вы передаете результат, затем пытается получить доступ к объектам для заполнения представления сетки. Где-то в процессе привязки к сетке, ленивый загружен свойства, доступ и Entity Framework пытается выполнить поиск для получения значений. Это не удается, потому что связанный контекст уже закончился.

у вас есть две проблемы:

  1. вы лениво загружаете объекты при привязке к сетке. Это означает, что вы делаете много отдельных операций запроса к SQL Server, которые будут замедлять все. Вы можете устранить эту проблему, либо сделав связанные свойства загруженными по умолчанию, либо попросив Entity Framework включаем их в результаты этого запроса с помощью Include метод расширения.

  2. вы заканчиваете свой контекст преждевременно: a DbContext должны быть доступны на протяжении всей единицы выполняемой работы, только утилизируя его, когда вы закончите с работой под рукой. В случае: ASP.NET, единицей работы обычно является обрабатываемый HTTP-запрос.

Нижняя Строка

ваш код получил данные (сущности) через entity-framework с включенной отложенной загрузкой, и после удаления DbContext ваш код ссылается на свойства (связанные/отношения/объекты навигации), которые не были явно запрошены.

Более Конкретно

The InvalidOperationException С этим сообщением всегда означает одно и то же: вы запрашиваете данные (сущности) из entity-framework после того, как DbContext был расположенный.

простой пример:

(эти классы будут использоваться для всех примеров в этом ответе, и предположим, что все свойства навигации были настроены правильно и имеют связанные таблицы в базе данных)

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

последняя строка будет кидать InvalidOperationException потому что dbContext не отключил ленивую загрузку, и код обращается к свойству навигации Pet после того, как контекст был удален с помощью заявление.

отладка

как вы находите источник этого исключения? Помимо рассмотрения самого исключения, которое будет выброшено именно в том месте, где оно происходит, применяются общие правила отладки в Visual Studio: поместите стратегические точки останова и проверить переменные, либо наведя указатель мыши на их имена, открывая окно (быстрого)просмотра или используя различные панели отладки, такие как Locals и Автоматический.

если вы хотите узнать, где находится ссылка или нет, щелкните правой кнопкой мыши ее имя и выберите "Найти все ссылки". Затем можно разместить точку останова в каждом месте, где запрашиваются данные, и запустить программу с подключенным отладчиком. Каждый раз, когда отладчик прерывается в такой точке останова, необходимо определить, должно ли быть заполнено ваше свойство навигации или необходимы запрошенные данные.

способов избежать

отключить Ленивая Загрузка

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

плюсы: вместо того, чтобы бросать InvalidOperationException свойство будет null. Доступ к свойствам null или попытка изменить свойства этого свойства вызоветNullReferenceException.

как явно запросить объект, когда это необходимо:

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

в предыдущем примере Entity Framework материализует домашнее животное в дополнение к человеку. Это может быть выгодно, потому что это один вызов базы данных. (Однако также могут возникнуть огромные проблемы с производительностью в зависимости от количества возвращаемых результатов и количества запрошенных свойств навигации, в этом случае не будет никакого штрафа за производительность, поскольку оба экземпляра являются только одной записью и одним соединением).

или

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

в предыдущем примере Entity Framework материализует домашнее животное независимо от человека, сделав дополнительный вызов база данных. По умолчанию Entity Framework отслеживает объекты, полученные из базы данных, и если он находит соответствующие свойства навигации, он будет авто-магически заполнить эти сущности. В данном случае потому что PetId на

Это очень поздний ответ, но я решил проблему отключение отложенной загрузки:

db.Configuration.LazyLoadingEnabled = false;

в моем случае я пассировал всех пользователей моделей в столбец, и он не был правильно отображен, поэтому я просто прошел 'Users.Name - и это все исправило.

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

большинство других ответов указывают на жадную загрузку, но я нашел другое решение.

в моем случае у меня был объект EF InventoryItem коллекция InvActivity дочерние объекты.

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

и так как я вытаскивал из коллекции дочерних объектов вместо контекстного запроса (с IQueryable), то Include() функция не была доступна для реализации активной загрузкой. Поэтому вместо этого мое решение состояло в том, чтобы создать контекст, из которого я использовал GetLatestActivity() и attach() возвращенный объект:

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

таким образом, вы не застряли с активной загрузкой.

Comments

    Ничего не найдено.