Entity framework, can't update entity - asp.net-mvc

I am trying to update entity but the entity framework do not save changes
this is my code
public bool updateUser(user CUser)
{
CurrentUser = (CustomPrincipal)HttpContext.Current.User;
user userToBeUpdated = context.users.Where(user => user.user_id == CurrentUser.Id).FirstOrDefault();
userToBeUpdated = CUser;
context.SaveChanges();
return true;
}
does there anything wrong in my code?
Update
public bool updateUser(user CUser)
{
CurrentUser = (CustomPrincipal)HttpContext.Current.User;
user userToBeUpdated = context.users.Where(user => user.user_id == CurrentUser.Id).FirstOrDefault();
CUser.user_id = userToBeUpdated.user_id;
CUser.date_created = userToBeUpdated.date_created;
if (CUser.Password == "hidden")
CUser.Password = userToBeUpdated.Password;
else
CUser.Password = new Hash().GetBcryptHashedPassword(CUser.Password);
if (CUser.UDTO.File.ContentLength > 0)
{
FileHelper ProfilePicture = new FileHelper(CUser.UDTO.File, "jpg,jpeg,png", 5, true, 500, 500);
if (!checkUserPersonalImage(CUser.UDTO.File, CUser, ProfilePicture, CurrentUser))
{
return false;
}
CUser.PersonalImage = ProfilePicture.uploadFile();
}
else
{
CUser.PersonalImage = userToBeUpdated.PersonalImage;
}
if (!checkRoleValidity(CUser, CurrentUser, false, false))
{
return false;
}
if (!checkDepartmentValidity(CUser, CurrentUser, false, false))
{
return false;
}
userToBeUpdated = CUser;
context.Entry(userToBeUpdated).State = System.Data.Entity.EntityState.Modified;
context.SaveChanges();
new UserHelperMethods().updateUserHolderLists(CurrentUser.Company, CUser);
CUser.UDTO.RedirectTo = "AddNewUser";
CUser.UDTO.Message="<div class='success'>User has been updated successfully</div>";
CUser.UDTO.Company = CurrentUser.Company;
return true;
}
this is the complete function and I got the following error
Attaching an entity of type 'workflow.Models.user' 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.

I used to update a field in the entity by changing its value and then call thecontext.SaveChanges() for example userToBeUpdated.X = 15; context.SaveChanges(); and then it saves the changes so I think that userToBeUpdated = CUser changes works the same like userToBeUpdated .X = 15
You are quite wrong in assuming that userToBeUpdated.X=15 and userTobeUpdated=CUser does the same thing. The original object that the userToBeUpdated variable referenced is still tracked by EF. After that you change the reference of the userToBeUpdated variable to an instance of another object and attach it to the DbContext. The dbcontext now tries to track two objects: the user it retrieved from the database and a second object that has the same primary key. EF balks at that and throws an exception.
So the .X=15 approach is the correct one. You should update all relevant properties on the userToBeUpated from CUser and then save changes.

Related

Multiple objects with the same key error

