Решение "экземпляр 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. Я не могу понять, почему я получаю эту ошибку. Кто-нибудь может мне помочь, пожалуйста?
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 пытается выполнить поиск для получения значений. Это не удается, потому что связанный контекст уже закончился.
у вас есть две проблемы:
вы лениво загружаете объекты при привязке к сетке. Это означает, что вы делаете много отдельных операций запроса к SQL Server, которые будут замедлять все. Вы можете устранить эту проблему, либо сделав связанные свойства загруженными по умолчанию, либо попросив Entity Framework включаем их в результаты этого запроса с помощью
Includeметод расширения.вы заканчиваете свой контекст преждевременно: 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