Question

I am using EntityFramework.Extended library to perform batch updates. The only problem is EF does not keep track of the batch updates performed by the library. So when I query the DbContext again it does not return the updated entities.

I found that using AsNoTracking() method while querying disables the tracking and gets fresh data from the database. However, since EF does not keep track of the entities queried with AsNoTracking(), I am not able to perform any update on the queried data.

Is there any way to force EF to get the latest data while tracking changes?

Was it helpful?

Solution

Please try this to refresh a single entity:

Context.Entry<T>(entity).Reload()

Edit: To get fresh data for a collection of entities is worth trying to dispose the DbContext instance after each request.

OTHER TIPS

I stumbled upon this question while searching for a solution to a problem I was having where the navigation properties were not populating after updating the entity. Whenever I attempted to reload the entity from the database, it would grab the entry from the local store instead which would not populate the navigation properties via lazy loading. Instead of destroying the context and recreating one, I found this allowed me to get fresh data with the proxies working:

_db.Entry(entity).State = EntityState.Detached;

The logic behind it was - my update attached the entity so it would track changes to it. This adds it to the local store. Thereafter, any attempts to retrieve the entity with functional proxies would result in it grabbing the local one instead of going out to the db and returning a fresh, proxy-enabled entity. I tried the reload option above, which does refresh the object from the database, but that doesn't give you the proxied object with lazy-loading. I tried doing a Find(id), Where(t => t.Id = id), First(t => t.Id = id). Finally, I checked the available states that were provided and saw there was a "Detached" state. Eureka! Hope this helps someone.

I declared the entity variable, without assignment, as part of the class. This allowed me to dispose of an instance without losing the variable for reference by other methods. I just came across this so it doesn't have alot of runtime under it's belt, but so far it seems to be working fine.

public partial class frmMyForm
{
    private My_Entities entity;

    public frmMyForm()
    {
        InitializeComponent();
    }

    private void SomeControl_Click(object sender, EventArgs e)
    {
        db.SaveChanges();
        db.Dispose();
        entity = new My_Entities();
        //more code using entity ...
}

Stumbled onto this problem. My app wasn't returning fresh data from the database.

These seems to be 3 solutions:

  1. Reload on select: first you select the object, then reload. Loading it twice if it's not cached?

  2. Detach after use: if you forget to detach an object after use, it's going to cause bugs in completely separate parts of the application that are going to be extremely hard to track down.

  3. Disposing the DbContext after use. Definitely seems like the way to go.

I was creating my DbContext instance in the Repository class. If the DbContext is declared at the Repository level, then I have no control over how it gets disposed. That's a no-no. If I create a new DbContext on every call, then I cannot call Select, modify data, and then call Update.

Seems like something is fundamentally missing in my Repository pattern.

After some research on fundamental Repository pattern, I found the solution: Unit of Work pattern alongside the Repository pattern.

This is an excellent article on the Unit of Work pattern

Or this article from Microsoft. What I currently have is the Repository further up in the page, and what's missing is the section "Implement a Generic Repository and a Unit of Work Class"

Basically, instead of injecting repositories into your services, you access all repositories via a UnitOfWork that you inject into your service. It will solve many problems.

public class UnitOfWork : IUnitOfWork
{
    private readonly ApplicationContext _context;
    public UnitOfWork(ApplicationContext context)
    {
        _context = context;
        Developers = new DeveloperRepository(_context);
        Projects = new ProjectRepository(_context);
    }
    public IDeveloperRepository Developers { get; private set; }
    public IProjectRepository Projects { get; private set; }
    public int Complete()
    {
        return _context.SaveChanges();
    }
    public void Dispose()
    {
        _context.Dispose();
    }
}

Remains the question: how to create the IUnitOfWork instance?

If I create it in the class constructor to be injected just like the repository, then it gets created and destroyed exactly the same way and we're back to the same problem. In ASP.NET and MVC, class instances are short-lived so injecting in the constructor may be fine, but in Blazor and desktop apps, class instances are much more long-lived and it's more of a problem.

This article from Microsoft clearly states that Dependency Injection isn't suitable to manage the lifetime of DbContext in Blazor:

In Blazor Server apps, scoped service registrations can be problematic because the instance is shared across components within the user's circuit. DbContext isn't thread safe and isn't designed for concurrent use. The existing lifetimes are inappropriate for these reasons:

