Resolvendo “A instância ObjectContext foi descartada e não pode mais ser usada para operações que requerem uma conexão” InvalidOperationException


122

Estou tentando preencher um GridViewEntity Frameworkm usando, mas sempre que recebo o seguinte erro:

"O acessador de propriedade 'LoanProduct' no objeto 'COSIS_DAL.MemberLoan' lançou a seguinte exceção: A instância ObjectContext foi descartada e não pode mais ser usada para operações que requerem uma conexão."

Meu código é:

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

O erro está mencionando a LoanProductNamecoluna do Gridview. Mencionado: estou usando C #, ASP.net, SQL-Server 2008 como banco de dados de back-end.

Eu sou bastante novo no Entity Framework. Não consigo entender por que estou recebendo esse erro. Alguém pode me ajudar por favor?


1
Você está acessando propriedades de navegação na grade? Se você fizer isso, também precisará incluir essas tabelas de navegação na consulta. Comoquery.Include("SomeOtherTable")
— Nilesh

Tente criar uma classe proxy para hospedar sua entidade ou, pelo menos, retornar um objeto anônimo. Do meu ponto de vista, o uso de ef requer a criação de classes proxy para implementar suas lógicas, use o edmx assim como a camada de acesso ao banco de dados e não os negócios.
— Gonzix 23/08/13

sim no gridview estou recebendo outra coluna da tabela também. Qual é LoanProviderName.
— Barsan

1
Tente db.MemberLoans.Include("LoanProduct").OrderByDescending()verificar a sintaxe, pois não tenho o VS na minha frente.
— Nilesh

