9

Entity Framework Core 3.1.2 - I have enabled UseLazyLoadingProxies on my DbContext to ensure data integrity, but I want to throw an exception during development if it is used.

How can I execute some code every time EF Core loads a relationship lazily?

1
  • 1
    have you tried adding an attribute that tells you if the object is deriving from the "ILazyLoader" interface in Microsoft.EntityFrameworkCore.Infrastructure, if it is then there's a good chance it's loading is deferred, that's all I can think of at the moment anyway, If I get chance I'll have a more in depth look later to day. Commented Mar 3, 2020 at 15:07

2 Answers 2

10

I used this to progressively get rid of lazy proxy use in our codebase:

if (enableProxies)
{
    builder.UseLazyLoadingProxies();
    var lazyLoadEvents = new[]
    {
        CoreEventId.NavigationLazyLoading,
        CoreEventId.DetachedLazyLoadingWarning,
        CoreEventId.LazyLoadOnDisposedContextWarning,
    };
#if DEBUG
    builder.ConfigureWarnings(w => w.Throw(lazyLoadEvents));  //Lazyload now throws in DEBUG
#else
    if (sp.GetService<IHostEnvironment>()?.IsEnvironment("PRD") ?? false)
    {   //logs LazyLoad events as error everywhere else
        builder.ConfigureWarnings(w => w.Log(lazyLoadEvents.Select(lle => (lle, LogLevel.Error)).ToArray())); 
    }
#endif
}

This makes the following. In production behavior is unchanged. In DEBUG an exception is raised when lazy-load is used. On qualification environments, an error is logged.

Sign up to request clarification or add additional context in comments.

Comments

9

The only way I know is diagnostic messages. See an example here: https://www.domstamand.com/getting-feedback-from-entityframework-core-through-diagnostics.

The event class you want is https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.diagnostics.lazyloadingeventdata.

In the application's DBContext

#if DEBUG
    static ApplicationDbContext()
    {
        // In DEBUG mode we throw an InvalidOperationException
        // when the app tries to lazy load data.
        // In production we just let it happen, for data
        // consistency reasons.
        DiagnosticListener.AllListeners.Subscribe(new DbContextDiagnosticObserver());
    }
#endif

Then a class to hook into EF notifications

internal class DbContextDiagnosticObserver : IObserver<DiagnosticListener>
    {
        private readonly DbContextLazyLoadObserver LazyLoadObserver =
            new DbContextLazyLoadObserver();

        public void OnCompleted() { }

        public void OnError(Exception error) { }

        public void OnNext(DiagnosticListener listener)
        {
            if (listener.Name == DbLoggerCategory.Name)
                listener.Subscribe(LazyLoadObserver);
        }
    }

And then finally the class that throws exceptions whenever a lazy load occurrs

internal class DbContextLazyLoadObserver : IObserver<KeyValuePair<string, object>>
    {
        public void OnCompleted() { }
        public void OnError(Exception error) { }

        public void OnNext(KeyValuePair<string, object> @event)
        {
            // If we see some Lazy Loading, it means the developer needs to
            // fix their code!
            if (@event.Key.Contains("LazyLoading"))
                throw new InvalidOperationException(@event.Value.ToString());
        }
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.