0

I am using Blazor Server, with EF Core and have an issue where EF is holding on to changes made to an entity (but not committed to the DB).

My Entity is created in this way in Program.cs

builder.Services.AddDbContext<RpContext>(options => options.UseSqlServer(config.GetConnectionString("RpCon")));

My Repository is added in Program.cs like this:

builder.Services.AddScoped<IUserRepository, UserRepository>();

The Repository Looks like this:

 public class UserRepository : IUserRepository
 {
    private RpContext _entities;
    public UserRepository(RpContext entities)
    {
        _entities = entities;
    }

    public User GetUserById(int id)
    {
        return _entities.User.Include(x => x.Profile).Where(x => x.Id == id).Where(x => x.Valid == true).FirstOrDefault();
    }

}

public interface IUserRepository
{
    User GetUserById(int id);
}

On my Blazor component I go to the database to get the user record like this:

public void GetUser(int userId)
{
    UserDetails = null;
    UserDetails = UserService.GetUserById(userId);
}

The page has an EditForm that is bound to UserDetails. Calling GetUser populates all fields correctly from the DB on first load of the page. If I then remove a value from any of the fields on the EditForm and then call the GetUser method again, tracking decides that it will use the entity as it stood on the page (without the values I removed) rather than going to the DB and getting a fresh copy (even though I have nulled it out previously).

I could use AsNoTracking but I want to track changes in the case where the user of the page decides to make a change and save it.

It feels like I have not got something quite right with the way I am using the context as this must be an extremely common use case (get a record, abandon, then reload without the abandoned changed).

Can anyone offer any pointers on where I might be going wrong?

5
  • 1
    Are you using any caching service? Commented Aug 29, 2023 at 11:12
  • 1
    You should not be using a single RpContext to do everything. You need to use a DbContextFactory. Search "Blazor DbContextFactory" on here for other answers on the topic and read and understand this MS Docs article - learn.microsoft.com/en-us/ef/core/dbcontext-configuration Commented Aug 29, 2023 at 11:15
  • EF Core always returns the latest results. It doesn't need a CRUD class wrapper either, DbSet is already a single-entity repository, DbContext is a Unit-of-Work, not a database connection Commented Aug 29, 2023 at 11:27
  • I suspect the CRUD class uses Find at some point, which does return an already loaded object. That's not a bug when DbContext is used as intended - as a short-lived Unit-of-Work, disposed as soon as possible. A Unit-of-Work represents a single business transaction and its objects. The CRUD class though is registered as scoped, which means it remains alive for the entire user session. Commented Aug 29, 2023 at 11:30
  • In Blazor Server you can't use a scoped DbContext. This is explained in ASP.NET Core Blazor Server with Entity Framework Core. You need to decide what the boundaries of your unit-of-work/transaction are and use the appropriate code. If the UoW is the entire component, you can create the component in OnInitializedAsync and dispose it in Dispose. If it's shorter, you need to create it in a using block when needed. In both cases you can use a DbContextFactory to get instances from DI Commented Aug 29, 2023 at 11:35

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.