3
Você só precisa continuar incluindo todas as propriedades de navegação que está acessando fora do contexto db.MemberLoans.Include("LoanProduct").Include("SomeOtherTable). Verifique as respostas de @Tragedian e @lazyberezovsky
— Nilesh

Respostas:


174

Por padrão, o Entity Framework usa carregamento lento para propriedades de navegação. É por isso que essas propriedades devem ser marcadas como virtuais - o EF cria uma classe de proxy para sua entidade e substitui as propriedades de navegação para permitir o carregamento lento. Por exemplo, se você possui esta entidade:

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

O Entity Framework retornará o proxy herdado dessa entidade e fornecerá a instância DbContext a esse proxy para permitir o carregamento lento da associação posteriormente:

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

Portanto, a entidade possui uma instância do DbContext que foi usada para carregar a entidade. Esse é o seu problema. Você usingbloqueou o uso do CosisEntities. Que descarta o contexto antes que as entidades sejam retornadas. Quando algum código tenta posteriormente usar a propriedade de navegação com carregamento lento, ele falha, porque o contexto é descartado naquele momento.

Para corrigir esse comportamento, você pode usar o carregamento rápido das propriedades de navegação, necessárias mais tarde:

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

Isso pré-carregará todas as associações e o carregamento lento não será usado. Para obter detalhes, consulte o artigo Carregando entidades relacionadas no MSDN.


Muito obrigado pela sua explicação e resposta úteis. Na verdade, aqui estou incluindo três tabelas, então não sei como posso adicionar as três tabelas com INCLUDE. você pode por favor me ajudar nisso por favor.
— Barsan

8
O @barsan inclui apenas todas as propriedades de navegação, uma por uma. Por exemplo, db.MemberLoans.Include(m => m.Membership).Include(m => m.LoanProduct).OrderByDescending(m => m.LoanDate);isso gerará a consulta JOIN e retornará todos os dados de uma só vez.
— Sergey Berezovskiy

1
Muito obrigado lazyberezovsky. Sou muito grato a você. Você me salvou quase um dia. Pela sua explicação, estou aprendendo mais sobre o Entity Framework. Obrigada meu amigo
— Barsan

Obrigado companheiro, perfeito. Eu tinha uma declaração de uso que estava limitando o carregamento lento. Ótima resposta.
— Ncbl 08/11

4
E se eu não quiser incluir essas entidades relacionadas na minha consulta?
— Ortund

32

A CosisEntitiesturma é sua DbContext. Ao criar um contexto em um usingbloco, você define os limites para sua operação orientada a dados.

No seu código, você está tentando emitir o resultado de uma consulta de um método e finalizar o contexto dentro do método. A operação para a qual você passa o resultado tenta acessar as entidades para preencher a visualização em grade. Em algum lugar do processo de ligação à grade, uma propriedade de carregamento lento está sendo acessada e o Entity Framework está tentando executar uma pesquisa para obter os valores. Falha, porque o contexto associado já terminou.

Você tem dois problemas:

  1. Você é uma entidade de carregamento lento quando liga à grade. Isso significa que você está executando muitas operações de consulta separadas no SQL Server, o que atrasará tudo. Você pode corrigir esse problema, tornando as propriedades relacionadas prontamente carregadas por padrão ou solicitando que o Entity Framework as inclua nos resultados dessa consulta usando o Includemétodo de extensão

  2. Você está encerrando seu contexto prematuramente: a DbContextdeve estar disponível em toda a unidade de trabalho que está sendo executada, descartando-a somente quando você terminar o trabalho em mãos. No caso do ASP.NET, uma unidade de trabalho normalmente é a solicitação HTTP sendo manipulada.


Muito obrigado pelas informações úteis e boa explicação do problema. Na verdade, eu sou tão novo no Entity Framework, bem como no Linq, então essas informações são realmente uma ótima lição para eu aprender.
— Barsan

20

Bottom Line

Seu código recuperou dados (entidades) por meio de estrutura de entidade com carregamento lento ativado e depois que o DbContext foi descartado, seu código está fazendo referência a propriedades (entidades relacionadas / de relacionamento / entidades de navegação) que não foram solicitadas explicitamente.

Mais especificamente

O InvalidOperationExceptioncom esta mensagem sempre significa a mesma coisa: você está solicitando dados (entidades) de entidade-quadro após a DbContext foi descartado.

Um caso simples:

(essas classes serão usadas para todos os exemplos nesta resposta e assumirão que todas as propriedades de navegação foram configuradas corretamente e têm tabelas associadas no banco de dados)

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

A última linha lançará o InvalidOperationExceptionporque o dbContext não desativou o carregamento lento e o código está acessando a propriedade de navegação Pet após o contexto ser descartado pela instrução using.

Depuração

Como você encontra a fonte dessa exceção? Além de observar a exceção em si, que será lançada exatamente no local em que ocorre, as regras gerais de depuração no Visual Studio se aplicam: coloque pontos de interrupção estratégicos e inspecione suas variáveis , passando o mouse sobre seus nomes, abrindo um ( Rápido) Observe a janela ou use os vários painéis de depuração, como Locals e Autos.

Se você deseja descobrir onde a referência está ou não está definida, clique com o botão direito do mouse no nome e selecione "Localizar todas as referências". Você pode colocar um ponto de interrupção em qualquer local que solicite dados e executar seu programa com o depurador conectado. Sempre que o depurador interrompe esse ponto de interrupção, é necessário determinar se sua propriedade de navegação deveria ter sido preenchida ou se os dados solicitados são necessários.

Maneiras de evitar

Desativar carregamento lento

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

Prós: em vez de lançar InvalidOperationException, a propriedade será nula. Acessar propriedades null ou tentar alterar as propriedades dessa propriedade lançará uma NullReferenceException .

Como solicitar explicitamente o objeto quando necessário:

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

No exemplo anterior, o Entity Framework materializará o animal de estimação além da pessoa. Isso pode ser vantajoso porque é uma chamada única para o banco de dados. (No entanto, também pode haver enormes problemas de desempenho, dependendo do número de resultados retornados e do número de propriedades de navegação solicitadas, nesse caso, não haverá penalidade no desempenho, pois ambas as instâncias são apenas um registro e uma associação única).

ou

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

No exemplo anterior, o Entity Framework materializará o Pet independentemente da Pessoa, fazendo uma chamada adicional ao banco de dados. Por padrão, o Entity Framework rastreia os objetos recuperados do banco de dados e, se encontrar propriedades de navegação correspondentes, preencherá automaticamente essas entidades. Nesse caso, porque PetIdon do Personobjeto corresponde ao Pet.Id, o Entity Framework atribui o Person.Petao Petvalor recuperado, antes que o valor seja atribuído à variável pet.

Eu sempre recomendo essa abordagem, pois força os programadores a entender quando e como o código solicita dados via Entity Framework. Quando o código lança uma exceção de referência nula na propriedade de uma entidade, quase sempre você pode ter certeza de que não solicitou esses dados explicitamente.


13

É uma resposta muito tardia, mas resolvi o problema desativando o carregamento lento:

db.Configuration.LazyLoadingEnabled = false;

Para mim, o StackOverflow faz maravilhas com um revestimento. E isso fez por mim, parabéns a você!
— Harold_Finch 14/08

A desvantagem é que você precisa usar o .Include e coisas assim para carregar as propriedades de navegação.
— boylec1986

1

No meu caso, eu estava passando todos os modelos 'Users' para a coluna e não foram mapeados corretamente, então passei apenas 'Users.Name' e ele foi corrigido.

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

A maioria das outras respostas aponta para um carregamento ansioso, mas encontrei outra solução.

No meu caso, eu tinha um objeto EF InventoryItemcom uma coleção de InvActivityobjetos filho.

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

E como eu estava retirando da coleção de objetos filho em vez de uma consulta de contexto (com IQueryable), a Include()função não estava disponível para implementar o carregamento antecipado. Então, em vez disso, minha solução foi criar um contexto a partir do qual eu utilizei GetLatestActivity()e attach()o objeto retornado:

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

Portanto, você não está preso a um carregamento ansioso.


Isso é basicamente um carregamento ansioso, você carregou o objeto através de um contexto. Existem apenas duas opções; carregamento ansioso e carregamento preguiçoso.
— Erik Philips

@ErikPhilips certo, é o carregamento lento com um novo contexto de dados
— Zorgarath

1
@ErikPhilips - também há carregamento explícito - docs.microsoft.com/en-us/ef/ef6/querying/…
— Dave Black

1

Se você estiver usando o ASP.NET Core e se perguntar por que recebeu essa mensagem em um dos métodos de controlador assíncrono, retorne um Taskpoucovoid - o ASP.NET Core descarta os contextos injetados.

(Estou postando esta resposta, pois essa pergunta é alta nos resultados da pesquisa para essa mensagem de exceção e é um problema sutil - talvez seja útil para as pessoas que pesquisam no Google.)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.