asp.net-coreentity-framework-coreblazoreditform

What is the correct way to get data for an EditForm using EF Core, with or without tracking?


I have a Blazor EditForm and I use Model to relate it to the object and to bind the controls.

EditForm Model="@Client" OnValidSubmit="SubmitForm"

I use EF Core to bring the data from the model when the user wants to edit it,

var Client = _dbContext.Set().FindAsync(id);

However, when the user enters to edit a Client and changes a piece of data, but then leaves the form without saving it by no pressing the save button, if he returns to the form to edit the same record, the field that was modified without saving shows the data that the user had captured, this is because of the tracking that EF Core does.

If I change my code to var client = _dbContext.Set().AsNoTracking().FirstOrDefaultAsync(c => c.ClientId == id),

EF Core no longer does the tracking and that solves the problem.

What I would like to know is if doing this is correct and if it affects the performance of my system.

Thank you.


Solution

  • You are facing a few challenges:

    It’s very likely that you’ve registered DbContext as scoped, which is correct, but Blazor does not automatically create scopes. This is why the documentation emphasizes using IDbContextFactory.

    However, working with IDbContextFactory can complicate repository usage since every method needs to create and dispose of a context.

    In your case, having one DbContext per user session leads to conflicts with other operations triggered by the same session.

    If you prefer not to manage IDbContextFactory and DbContext resources manually (this is subjective), here are two options:

    Less ideal: Clear the DbContext state before each use: context.ChangeTracker.Clear().

    Much better: Inject IServiceScopeFactory into your controller to manually create a scope. When you need to work with services or repositories, use CreateScope(), retrieve the required service, execute the desired method, and dispose of the scope.

    Here’s a simplified example in C#:

    public class MyController
    {
        private readonly IServiceScopeFactory _scopeFactory;
    
        public MyController(IServiceScopeFactory scopeFactory)
        {
            _scopeFactory = scopeFactory;
        }
    
        public void SomeControllerMethod()
        {
            // Any services or repositories registered with scope lifetime will be disposed of automatically
            using var scope = _scopeFactory.CreateScope();
    
            var someService = scope.ServiceProvider.GetRequiredService<SomeService>();
            someService.DoSomeWork();
        }
    }