How to refresh an Entity Framework Core DBContext?
Dependency Injection and DbContextYou 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-livedDbContext
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 throwDbConcurrencyException
(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.
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 using
s):
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
How to Validate a Datetime in C#
C# ASP.NET MVC Download a File Via Ajax
Post Json Array to MVC Controller
Generate a Zip File from Azure Blob Storage Files
How to Delete All Files in an Azure File Storage Folder
Linq - Select Date from Datetime
How to Call a Method Daily, At Specific Time, in C#
How to Ignore the Certificate Check When Ssl
Converting a CSV File to Json Using C#
How to Disable Uwp App Suspension
How to Remove Illegal Characters from Path and Filenames
How to Print the Elements With Text Value That Contains in a List Selenium C#
Unicode Characters Replace from String Using C#
Instantiating an Iformfile from a Physical File
Configurationmanager.Appsettings Getting Null
How to Check, If Bitmask Contains Bit
How to Iterate Through the Following Json Using C#
How to Add a Run Button and Compile Button on the Toolbar in Visual Studio