I have a Service Object Update
public bool Update(object original, object modified) { var originalClient = (Client)original; var modifiedClient = (Client)modified; _context.Clients.Update(originalClient); //<-- throws the error _context.SaveChanges(); //Variance checking and logging of changes between the modified and original }
This is where I am calling this method from:
public IActionResult Update(DetailViewModel vm) { var originalClient = (Client)_service.GetAsNoTracking(vm.ClientId); var modifiedClient = (Client)_service.Fetch(vm.ClientId.ToString()); // Changing the modifiedClient here _service.Update(originalClient, modifiedClient); }
Here is the GetAsNotTracking
method:
public Client GetAsNoTracking(long id) { return GetClientQueryableObject(id).AsNoTracking().FirstOrDefault(); }
Fetch
method:
public object Fetch(string id) { long fetchId; long.TryParse(id, out fetchId); return GetClientQueryableObject(fetchId).FirstOrDefault(); }
GetClientQueryableObject
:
private Microsoft.Data.Entity.Query.IIncludableQueryable<Client, ActivityType> GetClientQueryableObject(long searchId) { return _context.Clients .Where(x => x.Id == searchId) .Include(x => x.Opportunities) .ThenInclude(x => x.BusinessUnit) .Include(x => x.Opportunities) .ThenInclude(x => x.Probability) .Include(x => x.Industry) .Include(x => x.Activities) .ThenInclude(x => x.User) .Include(x => x.Activities) .ThenInclude(x => x.ActivityType); }
Any ideas?
I have looked the following articles / discussions. To no avail:ASP.NET GitHub Issue 3839
UPDATE:
Here are the changes to GetAsNoTracking
:
public Client GetAsNoTracking(long id) { return GetClientQueryableObjectAsNoTracking(id).FirstOrDefault(); }
GetClientQueryableObjectAsNoTracking
:
private IQueryable<Client> GetClientQueryableObjectAsNoTracking(long searchId) { return _context.Clients .Where(x => x.Id == searchId) .Include(x => x.Opportunities) .ThenInclude(x => x.BusinessUnit) .AsNoTracking() .Include(x => x.Opportunities) .ThenInclude(x => x.Probability) .AsNoTracking() .Include(x => x.Industry) .AsNoTracking() .Include(x => x.Activities) .ThenInclude(x => x.User) .AsNoTracking() .Include(x => x.Activities) .ThenInclude(x => x.ActivityType) .AsNoTracking(); }
When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder. EnableSensitiveDataLogging' to see the conflicting key values.
AsNoTracking(IQueryable)Returns a new query where the entities returned will not be cached in the DbContext or ObjectContext. This method works by calling the AsNoTracking method of the underlying query object.
Without overriding EF track system, you can also Detach the 'local' entry and attach your updated entry before saving :
// var local = _context.Set<YourEntity>() .Local .FirstOrDefault(entry => entry.Id.Equals(entryId)); // check if local is not null if (local != null) { // detach _context.Entry(local).State = EntityState.Detached; } // set Modified flag in your entry _context.Entry(entryToUpdate).State = EntityState.Modified; // save _context.SaveChanges();
UPDATE: To avoid code redundancy, you can do an extension method :
public static void DetachLocal<T>(this DbContext context, T t, string entryId) where T : class, IIdentifier { var local = context.Set<T>() .Local .FirstOrDefault(entry => entry.Id.Equals(entryId)); if (!local.IsNull()) { context.Entry(local).State = EntityState.Detached; } context.Entry(t).State = EntityState.Modified; }
My IIdentifier
interface has just an Id
string property.
Whatever your Entity, you can use this method on your context :
_context.DetachLocal(tmodel, id); _context.SaveChanges();
public async Task<Product> GetValue(int id) { Product Products = await _context.Products.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id); return Products; }
AsNoTracking()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With