  • Singleton shares state across all users of the app and leads to inappropriate concurrent use.
  • Scoped (the default) poses a similar issue between components for the same user.
  • 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.

They suggest using the Factory pattern, which can be implemented like this

/// <summary>
/// Creates instances of UnitOfWork. Repositories and UnitOfWork are not automatically injected through dependency injection,
/// and this class is the only one injected into classes to give access to the rest.
/// </summary>
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
    private readonly IDateTimeService _dateService;
    private readonly DbContextOptions<PaymentsContext> _options;

    public UnitOfWorkFactory(IDateTimeService dateService, DbContextOptions<PaymentsContext> options)
    {
        _dateService = dateService;
        _options = options;
    }

    /// <summary>
    /// Creates a new Unit of Work, which can be viewed as a transaction. It provides access to all data repositories.
    /// </summary>
    /// <returns>The new Unit of Work.</returns>
    public IUnitOfWork Create() => new UnitOfWork(CreateContext(), _dateService);

    /// <summary>
    /// Creates a new DbContext.
    /// </summary>
    /// <returns>The new DbContext.</returns>
    public PaymentsContext CreateContext() => new(_options);
}

Neither IWorkOfUnit nor any repository will be registered into the IoC container. Only IWorkOfUnitFactory.

And finally... how to share a transaction between various services?

I have a SetStatus method that updates the status field in the database. How is this method supposed to know whether it's a stand-alone operation or part of a larger transaction?

Since class-level dependency injection isn't suitable to manage and share the Work of Unit, then the only option is to pass it as parameters to the methods that need it.

I add an optional IUnitOfWork? workScope = null parameter to every method that needs it, and call Save only if this parameter is null. Here's an implementation.

public virtual async Task<TempOrder?> SetStatusAsync(int orderId, PaymentStatus status, IUnitOfWork? workScope = null)
{
    using var unitOfWork = _workFactory.Create();
    var work = workScope ?? unitOfWork;

    var order = await work.Orders.GetByIdAsync(orderId);
    if (order != null)
    {
        order.Status = status;
        work.Orders.Update(order); // DateModified gets set here

        if (workScope == null)
        {
            await work.SaveAsync();
        }
    }
    return order;
}

Another option is to have IUnitOfWorkFactory.Create take the workScope parameter, and when set:

  • Re-use the existing DbContext
  • Do not dispose
  • IUnitOfWork.Save won't submit

My final implementation can be used like this

public virtual async Task<TempOrder?> SetStatusAsync(int orderId, PaymentStatus status, IUnitOfWork? workScope = null)
{
    using var unitOfWork = _workFactory.Create(workScope);

    var order = await unitOfWork.Orders.GetByIdAsync(orderId);
    if (order != null)
    {
        order.Status = status;
        work.Orders.Update(order); // DateModified gets set here
        await unitOfWork.SaveAsync(); // Ignored if workScope != null
    }
    return order;
}

Pheww! That bug was a rabbit hole. It's a pretty long solution but should solve it for good with a solid architecture.

Making the code run on the same context will not yield you updated entities. It will only append new entities created in the database between runs. EF force reload can be done like this:

ObjectQuery _query = Entity.MyEntity;
_query.MergeOption = MergeOption.OverwriteChanges;
var myEntity = _query.Where(x => x.Id > 0).ToList();

For me ... I access my DbContext like this:

_viewModel.Repo.Context

To force EF to hit the database I do this:

_viewModel.Repo.Context = new NewDispatchContext();

Overwriting the current DbContext with a new instance. Then the next time I use my data services they get the data from the database.

Reloading specific entities was not an option for me because I didn't know the exact entity. I also did not want to create a new DbContext as it is injected by DI. So I resorted to the following trick to "reset" the whole context.

            foreach (var entry in db.ChangeTracker.Entries())
            {
                entry.State = EntityState.Detached;
            }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top