Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET 8 Blazor - DbContextFactory and EF Core Lazy Loading #54616

Open
1 task done
sbwalker opened this issue Mar 19, 2024 · 2 comments
Open
1 task done

.NET 8 Blazor - DbContextFactory and EF Core Lazy Loading #54616

sbwalker opened this issue Mar 19, 2024 · 2 comments
Labels
area-blazor Includes: Blazor, Razor Components bug This issue describes a behavior which is not expected - a bug. feature-templates
Milestone

Comments

@sbwalker
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

The latest guidance from Microsoft for using EF Core with Blazor is to use DbContextFactory and short-lived DbContent instances (confirmed here https://learn.microsoft.com/en-us/aspnet/core/blazor/blazor-ef-core?view=aspnetcore-8.0#new-dbcontext-instances).

The basic pattern is to use CreateDbContext() in your data access methods. The following is an example of method which returns a collection of users:

        public IEnumerable<User> GetUsers()
        {
            using var db = _dbContextFactory.CreateDbContext();
            return db.User;
        }

Note that when using this pattern it is not possible to use the standard EF Core lazy loading behavior. This is because the DbContext is disposed as soon as the data access is complete. If you try to defer performing any additional operations on the DbSet it will throw an exception. For example if we add an .OrderBy():

        public IEnumerable<User> GetUsers()
        {
            using var db = _dbContextFactory.CreateDbContext();
            return ctx.User.OrderBy(item => item.Name);
        }

It will result in the following exception:

"Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances."

The solution to this problem is to add .ToList() to instruct EF Core to perform eager loading on the collection:

        public IEnumerable<User> GetUsers()
        {
            using var db = _dbContextFactory.CreateDbContext();
            return ctx.User.OrderBy(item => item.Name).ToList();
        }

However this means that your Blazor application will not be able to utilize lazy loading, which is one of the most significant performance features offered by EF Core.

Since DbContextFactory is the recommended guidance, I was curious how aspnetcore handled this challenge in its own classes which interact with EF Core. A few examples:

  1. The new CRUD templates for Blazor which were introduced recently for scaffolding code. It appears they do not follow the guidance - they continue to use DbContext (which has been well documented to to not align with Blazor's process model). In fact someone already logged an issue about this: Blazor Identity templates and CRUD scaffolding should use the DbContextFactory #54539

  2. The .NET Identity classes in .NET 8. It appears they do not use DbContextFactory either. Someone has logged an issue here: UserStore should use IDbContextFactory<TContext> (instead of just TContext) if available #42260

So since there are no examples on the recommended approach for using DbContextFactory, I would request that the documentation be updated with some examples. Specifically, it would be helpful if this challenge related to lazy loading is addressed in the docs so that it is clear on whether Microsoft is recommending that developers utilize an eager loading approach with EF Core when it comes to Blazor.

Expected Behavior

Improved documentation and actual DbContextFactory usage in aspnetcore implementations.

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version

8.0

Anything else?

No response

@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label Used by the dotnet-issue-labeler to label those issues which couldn't be triaged automatically label Mar 19, 2024
@mkArtakMSFT mkArtakMSFT added area-blazor Includes: Blazor, Razor Components and removed needs-area-label Used by the dotnet-issue-labeler to label those issues which couldn't be triaged automatically labels Mar 19, 2024
@mkArtakMSFT
Copy link
Member

Thanks for bringing this up, @sbwalker.
Have you seen my response in #54539 (comment)? Maybe that addresses this too?

@sbwalker
Copy link
Author

sbwalker commented Mar 19, 2024

@mkArtakMSFT I don't think it does address the issue. I am developing Static Server-Side components and when I use EF Core for data access I was running into exceptions:

"System.InvalidOperationException: There is already an open DataReader associated with this Connection which must be closed first."

I consulted the documentation which mentions "server-side Blazor apps":

"In server-side Blazor apps, DbContext isn't thread safe and isn't designed for concurrent use. The existing lifetimes are inappropriate for these reasons:

  1. Singleton shares state across all users of the app and leads to inappropriate concurrent use.
  2. Scoped (the default) poses a similar issue between components for the same user.
  3. Transient results in a new instance per request; but as components can be long-lived, this results in a longer-lived context than may be intended.

By default, consider using one context per operation. The context is designed for fast, low overhead instantiation:

using var context = new MyContext();
return await context.MyEntities.ToListAsync();"

So I modified my code to use DbContextFactory and it fully resolved my issue.

So in my experience DbContextFactory needs to be used in Static Server-Side Blazor as well as Interactive - Blazor Server scenarios.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components bug This issue describes a behavior which is not expected - a bug. feature-templates
Projects
None yet
Development

No branches or pull requests

3 participants