2

I have a backend service that has its own scoped context. This service polls external appliances every minute and runs on a minute timer to repeat the process. Every minute, the service also checks for appliance state changes (start, stop, run, off) which need to be applied. These state changes, if any, will have come from a Razor Page server Post method using the DBcontext and DI to update the DeployedAppliance in the DB.

The issue is as follows: the Razor Page successfully updates the DeployedAppliance DB (state enum). However, the backend service scope, created anew every minute, is not getting the new DeployedAppliance state.

Here is some relevant backend code:

    public class ApplianceService : IHostedService, IDisposable
    {
        private Timer _timer;
        private readonly IServiceScopeFactory _serviceScopeFactory;
        private IApplianceManager applianceMgr;

        public Task StartAsync(CancellationToken stoppingToken)
        {
            applianceMgr = new ApplianceManagerImpl();
            ... 
           _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(60));
        }

        private void DoWork(object state)
        {
            // Create local scope. 
            using (var scope = _serviceScopeFactory.CreateScope())
            {
                var _db = scope.ServiceProvider.GetRequiredService<DbContext>();
                applianceMgr.Db = _db;
                applianceMgr.DoRemoteAppliancePolling();
                applianceMgr.CheckForApplianceStateChange();
            }
         }        
     }

     public class ApplianceManager : IApplianceManager 
     {   
        DeployedAppliance DeployedAppliance {get; set;}

        ...
        public void CheckForApplianceStateChange()      
        {
            // This DeployedAppliance object never gets the change in state that is actually in the Db
            DeployedAppliance = Db.DeployedAppliances.Find(ApplianceDeploymentID);
            ...
        }
     }

I have looked at stackoverflow submissions to refresh Db entities. I have tried the following code to refresh the Db.

// refresh DBcontext
var context = ((IObjectContextAdapter)Db).ObjectContext;
var refreshableObjects = Db.ChangeTracker.Entries().Select(c => c.Entity).ToList();
context.Refresh(RefreshMode.StoreWins, refreshableObjects);

Howevever, this code throws: InvalidCastException: Unable to cast object of type 'Pilot.Models.Database.DbContext' to type 'System.Data.Entity.Infrastructure.IObjectContextAdapter'. Note that the DbContext inherits from IdentityDbContext.

1 Answers1

0

You can do the following:

  1. Get a list of added/modified entities
  2. Perform SaveChanges()
  3. Detach the added/modified entities from your context.

Here is an example to do this by using ObjectContext:

IEnumerable<object> entitiesCollection = from e in objectContext.ObjectStateManager.GetObjectStateEntries
                                 (EntityState.Added | EntityState.Modified)
                                      select e.Entity;

objectContext.SaveChanges();

foreach (object entity in entitiesCollection)
{
    if (entity!= null)
    {
        objectContext.Detach(entity);
    }
}

In this way, since the added/modified entities are detached from your context, EntityFramework will have to reload them from the database so you will get the new updated values.

EDIT:

Regarding your comment about how to do this in EF Core:

As ajcvickers posted here on Apr 17, 2018:

There isn't currently an equivalent in EF Core. context.Entry(foo).Reload() is currently the closest thing, but it only works on a single entity. In general, we recommend using short-lived contexts that cover a single unit-of-work. This usually makes reloading from the store unnecessary.

Also, as posted here by the same person on Jul 15, 2019, development of this feature has been added to EF Product backlog:

Putting this on the backlog to consider behaviors for DbContext in EF Core similar to ObjectContext.Refresh in the old stack.

gwt
  • 2,331
  • 4
  • 37
  • 59
  • I've tried this code to get the ObjectContext, however, it doesn't work with the IdentityDbContext: ObjectContext objectContext = ((IObjectContextAdapter)_db).ObjectContext; It throws a runtime exception – TommyBoyWest Mar 10 '20 at 10:37
  • Here are the exception details: Unable to cast object of type 'BMS_Pilot.Models.Database.BMSDbContext' to type 'System.Data.Entity.Infrastructure.IObjectContextAdapter' – TommyBoyWest Mar 10 '20 at 10:59
  • How do you do this type of entity manipulation exclusively with EntityFrameworkCore? – TommyBoyWest Mar 10 '20 at 13:44
  • @TommyBoyWest You might also find https://stackoverflow.com/questions/32594262/get-list-of-modified-objects-within-entity-framework-7 useful on how to get the list of modified entities using DbContext. – gwt Mar 10 '20 at 14:39
  • @TommyBoyWest This links also could be useful for detaching using DbContext: https://stackoverflow.com/questions/7678665/attaching-and-detaching-entities-from-context-correctly-in-ef4-1/7693732#7693732 – gwt Mar 10 '20 at 14:46
  • @TommyBoyWest Your wellcome, glad it helped. – gwt Mar 10 '20 at 14:49