Hi I'm getting following error while updating the record
An object with the same key already exists in the ObjectStateManager.
The ObjectStateManager cannot track multiple objects with the same
key.
Here's my Edit (Post) Action.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,Name,Type,Weightage,Description,ParentId")] HiringFactor hiringfactor)
{
if (ModelState.IsValid)
{
var childCount = 0;
var indent = "";
var parentId = hiringfactor.ParentId;
HiringFactor parentFactor = db.HiringFactors.Find(parentId);
if (parentFactor != null)
{
childCount = parentFactor.ChildHiringFactors.Count;
indent = parentFactor.Indent + "." + (childCount + 1);
}
else
{
var parentCount = db.HiringFactors.Count(hf => (hf.ParentId == null || hf.ParentId == 0));
indent = (parentCount + 1).ToString();
}
hiringfactor.Indent = indent;
db.Entry(hiringfactor).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ParentFactors = new SelectList(db.HiringFactors, "Id", "IndentName", hiringfactor.ParentId);
return View(hiringfactor);
}
Is it becaus of I'm dealing with objects of Same Type within DB Context?
The problem seems originated from EntityState.Modified before SaveChanges method, assuming hiringfactor is new entity as modification target:
db.Entry(hiringfactor).State = EntityState.Modified;
By convention, you can't re-attach a model once the entity has been loaded by the same key value. Try using this code right before SaveChanges:
db.Entry(parentFactor).CurrentValues.SetValues(hiringfactor);
Another way to set modified entity is using AsNoTracking when doing Find method like this:
HiringFactor parentFactor = db.HiringFactors.AsNoTracking().Find(parentId);
Similar issues:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key

Error in Updating dbModel of entity framework

This blow code for my post edit action :
public ActionResult EditProduct(EditProductModel viewModel,HttpPostedFileBase file)
{
if (Session["AdminId"] != null && Session["AdminName"] != null)
{
repository = new Repository();
var pro = repository.FindProductById(viewModel.Id);
// Automappper configoration.
var config = new MapperConfiguration(cgf => cgf.CreateMap<EditProductModel, Product>());
var mapper = config.CreateMapper();
pro = mapper.Map<Product>(viewModel);
repository.UpdateProduct(pro);// Error
repository.SaveChanges();
return RedirectToAction("ShowProduct",new{id = AdminId});
}
return RedirectToAction("AdminLogin");
}
The repository.UpdateProduct() Containing this code:
public void UpdateProduct(Product obj)
{
entities.Entry(obj).State = EntityState.Modified;
}
But this top code throws this error :
Attaching an entity of type 'MobileShop.Models.Product' 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.
How I can solve it?
Your pro object is already loaded from context, and it is tracked by EF. Changing it's state to modified in Update method is redundant, and hence giving this error.
So you can either skip the code in your Update method that changes the state,
OR comment out the following lines, because you are anyway building your entity from viewModel
repository = new Repository();
var pro = repository.FindProductById(viewModel.Id);
As a rule; if an object has been loaded from context, dont attach it
And if it has been created outside the context, set its state to modified.
A good read - https://msdn.microsoft.com/en-us/data/jj592676.aspx
Add this lines for updating and saving:
using (var Context = new MobileDatabase_1_Entities())
{
Context.Entry(pro).State = EntityState.Modified;
Context.SaveChanges();
}

Best Way to Update only modified fields with Entity Framework

Currently I am doing like this:
For Example:
public update(Person model)
{
// Here model is model return from form on post
var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
db.Entry(oldobj).CurrentValues.SetValues(model);
}
It works, but for example,
I have 50 columns in my table but I displayed only 25 fields in my form (I need to partially update my table, with remaining 25 column retain same old value)
I know it can be achieve by "mapping columns one by one" or by creating "hidden fields for those remaining 25 columns".
Just wondering is there any elegant way to do this with less effort and optimal performance?
This is a very good question. By default I have found that as long as change tracking is enabled (it is by default unless you turn it off), Entity Framework will do a good job of applying to the database only what you ask it to change.
So if you only change 1 field against the object and then call SaveChanges(), EF will only update that 1 field when you call SaveChanges().
The problem here is that when you map a view model into an entity object, all of the values get overwritten. Here is my way of handling this:
In this example, you have a single entity called Person:
Person
======
Id - int
FirstName - varchar
Surname - varchar
Dob - smalldatetime
Now let's say we want to create a view model which will only update Dob, and leave all other fields exactly how they are, here is how I do that.
First, create a view model:
public class PersonDobVm
{
public int Id { get; set; }
public DateTime Dob { get; set; }
public void MapToModel(Person p)
{
p.Dob = Dob;
}
}
Now write the code roughly as follows (you'll have to alter it to match your context name etc):
DataContext db = new DataContext();
Person p = db.People.FirstOrDefault();
// you would have this posted in, but we are creating it here just for illustration
var vm = new PersonDobVm
{
Id = p.Id, // the Id you want to update
Dob = new DateTime(2015, 1, 1) // the new DOB for that row
};
vm.MapToModel(p);
db.SaveChanges();
The MapToModel method could be even more complicated and do all kinds of additional checks before assigning the view model fields to the entity object.
Anyway, the result when SaveChanges is called is the following SQL:
exec sp_executesql N'UPDATE [dbo].[Person]
SET [Dob] = #0
WHERE ([Id] = #1)
',N'#0 datetime2(7),#1 int',#0='2015-01-01 00:00:00',#1=1
So you can clearly see, Entity Framework has not attempted to update any other fields - just the Dob field.
I know in your example you want to avoid coding each assignment by hand, but I think this is the best way. You tuck it all away in your VM so it does not litter your main code, and this way you can cater for specific needs (i.e. composite types in there, data validation, etc). The other option is to use an AutoMapper, but I do not think they are safe. If you use an AutoMapper and spelt "Dob" as "Doob" in your VM, it would not map "Doob" to "Dob", nor would it tell you about it! It would fail silently, the user would think everything was ok, but the change would not be saved.
Whereas if you spelt "Dob" as "Doob" in your VM, the compiler will alert you that the MapToModel() is referencing "Dob" but you only have a property in your VM called "Doob".
I hope this helps you.
I swear by EntityFramework.Extended. Nuget Link
It lets you write:
db.Person
.Where(x => x.ID == model.ID)
.Update(p => new Person()
{
Name = newName,
EditCount = p.EditCount+1
});
Which is very clearly translated into SQL.
Please try this way
public update(Person model)
{
// Here model is model return from form on post
var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
// Newly Inserted Code
var UpdatedObj = (Person) Entity.CheckUpdateObject(oldobj, model);
db.Entry(oldobj).CurrentValues.SetValues(UpdatedObj);
}
public static object CheckUpdateObject(object originalObj, object updateObj)
{
foreach (var property in updateObj.GetType().GetProperties())
{
if (property.GetValue(updateObj, null) == null)
{
property.SetValue(updateObj,originalObj.GetType().GetProperty(property.Name)
.GetValue(originalObj, null));
}
}
return updateObj;
}
I have solved my Issue by using FormCollection to list out used element in form, and only change those columns in database.
I have provided my code sample below; Great if it can help someone else
// Here
// collection = FormCollection from Post
// model = View Model for Person
var result = db.Person.Where(x => x.ID == model.ID).SingleOrDefault();
if (result != null)
{
List<string> formcollist = new List<string>();
foreach (var key in collection.ToArray<string>())
{
// Here apply your filter code to remove system properties if any
formcollist.Add(key);
}
foreach (var prop in result.GetType().GetProperties())
{
if( formcollist.Contains(prop.Name))
{
prop.SetValue(result, model.GetType().GetProperty(prop.Name).GetValue(model, null));
}
}
db.SaveChanges();
}
I still didn't find a nice solution for my problem, so I created a work around. When loading the Entity, I directly make a copy of it and name it entityInit. When saving the Entity, I compare the both to see, what really was changed. All the unchanged Properties, I set to unchanged and fill them with the Database-Values. This was necessary for my Entities without Tracking:
// load entity without tracking
var entityWithoutTracking = Context.Person.AsNoTracking().FirstOrDefault(x => x.ID == _entity.ID);
var entityInit = CopyEntity(entityWithoutTracking);
// do business logic and change entity
entityWithoutTracking.surname = newValue;
// for saving, find entity in context
var entity = Context.Person.FirstOrDefault(x => x.ID == _entity.ID);
var entry = Context.Entry(entity);
entry.CurrentValues.SetValues(entityWithoutTracking);
entry.State = EntityState.Modified;
// get List of all changed properties (in my case these are all existing properties, including those which shouldn't have changed)
var changedPropertiesList = entry.CurrentValues.PropertyNames.Where(x => entry.Property(x).IsModified).ToList();
foreach (var checkProperty in changedPropertiesList)
{
try
{
var p1 = entityWithoutTracking.GetType().GetProperty(checkProperty).GetValue(entityWithoutTracking);
var p2 = entityInit.GetType().GetProperty(checkProperty).GetValue(entityInit);
if ((p1 == null && p2 == null) || p1.Equals(p2))
{
entry.Property(checkProperty).CurrentValue = entry.Property(checkProperty).OriginalValue; // restore DB-Value
entry.Property(checkProperty).IsModified = false; // throws Exception for Primary Keys
}
} catch(Exception) { }
}
Context.SaveChanges(); // only surname will be updated
This is way I did it, assuming the new object has more columns to update that the one we want to keep.
if (theClass.ClassId == 0)
{
theClass.CreatedOn = DateTime.Now;
context.theClasses.Add(theClass);
}
else {
var currentClass = context.theClasses.Where(c => c.ClassId == theClass.ClassId)
.Select(c => new TheClasses {
CreatedOn = c.CreatedOn
// Add here others fields you want to keep as the original record
}).FirstOrDefault();
theClass.CreatedOn = currentClass.CreatedOn;
// The new class will replace the current, all fields
context.theClasses.Add(theClass);
context.Entry(theClass).State = EntityState.Modified;
}
context.SaveChanges();
In EF you can do like this
var result = db.Person.Where(x => x.ID == model.ID).FirstOrDefault();
if(result != null){
result.Name = newName;
result.DOB = newDOB;
db.Person.Update(result);
}
Or you can use
using (var db= new MyDbContext())
{
var result= db.Person.Where(x => x.ID == model.ID).FirstOrDefault();
result.Name= newName;
result.DOB = newDOB;
db.Update(result);
db.SaveChanges();
}
For more detail please EntityFramework Core - Update Only One Field
No Worry guys
Just write raw sql query
db.Database.ExecuteSqlCommand("Update Person set Name='"+_entity.Name+"' where Id = " + _entity.ID + "");

Entity Framework update record not working

I'm new to Entity Framework. I'm trying to update a record and save changes to the database.
public void SaveEdit(Gate gate)
{
try
{
using (dc = new GateEntities())
{
var query = (from tbsite in dc.tblSites
where tbsite.ID == gate.ID
select tbsite).FirstOrDefault();
query.CalledInAt = gate.CalledInAt;
query.CallerRequest = gate.CallerRequest;
query.ContactPersonOnSite = gate.ContactPersonOnSite;
query.Email = gate.Email;
query.EmailSitePerson = gate.EmailSitePerson;
dc.SaveChanges();
}
}
catch (Exception ex)
{
throw ex;
}
}
It gets no exceptions or error messages but it does not save the changes to the database. why it's not updating the record?
After You modify query object You should change it's state to Modified before calling context.SaveChanges(). Your context object should know about the entity that You modify. Assuming dc is Your context object:
query.CalledInAt = gate.CalledInAt;
//change other properties ..
dc.Entry(query).State = EntityState.Modified;
dc.SaveChanges();
That should work for You.
You have to use the entityframework to select your object, with that the result object will be track-able, so try this
using (var dc = new GateEntities())
{
var gate = dc.tblSites.Where(g => g.ID == date.ID).FirstOrDefault();
gate.CalledInAt = gate.CalledInAt;
gate.CallerRequest = gate.CallerRequest;
gate.ContactPersonOnSite = gate.ContactPersonOnSite;
gate.Email = gate.Email;
gate.EmailSitePerson = gate.EmailSitePerson;
dc.SaveChanges();
}
Also noticed this happening when the Entity doesn't have configured a primary Key.
On EF Core check your OnModelCreating and make sure entity.HasKey(e => e.TheKey); is present.
By the documentation you are not required to work with states: https://learn.microsoft.com/en-us/ef/core/get-started/?tabs=netcore-cli
var yourEntity = await context.FirstOrDefaultAsync(e => e.Id = 1);//You can also call FindAsync(id)
yourEntity.Name = "NewName";//This line will let EF Core knows the entity has been modify
await context.SaveChangesAsync();
Although you can check how to work with states so here: https://learn.microsoft.com/en-us/ef/ef6/saving/change-tracking/entity-state
And the code will look like:
var yourEntity = await context.FirstOrDefaultAsync(e => e.Id = 1);
yourEntity.Name = "NewName";
context.Entry(yourEntity).State = EntityState.Modified;
await context.SaveChangesAsync();
They are both the same, I honestly prefer manually setting the state (in big projects) sometimes EF Core loses tracking of some entities (this is a personal experience) so I double check updating the status as Added Or Modified.
Thanks

C# Entity Framework Update SaveChanges() is not updating DB entry

Been working on this C# Entity Framework update problem for a bit now. Maybe one of you can see what I'm missing.
The problem is within the userEntry portion of the code. I've traced through and made sure that userEntry is indeed populated with the information that I intend to update. When entities.SaveChanges(); is invoked, the record is not updated in the database.
int userId;
using (Entities entities = new Entities())
{
MSIFeedStoreData feedStoreEntry = entities.MSIFeedStoreDatas
.FirstOrDefault((d) => d.Alias == user.Alias);
if (Object.ReferenceEquals(feedStoreEntry, null))
{
throw new ArgumentException("The user's alias could not be located in the feed store. The user cannot be added at this time.");
}
int feedStorePersonnelIdValue;
if (Int32.TryParse(feedStoreEntry.PersonellID, out feedStorePersonnelIdValue))
{
user.EmployeeId = feedStorePersonnelIdValue;
}
else
{
throw new ApplicationException("DATABASE BUG CHECK: An entry was found in the feed store for this user but the personnel ID could not be parsed.");
}
MSIUser userEntry = entities.MSIUsers
.FirstOrDefault((u) => u.EmployeeID == feedStorePersonnelIdValue);
if (Object.ReferenceEquals(userEntry, null))
{
userEntry = Mapper.Map<MSIUser>(user);
userEntry = entities.MSIUsers.Add(userEntry);
}
else
{
Mapper.DynamicMap<User, MSIUser>(user, userEntry);
entities.MSIUsers.Attach(userEntry);
}
userId = userEntry.MSIUser_ID;
entities.SaveChanges();
}
return userId;
}
Remove the call to Attach, its already attached to the context.

Resources