I'm trying to update changes that happened in the database for a tracked entity and update my entity.
Follow the code
var user = await _userRepository.GetByUserId(userId);
_userRepository.Update(user); // _dbSet.Update(obj);
_unitOfWork.Commit(); // _entityContext.SaveChanges();
await Task.Delay(delay);
_unitOfWork.AcceptAllChanges(); // _entityContext.ChangeTracker.AcceptAllChanges();
In that time that the application was waiting for the task.delay,
another service made changes to the database for this same entity I am tracking, I try to fetch the changes from the database and update my entity with AcceptAllChanges but it doesn't work.
Does anyone know what could be wrong? Thank you in advance
You want EntityEntry.Reload. Otherwise even if you re-query the entity, EF doesn't update the entity in memory.
db.Entry(user).Reload();
Related
I am using Entity Framework Core 5.0.3, .NET 5.0.103, ASP.NET Core 5 Blazor WebAssebmly. I have -
// Cập nhật
[HttpPut("{id}")]
public async Task<ActionResult> UpdateItem(int id, ProjectWorkCategory newItem)
{
var oldItem = await db.ProjectWorkCategories.FindAsync(id);
if (oldItem == null)
{
return NotFound();
}
// Mã loại công trình.
if (!string.IsNullOrEmpty(newItem.ProjectWorkCategoryCode))
{
oldItem.ProjectWorkCategoryCode = newItem.ProjectWorkCategoryCode;
}
// Tên loại công trình
if (!string.IsNullOrEmpty(newItem.ProjectWorkCategoryName))
{
oldItem.ProjectWorkCategoryName = newItem.ProjectWorkCategoryName;
}
// Diễn giải
if (!string.IsNullOrEmpty(newItem.Description))
{
oldItem.Description = newItem.Description;
}
// Trạng thái theo dõi.
if (newItem.ActiveStatus != oldItem.ActiveStatus)
{
oldItem.ActiveStatus = newItem.ActiveStatus;
}
// Người sửa.
if (!string.IsNullOrEmpty(newItem.ModifiedBy))
{
oldItem.ModifiedBy = newItem.ModifiedBy;
}
oldItem.ModifiedDate = DateTime.Now;
//db.ProjectWorkCategories.Add(oldItem);
db.Entry(oldItem).State = EntityState.Modified;
await db.SaveChangesAsync();
return NoContent();
}
What is the different between
db.ProjectWorkCategories.Add(oldItem);
await db.SaveChangesAsync();
versus
db.Entry(oldItem).State = EntityState.Modified;
await db.SaveChangesAsync();
db.ProjectWorkCategories.Add(oldItem);
await db.SaveChangesAsync();
the above code adds the entity to database. if 'oldItem' has Unique Id, then you will get exception trying to save it, otherwise it would save duplicate record.
db.Entry(oldItem).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
await db.SaveChangesAsync();
this is the correct way to update an entity.
A record will be added to the database when you use the add method. But if you use the 'State = Microsoft.EntityFrameworkCore.EntityState.Modified', your existing record will be edited in the database.
This one is adding a new item, and as mentioned by #Navid it will through an error if it has a unique Id.
db.ProjectWorkCategories.Add(oldItem);
await db.SaveChangesAsync();
If you want to update you should be using Update method:
db.ProjectWorkCategories.Update(oldItem);
await db.SaveChangesAsync();
This one also can be used for updating an existing item.
db.Entry(oldItem).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
await db.SaveChangesAsync();
Deep dive:
Updated to explain the points mentioned by #atiyar comment
There is a some differences between using Add, Update methods and changing the entity state EntityState.Modified.
When using (1)Add Update method, the framework will start to track this entity as EntityState.Added if it has no Id assigned, or will track it as EntityState.Modified if it has an Id.
DbContext.Update Method:
For entity types with generated keys if an entity has its primary key value set then it will be tracked in the EntityState.Modified state. If the primary key value is not set then it will be tracked in the EntityState.Added state.
(2) When using Update method the framework will track the entity and all its referenced entities with EntityState.Modified flag.
(2) I still think this is valid. Please see the explanation in this document: Where this method differs from explicitly setting the State property, is in the fact that the context will begin tracking any related entities (such as a collection of books in this example) in the Modified state, resulting in UPDATE statements being generated for each of them. If the related entity doesn't have a key value assigned, it will be marked as Added, and an INSERT statement will be generated.
When using EntityState.Modified the framework will track this entity as EntityState.Modified. But all referenced entities will be tracked by EntityState.Unchanged flag, and you may need to change the state of each referenced entity to Modified manually.
Another difference, when calling SaveChanges() after Update method, only changed values of the entity will be submitted to the DB. But when you call SaveChanges() after using EntityState.Modified all values will be submitted to the DB since all fields has been marked as Modified.
(3) That is my mistake, I used to use another library which we can use to send only modified fields for update, but this is not valid for EF Update method.
EDIT
With reference to your update method, you can use both Update or EntityState.Modified. The easy way is to use Update(entity). In some advanced cases when the entity state could be Detached (un-tracked) I use EntityState flags to check if the entity is attached (tracked) or not, then I do modify its state accordingly.
Finally, instead of avoiding the result of SaveChangesAsync() I recommend to check the value and return:
var success = await db.SaveChangesAsync();
if(success > 0)
// log or message for success
else
// log or message for failure
Entity Framework tracks an entity with a State value, which you can check by -
var state = db.Entry(myEntity).State;
And when you call SaveChanges(), EF tries to insert, update or delete an entity based on its State value.
What Add() and EntityState.Modified do :
The Add() method marks an entity as Added (sets the State value to EntityState.Added), whether the entity has a primary key value or not, and when you call SaveChanges() the next time, EF will try to insert that entity.
db.Entry(myEntity).State = EntityState.Modified; sets the State value of the entity explicitly to Modified, which basically tells EF that "this entity already exits in database, and has been modified". Therefore, on the next SaveChanges() call EF will try to update the entity with its new values.
What Add() and EntityState.Modified will do in your scenario :
You are trying to update an entity not to insert it. For update operation you are not supposed to use the Add() method.
You fetched an existing entity from the database (and it has its primary key value in it) with -
var oldItem = await db.ProjectWorkCategories.FindAsync(id);
At this point the entity will be in Unchanged state. Then as soon as you set a new value to any of its properties, the entity gets modified and will automatically go in Modified state. But then if you call -
db.ProjectWorkCategories.Add(oldItem);
the Add() method will put the entity in Added state. At this point calling SaveChanges() will throw an exception, because EF is trying to insert an entity which already exists in database (the primary key violation).
In your scenario, by setting -
db.Entry(oldItem).State = EntityState.Modified;
you are explicitly marking the entity as Modified, and on the next SaveChanges() call EF will update the entity with its new values. But the thing is, you don't even need the above line. As I mentioned earlier, fetching an existing entity and changing any of its property value will automatically put the entity in Modified state. Therefore, after setting the new values you can directly call -
await db.SaveChangesAsync();
and your entity will get updated as expected.
I am adding a new entity to the DbContext with a navigational property passed from the client but instead of EF recognising that the navigational property entity already exist, it tries to re-insert it which fails because you cannot have duplicate primary keys.
var profile = _mapper.Map<Profile>(profileDto);
profile.User.LockoutEnabled = true;
profile.User.Password = new PasswordHasher<User>().HashPassword(profile.User, "*********");
profile.Company = null; // Doing this works fine but otherwise it fails.
await _dataContext.Set<Profile>().AddAsync(profile);
await _dataContext.SaveChangesAsync();
profile.Company = await _dataContext.Set<Company>().FindAsync(profile.CompanyId);
return _mapper.Map<ProfileCreateDto>(profile);
Since nulling the Company entity or changing the client to pass only the CompanyId instead of the Company value works fine, I still want to understand why EF is try to re-insert an existing entity.
EF is trying to re-insert the Company entity because you are using the AddAsync method to insert your Profile entity.
The AddAsync method causes the entity in question (Profile) and all it's related entities (e.g. Company) present in the entity-graph to be marked as Added. An entity marked as Added implies - This is a new entity and it will get inserted on the next SaveChanges call. See details - DbSet<TEntity>.AddAsync().
As a general solution, in a disconnected scenario, when creating new entity with an entity-graph (with one or more related entities) use the Attach method instead.
The Attach method causes any entity to be marked as Added only if it doesn't have the primary-key value set. Otherwise, the entity is marked as Unchanged. An entity marked as Unchanged implies - This entity already exists in the database and it might get updated on the next SaveChanges call. See details - DbSet<TEntity>.Attach()
I hope that meets your curiosity.
I have this problem where I have 2 entities connected by foreign key.
AEntity: id, idOfEntityB (foreign key, constraint), fields...
BEntity: id, fields...
I save both of them to the database with SaveChanges(), later when I try to get AEntity's idOfEntityB, I succeed but when I try to get BEntity according to the id I got from AEntity, I get nothing:
context.AEntities.Add(new AEntity {
BEntity = new BEntity { ... }
});
context.SaveChanges();
.
.
.
var id1 = context.AEntities.Select(x => x.idOfEntityB);
var bEntities = context.BEntities.Where(x => id1.Contains(x.id));
bEntities has nothing in it. but the fact I was able to have values in id1 is even more confusing since they have foreign key relations (with constraint) and furthermore, id could not be created if it was not saved to the DB.
Later, when I look in the DB I see both entities as should be.
It happens sometimes and I cant reproduce the problem, I cant give more then this as an example since there's a lot of code, I believe it has something to do with caching, and therefore would like to ask if something like that is possible or not and how.
is there a way entities are saved to the DB while the context (a different one used from the context that saved) does not hold all of them in completion?
This is likely the issue you are encountering if you are relying on seeing changes between state changes between different DbContext instances. When a DbContext has loaded entities, then another DbContext instance makes changes to those records or the records change behind the scenes in the database, that original DbContext will not refresh the entities from the database.
EF does support the ability to reload entities from the database, but when dealing with child collections it gets a bit more complicated to perform a full refresh. You effectively need to tell the DbContext to forget all of the child collections, stop tracking the parent, clear the parent's child collection, then re-attach and reload the child collection. I recently covered this in the answer for this question: Replacing a entity collection in Entity Framework Core causes DbContext to fetch the new values when not saved to db. How to reload the collection?
Ultimately a DbContext lifespan should be kept as short as possible.
I can add an entity but when I try to update Entity Framework doesn't work. This is what I'm doing:
Get model in action filter in Web API. I lookup some data about this model in the database and use that to change/hydrate different parts of the model. Yes, it has to be exactly that way.
Controller runs. Controller does nothing except pass the model to the repository to be saved.
Repository checks if a model with that ID exists in the database. If it does it tries to attach and then save.
Code:
_db.Airplanes.Attach(airplane);
// _db.Entry(airplane).State = EntityState.Modified; // doesn't do anything
_db.SaveChanges();
It doesn't work. Instead, it throws this error message:
System.Data.Entity.Core.OptimisticConcurrencyException: 'Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=472540 for information on understanding and handling optimistic concurrency exceptions.'
Perhaps the problem is that I originally got the entity way back in the Action Filter and it has since gone out of context or something? In any event I don't know why Entity Framework won't let me just add it back.
Edit: I'm being forced to edit this to explain how this isn't solved by another question. ...umm, it isn't. The burden of proof should be on the person making the claim.
The answer was that in my action filter I was calling AirplaneRepository to get the ID of the airplane. Although I only used the ID I guess it was still adding the entire airplane to its black box (entity framework's black box). The solution was to detach the airplane from EF.
AirplaneRepository.cs
async Task<Airplane> GetAirplane(int partId, bool detachFromEF = false)
{
var airplane = await _db.Airplane.SingleOrDefaultAsync(c => c.partId == partId);
if(detachFromEF)
{
_db.Entry(airplane).State = EntityState.Detached; // <-- the fix
}
return airplane;
}
async Task<int?> GetAirplaneId(int partId)
{
var airplane = await GetAirplane(partId, true);
return airplane == null ? (int?)null : airplane.AirplaneId;
}
}
I have code which I thought was working for Entity Framework update sql server table. I do not get an error upon stepping through the code.
var newsToUpdate = db.tblTips.Find(id);
//if(TryUpdateModel(newsToUpdate, "",))
try
{
db.SaveChanges();
}
catch (RetryLimitExceededException)
{
ModelState.AddModelError("", "unable to save changes");
}
Notice I have that if(TryUpdateModel(... line I don't recall if I had used that when records in the database table DID update.
I can see that the Model has the correct id
DB table is not updated.
What can I do to figure out WHY in C# visual studio as to it not updating the record? There are no errors.
UPDATE:
my model is tblTips , signature on my method is
public ActionResult AddNews(tblTips tips, int? groupid, int? id)
Seems that the update page is not knowing about other columns and trying to update all the columns of which it is trying to update a column to null in which in the db table that column is a "not null"
Is it "ok" ( yes yes i know i should use a viewmodel) to do the following to exclude a column from the update?
db.Entry(tips).State = EntityState.Modified;
db.Entry(tips).Property(x => x.createdby).IsModified = false;
db.SaveChanges();
I guess I should not believe everything I read on Stackoverflow , that failed above with
Attaching an entity of type 'BRM.Data.Models.tblTips' failed because
another entity of the same type already has the same primary key
value. This can happen when using the 'Attach' method or setting the
state of an entity to 'Unchanged' or 'Modified' if any entities in the
graph have conflicting key values. This may be because some entities
are new and have not yet received database-generated key values. In
this case use the 'Add' method or the 'Added' entity state to track
the graph and then set the state of non-new entities to 'Unchanged' or
'Modified' as appropriate.
As it stands, your code now says "get this thing out of the database, then save any changes I just made", but it never actually changes the thing you got out of the database.
Most likely your problem came when you commented out the call to TryUpdateModel(), which ASP.NET MVC uses to make modifications to the given object based on parameters passed into the HTTP request.
I highly recommend using a version control system to make it easier to see what you've changed at any given point in time, and undo things you changed by mistake.
If you look at the documentation for DbContext.SaveChanges, you'll see it mentions the following...
Saves all changes made in this context to the underlying database.
Since you've made no changes (in the shown code), the expected result is happening.
Also, the api public virtual int SaveChanges() suggests you can get the number of records updated from SaveChanges, e.g.
var recordsUpdated = db.SaveChanges();
In the code as you posted it, there are no changes made to any data. Entity Framework correctly determines this and does not issue any update statements.
It appears that the TryUpdateModel call is what makes changes, though I wouldn't be able to tell without looking at its code. Since this call is commented out, the changes aren't happening.