How to Refresh an Entity Framework Core Dbcontext

How to refresh an Entity Framework Core DBContext?

Dependency Injection and DbContext

You mention that when you try to recreate your DbContext, you get an error about the context being managed by your dependency injection (DI) system. There are two different styles of using a dependency injection system for object creation. The DI can either create a global singleton instance that is shared as a service between all consumers or it can create an instance per scope/unit of work (e.g., per request in a web server).

If your DI system is configured to create a single global shared instance of DbContext, then you will encounter various problems associated with long-lived DbContext.

  • DbContext, by design, never automatically removes objects from its cache because it is not designed to be long-lived. Thus, a long-lived DbContext will retain memory wastefully.
  • Your code will never see changes to items loaded into its cache without manually reloading each entity it loads.
  • DbContext only allows one query to run at any time and is not threadsafe. If you try to run multiple queries on a globally shared instance, it will throw DbConcurrencyException (at least on its async interface, not sure about its sync interface).

Thus, the best practice is to use a single DbContext per unit of work. Your DI system can help you with this by being configured to provide a fresh instance for each request your application processes within a scope. For example, ASP.NET Core’s Dependency Injection system supports scoping instances by request.

Refreshing a Single Entity

The easiest way to get fresh data is to create a new DbContext. However, within your unit of work, or within the constraints of the granularity of scoping provided by your DI system, you may trigger an external process which is supposed to modify your entity directly in the database. You may need to see that change before exiting your DI’s scope or completing your unit of work. In that case, you can force a reload by detaching your instance of the data object.

To do this, first get the EntityEntry<> for your object. This is an object which lets you manipulate DbContext’s internal cache for that object. You can then mark this entry detached by assigning EntitytState.Detached to its State property. I believe that this leaves the entry in the cache but causes the DbContext to remove and replace it when you actually load the entry in the future. What matters is that it causes a future load to return a freshly loaded entity instance to your code. For example:

var thing = context.Things.Find(id);
if (thing.ShouldBeSentToService) {
TriggerExternalServiceAndWait(id);

// Detach the object to remove it from context’s cache.
context.Entities(thing).State = EntityState.Detached;

// Then load it. We will get a new object with data
// freshly loaded from the database.
thing = context.Things.Find(id);
}
UseSomeOtherData(thing.DataWhichWasUpdated);

How to Refresh DbContext

I just found that the Enumerable result should be evaluated because the Refresh method gets it as object and doesn't evaluate it.

var context = ((IObjectContextAdapter)myDbContext).ObjectContext;
var refreshableObjects = (from entry in context.ObjectStateManager.GetObjectStateEntries(
EntityState.Added
| EntityState.Deleted
| EntityState.Modified
| EntityState.Unchanged)
where entry.EntityKey != null
select entry.Entity).ToList();

context.Refresh(RefreshMode.StoreWins, refreshableObjects);

And I prefer the following:

var refreshableObjects = myDbContext.ChangeTracker.Entries().Select(c=>c.Entity).ToList();
context.Refresh(RefreshMode.StoreWins, refreshableObjects);

How to reload collection in EF Core 2.x?

Unfortunately although EntityEntry has Reload method, there is no such method for ReferenceEntry and CollectionEntry (or in general, for NavigationEntry which the base of the previous two). And Reload methods refreshes just the primitive properties, so it can't be used to refresh the navigation properties.

Fortunately it's not that hard to create a custom one. It needs to detach (or reload?) all the current collection items, set IsLoaded to false and CurrentValue to null before calling Load.

Something like this (put it in a static class of your choice and add the necessary usings):

public static void Reload(this CollectionEntry source)
{
if (source.CurrentValue != null)
{
foreach (var item in source.CurrentValue)
source.EntityEntry.Context.Entry(item).State = EntityState.Detached;
source.CurrentValue = null;
}
source.IsLoaded = false;
source.Load();
}

so you can use the desired

_dbContext.Entry(blog).Collection(b => b.Posts).Reload();

Refresh entity instance with DbContext

You must use this:

public void Refresh(Document instance)
{
_ctx.Entry<Document>(instance).Reload();
}

How to refresh EntityFramework core 5 model or query filter cache?

Well, it is bad to use static variable. EF Core supports variables in filters if they are part of DbContext

Create property in your DbContext TenantId and initialize, for example in constructor or using default initialization.

public class MyDbContext: DbContext
{
public int TenantId { get; set; } = SindikatDataSetBase.TenantId;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<SomeEntity>()
.HasQueryFilter(mm => EF.Property<int>(mm, "TenantId") == TenantId);
}
}

After that each new MyDbContext will catch your static variable and EF can create correct query.

UPDATE

How to avoid configuring parametrized QueryFilter inside DbContext

Idea is simple - mimic that we are already in DbContext's metod.

MyDbContext ctx = null;
m.Builder.HasQueryFilter(mm => EF.Property<int>(mm, "TenantId") == ctx.TenantId);


Related Topics



Leave a reply



Submit