Lazy Loading navigation properties returns 'System.InvalidOperationException'

.net-core-3.0 c# entity-framework-core visual-studio-2019

Question

I've been migrating a VB.net application using EF6 to a C# .Net Core app using EF-Core 3.0. I've always been using EF as DB-First. With EF-Core, I need to specify the way I want to load my values. Because I often need to access many navigation properties (links to other table through FKs), I'd rather to a Lazy Load than managing Eager Loads. But whenever I wanna do so, i get this error on the naviguation properties :

((Castle.Proxies.WillyDemandesProxy)willyDemandes).IdPartNavigation threw an exception of type 'System.InvalidOperationException' : Error generated for warning 'Microsoft.EntityFrameworkCore.Infrastructure.LazyLoadOnDisposedContextWarning: An attempt was made to lazy-load navigation property 'IdPartNavigation' on entity type 'WillyDemandesProxy' after the associated DbContext was disposed.'. This exception can be suppressed or logged by passing event ID 'CoreEventId.LazyLoadOnDisposedContextWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.

Table WillyDemandes is linked by foreign key to table Parts using WillyDemande.Id_Part and Parts.ID.

When you scaffold your DbContext using EF-Core DB-First, it creates virtual properties called "Navigation Properties" in order for you to easily get access to the linked info in other tables.

The exception is thrown whenever you try to access IDPartNavigation. Here's an example :

Navigation Property Error

It also happens not 100% of the time.

Any ideas?

Context

    /// <summary>
    /// https://docs.microsoft.com/en-us/ef/core/querying/related-data
    /// </summary>
    /// <param name="optionsBuilder"></param>
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseLazyLoadingProxies();
            //optionsBuilder.ConfigureWarnings(warnings => warnings.Default(WarningBehavior.Ignore));
            optionsBuilder.UseSqlServer("Server=TRBSQL02;Database=Info_Indus;Trusted_Connection=True;");
        }
    }

Function

    static public WillyDemandes GetFirst()
    {

        using (Info_IndusContext conn = new Info_IndusContext())
        {
            WillyDemandes willyDemandes;

                willyDemandes = conn.WillyDemandes
                    .Where(x => x.Statut == Statuts.EnTest.ToString() && x.Username == Environment.UserName)
                    //.Include(x=>x.IdPartNavigation)
                    .OrderBy(x => x.Priority)
                    .ThenBy(x => x.Id)
                    .FirstOrDefault();


            if (willyDemandes != null)
            {
                willyDemandes.Statut = Statuts.EnTraitement.ToString();
                willyDemandes.ServerName = Environment.MachineName;
                willyDemandes.DateDebut = DateTime.Now;
                conn.SaveChanges();
                conn.Entry(willyDemandes).GetDatabaseValues();
                conn.Entry(willyDemandes).Reload();
            }

            return willyDemandes;
        }
    }

Previously in Vb.Net

Public Function Demande_GetFirst() As WillyDemandes

        Dim conn As New Info_IndusEntities(False)

        Dim DemandeWilly As WillyDemandes = conn.WillyDemandes.Where(Function(x) x.Statut = Statuts.EnTest.ToString AndAlso x.Username = Environment.UserName).OrderBy(Function(x) x.Priority).ThenBy(Function(x) x.ID).FirstOrDefault

        If Not IsNothing(DemandeWilly) Then
            DemandeWilly.Statut = Statuts.EnTraitement.ToString
            DemandeWilly.ServerName = Environment.MachineName
            DemandeWilly.DateDebut = DateTime.Now
            conn.SaveChanges()
        End If

        Return DemandeWilly

    End Function

05/21/2019

The error seems to be related to the range of DbContext. It happens when you combine USING and PROXIES.

I've written another definition of my function but this time passing the connection through parameters instead of creating it inside the function with USING. The proxies are then available wherever DbContext is alive.

    static public WillyDemandes GetFirst(Info_IndusContext conn)
{


    WillyDemandes willyDemandes;


        willyDemandes = conn.WillyDemandes
            .Where(x => x.Statut == Statuts.EnTest.ToString() && x.Username == Environment.UserName)
            //.Include(x=>x.IdPartNavigation)
            .OrderBy(x => x.Priority)
            .ThenBy(x => x.Id)
            .FirstOrDefault();

    if (willyDemandes != null)
    {
        willyDemandes.Statut = Statuts.EnTraitement.ToString();
        willyDemandes.ServerName = Environment.MachineName;
        willyDemandes.DateDebut = DateTime.Now;
        conn.SaveChanges();
        conn.Entry(willyDemandes).GetDatabaseValues();
        conn.Entry(willyDemandes).Reload();
    }

    return willyDemandes;

}
1
4
5/21/2019 2:44:31 PM

Popular Answer

So I found a way to get through this but I doubt it is the best of solutions.

Basically what I've done is that I've created a ContextProvider Class that allows me to create 1 Info_IndusContext accessible by everybody. I also removed all the USING statements of my controllers.

I've also abadonned using LazyLoading and started using EagerLoading as you may noticed in the GetFirst function down below.

Thoughts?

ContextProvider

using WillyServer.Models;
namespace WillyServer.Controllers
{
    public static class ContextProvider
    {
        public static Info_IndusContext db = new Info_IndusContext();
    }
}

WillyDemandesController.GetFirst

    static public WillyDemandes GetFirst()
    {

   WillyDemandes willyDemandes = ContextProvider.db.WillyDemandes
                .Include(x => x.IdPartNavigation)
                .Include(x => x.WillyMachines)
                .Where(x => x.Statut == Statuts.EnTest.ToString() && x.Username == Environment.UserName)
                .OrderBy(x => x.Priority)
                .ThenBy(x => x.Id)
                .FirstOrDefault();

        if (willyDemandes != null)
        {
            willyDemandes.Statut = Statuts.EnTraitement.ToString();
            willyDemandes.ServerName = Environment.MachineName;
            willyDemandes.DateDebut = DateTime.Now;
            ContextProvider.db.SaveChanges();
        }

        return willyDemandes;

    }
0
5/24/2019 11:29:55 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow