Navigations properties are not updating - c#

For some reason my edit action are not updating my navigations properties
Check de codes
public ActionResult Edit(ClienteViewModel clienteViewModel)
{
if (ModelState.IsValid)
{
var cliente = Mapper.Map<Clientes>(clienteViewModel);
_context.Entry(cliente).State = EntityState.Modified;
_unitOfWork.Commit();
return RedirectToAction("Index");
}
return View(clienteViewModel);
}
Any ideas?
EDIT
I added a hidden field with endereco.id on edit view and now Endereco.Id is going to controller, but the error still the same

If you look at the primary key Id of your navigational property Endereco you will see that its value is Guid.Empty 00000000-0000-0000-0000-000000000000.
So updating that entity will generate a SQL query with this WHERE clause below:
WHERE Id = '00000000-0000-0000-0000-000000000000'
Of course a line with that empty guid doesn't exist into your table.
Your original code do a mapping with AutoMapper so make sure that all properties are mapped correctly when calling the line below:
var cliente = Mapper.Map<Clientes>(clienteViewModel);
Updates: Based on your comment you also need to be sure that the navigational property state is changed to EntityState.Modified (because changing the root entity's state doesn't impact the navigational properties) like below:
_context.Entry(cliente.Endereco).State = EntityState.Modified;

Related

EFCore update: The instance of entity type '' cannot be tracked because another instance with the key value '{ID: 0}' is already being tracked

This relates to my previous question from a few days ago: EF Core duplicate keys: The instance of entity type '' cannot be tracked because another instance with the key value '' is already being tracked
I am using context.ChangeTracker.TrackGraph to give me more control when inserting data. The insert process works great as show by my own answer on the question above. This problem results when I update items and attempt to save them.
My primary model is as follows (type names have been changed for simplicity):
public class Model
{
public List<Data1> List1 { get; set; }
...
}
Data1 looks like this:
public class Data1
{
public string Name{ get; set; }
...
}
To update to the database, I use this:
using var context = Factory.CreateDbContext();
context.ChangeTracker.TrackGraph(model, node =>
{
if (!(node.Entry.Entity.GetType().IsSubclassOf(typeof(EFChangeIgnore)) && node.Entry.IsKeySet)) //this is a workaround but it solves the pk problems I have been having
{
node.Entry.State = EntityState.Modified;
}
else if (!node.Entry.IsKeySet)
{
node.Entry.State = EntityState.Added;
}
});
return await context.SaveChangesAsync();
This works great as it prevents my existing entities (inheriting fromEFChangeIgnore) from being inserted and otherwise handles updates and inserts of other entities well. However, if I have more than one Data item in List1, I get the error:
The instance of entity type 'Data1' cannot be tracked because another instance with the key value '{ID: 0}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
I understand this error but I am confused as to why it is appearing in this scenario. Yes, both entities have an ID of zero, but only because they have yet to be inserted to the database. What am I doing wrong here?
It turns out my bug was due to a misplaced parenthesis. Go figure.
In my SaveChanges I had accidently negated the entire conditional at the top, such that entities that should have been Modified became Added, and vice versa. This led to multiple entities with an ID of 0 that I intended to add being marked as Modified. EF then complained that two existing entities had the same ID.
That is, this line:
if (!(node.Entry.Entity.GetType().IsSubclassOf(typeof(EFChangeIgnore)) && node.Entry.IsKeySet))
now becomes (conditionals swapped for clarity):
if (node.Entry.IsKeySet && (!node.Entry.Entity.GetType().IsSubclassOf(typeof(EFChangeIgnore))))
And my entire SaveChanges method now is:
using var context = Factory.CreateDbContext();
context.ChangeTracker.TrackGraph(model, node => //all entities inheriting from EFChangeIgnore will not be touched by the change tracker. Other entities with keys will be modified, and items without keys will be added
{
if (node.Entry.IsKeySet && (!node.Entry.Entity.GetType().IsSubclassOf(typeof(EFChangeIgnore)))) //this is a workaround but it solves key duplication problems
{
node.Entry.State = EntityState.Modified;
}
else if (!node.Entry.IsKeySet)
{
node.Entry.State = EntityState.Added;
}
return await context.SaveChangesAsync();

Could EntityState.Modified cause Insert operation to be performed

I am reading the following tutorial about entity framework 6 Link. And inside the section named ”Adding an Edit Page for Instructors”, the author wrote the following code inside the Post edit action method:-
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public ActionResult EditPost(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var instructorToUpdate = db.Instructors
.Include(i => i.OfficeAssignment)
.Where(i => i.ID == id)
.Single();
if (TryUpdateModel(instructorToUpdate, "",
new string[] { "LastName", "FirstMidName", "HireDate", "OfficeAssignment" }))
{
try
{
if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
{
instructorToUpdate.OfficeAssignment = null;
}
db.Entry(instructorToUpdate).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
}
return View(instructorToUpdate);
}
This code will cover these three conditions:-
If the user clears the office assignment and it originally had a value, you must remove and delete the OfficeAssignment entity.
If the user enters an office assignment value and it originally was empty, you must create a new OfficeAssignment entity.
If the user changes the value of an office assignment, you must change the value in an existing OfficeAssignment entity.
So does this means that
db.Entry(instructorToUpdate).State = EntityState.Modified;
will cause an insert statement to be performed for the OfficeAssignment record incase the Instructor did not have a prevouse OfficeAssignment object ? and what is the rule that govern this ?
here is the complete model diagram:-
DbContext.Entry method is used to do an explicit loading, that means that It gives you access to all the information that the DbContext has about an entity. This goes beyond the values that are stored in the properties of the actual entity and includes things such as the state of the entity and the original values for each property when it was retrieved from the database.
When you call the TryUpdateModel method, it will update the properties (that you pass their names as a parameter) with values from the model binder. One of these properties is OfficeAssignment, wich is updated too. If in your view you don't enter a Location, then you don't have reason to create a new OfficeAssigment (that's way you need to do instructorToUpdate.OfficeAssignment = null; because even when you don't enter a new Location, you will have a instance of OfficeAssignment). If you add a new Location, you are going to create a new OfficeAssignment, and if you modified the Location, then you are going to modified its value.
When you do this:
db.Entry(instructorToUpdate).State = EntityState.Modified;
You are going to set a flag on the entity indicating it has been changed. When the SaveChanges method is called, the Modified flag causes the Entity Framework to create SQL statements to update the database row. All columns of the database row will be updated, including those that the user didn't change, and concurrency conflicts are ignored. To understand better what happend, you can look the Instructor instance like a tree. Code First recognizes that you have a navegation property, so it need to be updated or insterted(depending on the case). If the OfficeAssignment have an Id different of the default(int) (I'm pressuming that is an interger), then it will be updated, and in other case, it will be inserted.
There are basically two ways an entity can be persisted through EF.
A. Add it directly to the Dbset with the additional relationships you want it to have.
Entity e = new Entity();
e.ForeignEntityId = 123;
context.Entities.Add(e);
context.SaveChanges();
B. Attach it to an existing entity and if that entity is/was untracked, mark that entity as `Modified.
Entity e = new Entity();
ForeignEntity fe = context.Find(...);
//Only needed if 'fe' was untracked
//context.Entry(fe).State = EntityState.Modified;
fe.Entity = e;
context.SaveChanges();
The way presented in your question is the second way. It's all about getting the "new" object to be present in the object graph that represents all tracked EF entities from your DB.
Yes,can load the DbContext.Entry method and can be used to do an explicit loading as mentioned above
I will suggest rather do Delete and Insert until you do not have real-time needs of modification.
{
//Remove existing data
modelname existingobj = dbobj.tablename.Find(id);
dbobj.tablename.Remove(existingobj);
dbobj.SaveChanges();
//Add data
dbobj.Entry(existingobj).State = EntityState.Added;
dbobj.SaveChanges();
}

Entity Framework: A referential integrity constraint violation on many to many relationship

Hey I have an application with a bunch of inproc caching and entity framework. When I want to write an update to an entity I reattach the cached copy. I track all things I've attached in the life cycle of the context so I don't try to attach them twice.
I have an error occurring on attach (very rarely in most cases this works fine and is really fast) which says the following:
A referential integrity constraint violation occurred: The property
values that define the referential constraints are not consistent
between principal and dependent objects in the relationship.
I've taken a really careful look at the entity which looks normal. I think this issue is due to the attachment/detachment of a foreign key when fixups runs.
Is there a good way to get any more info on this error or can it occur for reasons other than that the entity was in a state which EF wasnt expecting?
EDIT:
DB Diagram (note i'm using codefirst I just used the EDMX tool to make the diagram, I've also chopped a bunch of regular properties off the model for simplicity)
The error could occur for the one-to-many relationship between Person and Location you apparently have in your model in addition to the many-to-many relationship. For example the following code would throw the exception:
using (var context = new MyContext())
{
var person = new Person
{
CurrentLocationId = 1,
CurrentLocation = new Location { Id = 2 }
};
context.People.Attach(person); // Exception
}
"The property values that define the referential constraints" are the foreign key property value CurrentLocationId and the primary key value CurrentLocation.Id. If those values are different the exception is thrown. (Having CurrentLocation as null though is allowed.)
In my opinion this exception can only be thrown for foreign key associations because only for this type of association you have properties that define referential constraints at all in your model. It cannot be thrown for independent associations. Since every many-to-many relationship is an independent association (no foreign key property in the model) my guess is that the error is not related to your many-to-many relationship, but to the one-to-many.
I came across a very similar exception:
"A referential integrity constraint violation occurred:
The property value(s) of 'ObjectA.PropertyX' on one end of a relationship
do not match the property value(s) of 'ObjectB.PropertyY' on the other end."
The reason was this:
The client side of the web API sent a PUT request with the entire object including the navigation property (in this example ObjectA (more correctly ObjectB.ObjectA) was a navigation property and was fully supplied by the client). This occurs because the client receives the entire object from the server and bounces it as-is back to the server with minor changes.
On the other hand, the ObjectB.PropertyY had just been changed (this was the reason for the PUT request in the first place).
Since ObjectB.PropertyY was a reference to the same object ObjectA (a foreign key), EF tried to reconcile this and failed with the above exception.
The solution was simple:
ObjectB.ObjectA = null;
before the SaveChanges() solved this completely.
I hope this helps someone.
I have just been experiencing the same issue and the resolution to mine was that I had added mappings to the association and then setup the referential contstraints.
Inorder to resolve the issue I had to open the mappings window for the association and there was a link to delete the mappings. Once done the Mapping Details window then said Mappings are not allowed.. It appears that adding the referential constraint leaves any mappings in place.
Thought it may be worth posting in case anyone else is looking for solutions to this error message in the future.
#LukeMcGregor hi,
I think I can offer a different perspective as someone who has the same problem.
After I have performed all the necessary checks, I can say that I prefer to get this error.
Because in my scenario: I wanted to include an object that caused a mismatch error. It's the location object in your scenario. If I add an object with an ID, I get this error because the ID in the previous object (the one that is not updated) does not match the updated ID.
But it's not a big problem. As a solution; If it is still on the UI side, the object may still be included if it still exists.
You will either empty the object when you receive the update request from the user. (= Null) Or you will update the object with the ID updated by the user before the service-side update (attach, modified ... whatever) and update it in this way.
That's it. It can remain as it is in the database and diagrams.
To add to #Slauma's answer, it isn't just when adding objects to your context. For your example, if you edit the CurrentLocationId in Person, you also need to edit the CurrentLocation object embedded in the Person object. EF will automatically populate the CurrentLocation object because CurrentLocationId has a foreign key in the CurrentLocation's table. When you edit the CurrentLocationId without updating the CurrentLocation object as well, they become out of sync. This is what causes the exception in this case.
So let's say you needed to update the Person object's CurrentLocationId. We'll assume you pre-fetched the Person data and the Location data.
public class DbData
{
List<Person> PersonList;
List<Location> LocationList;
public DbData()
{
using (var context = new MyContext())
{
PersonList = context.Persons.ToList();
LocationList = context.Locations.ToList();
}
}
public void UpdatePersonLocation(Person person, int newLocationId)
{
using (var context = new MyContext())
{
var location = LocationList.Where(l=>l.id==newLocationId).Single();
//you need to update both the id and the location for this to not throw the exception
person.CurrentLocationId == newLocationId;
person.CurrentLocation == location;
context.Entry(person).State = System.Data.Entity.EntityState.Modified;
context.SaveChanges();
}
}
//or if you're giving it the location object...
public void UpdatePersonLocation(Person person, Location location)
{
using (var context = new MyContext())
{
//you need to update both the id and the location for this to not throw the exception
person.CurrentLocationId == location.id;
person.CurrentLocation == location;
context.Entry(person).State = System.Data.Entity.EntityState.Modified;
context.SaveChanges();
}
}
}
I just had this problem and came up with a pretty quick solution. My issue was with a many-many table.
Public class Pictures_Tag
{
[Key]
[Column(Order = 0)]
[ForeignKey("Picture")]
public Int16 Picture_ID { get; set; }
[Key]
[Column(Order = 1)]
[ForeignKey("ImageTag")]
public Int16 ImageTag_ID { get; set; }
public virtual Picture Picture { get; set; }
public virtual ImageTag ImageTag { get; set; }
}
I added the line where I assigned Picture = db.Pictures... and then it worked fine (not exactly sure why)
[HttpPost]
public ActionResult Edit2(WebSiteEF2017C.Models.Pictures_Tag p)
{
using (var db = new thisModel(Session["org"].ToString())
{
p.Picture = db.Pictures.Where(z => z.ID == p.Picture_ID).FirstOrDefault();
db.Pictures_Tags.Attach(p);
db.Entry(p).State = EntityState.Modified;
db.SaveChanges();
return View(db.Pictures_Tags.Include(x => x.Picture)
.Where(n => n.Picture_ID == p.Picture_ID & n.ImageTag_ID == p.ImageTag_ID).FirstOrDefault());
}
}
try to set any sub class object to null
MainObject.SubObject1 = null;
Person.Address=null; // Address Object to be null not id

Entity Framework DbContext SaveChanges() OriginalValue Incorrect

I am trying to implement an AuditLog using EF 4.1, by overriding the SaveChanges() method as discussed in the following places:
http://jmdority.wordpress.com/2011/07/20/using-entity-framework-4-1-dbcontext-change-tracking-for-audit-logging/
Entity Framework 4.1 DbContext Override SaveChanges to Audit Property Change
I am having problems with the "modified" entries though. Whenever I attempt to get at the OriginalValue of the property in question, it always has the same value as it does in the CurrentValue field.
I first use this code, and it successfully identifies the Entries that are modified:
public int SaveChanges(string userID)
{
// Have tried both with and without the following line, and received same results:
// ChangeTracker.DetectChanges();
foreach (
var ent in this.ChangeTracker
.Entries()
.Where( p => p.State == System.Data.EntityState.Added ||
p.State == System.Data.EntityState.Deleted ||
p.State == System.Data.EntityState.Modified ))
{
// For each change record, get the audit record entries and add them
foreach (AuditLog log in GetAuditRecordsForChange(ent, userID))
{
this.AuditLog.Add(log);
}
}
return base.SaveChanges();
}
The problem is in this (abbreviated code):
private List<AuditLog> GetAuditRecordsForChange(DbEntityEntry dbEntry, string userID)
{
if (dbEntry.State == System.Data.EntityState.Modified)
{
foreach (string propertyName in dbEntry.OriginalValues.PropertyNames)
{
if (!object.Equals(dbEntry.OriginalValues.GetValue<object>(propertyName),
dbEntry.CurrentValues.GetValue<object>(propertyName)))
{
// It never makes it into this if block, even when
// the property has been updated.
}
// If I updated the property "Name" which was originally "OldName" to the value "NewName" and then break here and inspect the values by calling:
// ?dbEntry.OriginalValues.GetValue<object>("Name").ToString()
// the result will be "NewName" and not "OldName" as expected
}
}
}
The strange thing is that the call to dbEntry.Property(propertyName).IsModified(); will
return true in this case. It is just that the OriginalValue doesn't have the expected value inside. Would anyone be willing to help point me in the right direction? I cannot seem to get this to work correctly.
When EF retrieves an entity from the database it takes a snapshot of the original values for all properties of that entity. Later, as changes are made to the values of these properties the original values will remain the same while the current values change.
However, for this to happen EF needs to be tracking the entity throughout the process. In a web or other n-tier application, typically the values are sent to the client and the context used to query the entity is disposed. This means that the entity is now no longer being tracked by EF. This is fine and good practice.
Once the application posts back the entity is reconstructed using values from the client and then re-attached to the context and set into a Modified state. However, by default the only values that come back from the client are the current values. The original values are lost. Usually this doesn't matter unless you are doing optimistic concurrency or want to be very careful about only updating values that have really changed. In these cases the original values should also be sent to the client (usually as hidden fields in a web app) and then re-applied as the original values as a part of the attach process. This was not happening in the example above and this is why the original values were not showing as expected.
If you change
dbEntry.OriginalValues.GetValue<object>(propertyName);
to
dbEntry.GetDatabaseValues().GetValue<object>(propertyName);
then that works.
I got this error when i override SaveChanges in context As follows
public override int SaveChanges()
{
var changeInfo = ChangeTracker.Entries()
.Select(t => new {
Original = t.OriginalValues.PropertyNames.ToDictionary(pn => pn, pn => t.OriginalValues[pn]),
Current = t.CurrentValues.PropertyNames.ToDictionary(pn => pn, pn => t.CurrentValues[pn]),
}).ToList();
return base.SaveChanges();
}
and when I cleared it fixed!
ChangeTracker.Entries().ToList() in SaveChanges is wrong...
The problem is not in the code you show here. The issue is that how you track entities.
If you just create an entity object and calls Update on it EF framework just overwrite the existing value in db ( provided you supplied correct ID ). That is done for efficiency. So if you do:
var company = new Company{Id = mySuppliedId, Name = newname};
Context.Companies.Update(company);
Context.SaveChanges();
EF will go directly to DB in one shot and update all properties on the entity, without bringing anything back first. So it has no way of knowing the original values.
If you change the code in your logic to something like:
var company = Context.Companies.Where(c=>c.Id == mySuppliedId).FirstOrDefault();
company.Name = newName;
Context.SaveChanges()
Then your ChangeTracker code you showed above all of sudden starts working, as EF brought the data from DB first. It is however less efficient as you make and extra query.
I need the old/original value in post method. Finally this worked for me.
//Get Orignal value before save changes
Item entityBeforeChange = db.Items.Single(x => x.Id == item.Id);
db.Entry(entityBeforeChange).State = EntityState.Detached; // breaks up the connection to the Context
var locId = entityBeforeChange.LocationId;//Orignal value
//Saving the current Value
if (ModelState.IsValid)
{
db.Entry(item).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
You can get data that you haven't committed yet.
var Current = _dbContext.Entry(entity).GetDatabaseValues().ToObject();

EF4 ObjectContext.EntitySet.AddObject() cascading inserts

How can I add an entity to the database through EF4 without attaching all of its referenced entities first?
var entity = new MyEntity() { FK_ID = 4 }; // associated entity key
using (var db = new MyEntities())
{
db.MyEntity.AddObject(entity);
db.SaveChanges();
db.AcceptAllChanges();
}
This code keeps trying to also insert a new default-valued FK_Entity.
I see some suggestions online that I need to attach FK_Entity first and then set the MyEntity.FK property to the attached FK_Entity, but that seems awful. 1) I assume attaching requires loading the FK_Entity, which I don't need just to insert the entity - I already gave it the right key and SQL will enforce referential integrity if I make a mistake. 2) As I have more references, I have to attach each one??
I can't find any options or overloads for supressing cascaded inserts. I must be thinking about this wrong?
Thanks.
What you can try to do is create 'dummy' FK entities with only the ID property set for each one. And make sure they have EntityStatus.Unchanged in the ObjectStateManager, so that EF doesn't try to 'update' and rewrite all the other properties of the FK entities.
I don't have an opportunity to test this, but something along the lines of:
var fkEntity = new FK_Entity { ID = 4 };
var entity = new MyEntity { FK_Entity = fkEntity };
using (var db = new MyEntities())
{
db.AddToEntities(entity);
ObjectStateEntry fkEntry = db.ObjectStateManager.GetObjectStateEntry(fkEntity);
// You can check the state here while debugging, but it's probably `Added`
fkEntity.ChangeState(EntityState.Unchanged);
db.SaveChanges();
}
I found that the MVC Binder (or something) was creating an empty FK_Entity that I don't want it to. So I Exclude = "FK_Entity" in the Create() controller handler properties, and continue to just set the fk_id property.
I know I could use ViewModels for this, translate the objects in and out of EF types, but I want to avoid that overhead for now.

Categories