Adding a new entity to collection in attached entity causes ConcurrencyException - entity-framework

I have simplified the code below to show the root of the problem. My real code is using GenericRepository and UnitOfWork pattern but I get the same exception with this simplified code too.
I am using Entity Framework 6, Code First
It uses the following POCO entities
public class Order
{
public int Id {get;set;}
public virtual List<OrderProducts> OrderProducts {get;set;}
...
}
public class Product
{
public int Id {get;set;}
...
}
public class OrderProduct
{
public int OrderId {get;set;}
public int ProductId {get;set;}
public int Quantity
public virtual Order Order { get; set; }
public virtual Product Product{ get; set; }
}
The user is able to create a new product and add it to the order products on the same screen.
//Pull an order from the database:
var existingOrder = db.Orders.FirstOrDefault(x => x.Id == inputModel.OrderId);
//Iterate the OrderProductInputModels (IMs) in the Inputmodel
foreach (var orderProductIM in inputModel.OrderProductIMs )
{
var orderProduct = existingOrder.OrderProducts.SingleOrDefault(o => o.Id == orderProductIM.Id);
//if its an existing order product (already in db)
if (orderProduct != null)
{
//just update its property values
}
//if it has been added
else
{
//we need to create a new product first
var newProduct= new Product() { <set some properties> };
orderProduct= new OrderProduct()
{
Product=newProduct,
Order=existingOrder
}
//Add the OrderProduct to the order
existingOrder.OrderProducts.Add(orderProduct);
}
db.SaveChanges();
On save changes, I get the following error.
[System.Data.Entity.Infrastructure.DbUpdateConcurrencyException] = {"Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."}
Why is this?
I expected entity framework to see that the existingOrders nested properties were newly added and unattached, update the order and create the new OrderProduct and Product.

Should it not be other way around in your if clause as you are checking for null ( then only it is a new order product else update. Issue is here:
//if its an existing order product (already in db)
if (orderProduct == null)
{
//just update its property values
}
//if it has been added
else
{

When you are looping around all the OrderProducts, you are constantly updating the database but the existingOrder object is not getting refreshed. Update that or add all the objects first and then update the database.

Finally solved it by creating a test project and reverse code first engineering the database. Noticed that OrderProduct entity was not generated. On inspecting the database, the primary key was not set. Once I set the primary key in the database, the issue was resolved. Thanks for all the suggestions.

Related

How to update the "LastModifiedDate" timestamp automatically on parent entity when adding/removing child entities

Is there a way to automatically enforce parent entity to be timestamped as having been modified, if any of its dependent child items are added/deleted/modified? The key word is automatically. I know this can be done by manipulating the DbEntry's EntityState or by manually setting the timestamp field in the parent, but I need this done on a number of parent-child entities in a system, so the desire is to have EF (or a related component) automatically do this somehow.
More Background and Examples
Let's say we have an Order and Order Items (1-many). When order items are added/removed from an order, the parent order itself needs to be updated to store the last modified timestamp.
public interface IModifiableEntity
{
DateTime LastModifiedOn { get; set; }
}
public class Order : IModifiableEntity
{
// some Order fields here...
// timestamp for tracking when the order was changed
public DateTime LastModifiedOn { get; set; }
// list of order items in a child collection
public ICollection<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public int OrderId { get; set; }
// other order item fields...
}
Somewhere in application logic:
public void AddOrderItem(OrderItem orderItem)
{
var order = _myDb.Orders.Single(o => o.Id == orderItem.OrderId);
order.OrderItems.Add(orderItem);
_myDb.SaveChanges();
}
I already have a pattern in place to detect modified entities and set timestamps automatically via EF's SaveChanges, like this:
public override int SaveChanges()
{
var timestamp = DateTime.Now;
foreach (var modifiableEntity in ChangeTracker.Entries<IModifiableEntity>())
{
if (modifiableEntity.State == EntityState.Modified)
{
modifiableEntity.Entity.UpdatedOn = timestamp;
}
}
return base.SaveChanges();
}
That works great if any direct fields on an IModifiableEntity are updated. That entity's state will then be marked as Modified by EF, and my custom SaveChanges() above will catch it and set the timestamp field correctly.
The problem is, if you only interact with a child collection property, the parent entity is not marked as modified by EF. I know I can manually force that via context.Entry(myEntity).State or just by manually setting the LastModifiedOn field when adding child items in application logic, but that wouldn't be done centrally, and is easy to forget.
I DO NOT want to do this:
public void AddOrderItem(OrderItem orderItem)
{
var order = _myDb.Orders.Single(o => o.Id == orderItem.OrderId);
order.OrderItems.Add(orderItem);
// this works but is very manual and EF infrastructure specific
_myDb.Entry(order).State = EntityState.Modified;
// this also works but is very manual and easy to forget
order.LastModifiedOn = DateTime.Now;
_myDb.SaveChanges();
}
Any way I can do this centrally and inform EF that a "root" entity of a parent-child relationship needs to be marked as having been updated?

EF Code First Edit has no effect on mapped entities

I am using Entity Framework Code First.
I have two entities:
public class Meal
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int MealID { get; set; }
public virtual List<MealCategory> MealCategories { get; set; }
}
public class MealCategory
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int MealCategoryID { get; set; }
public virtual List<Meal> Meals { get; set; }
}
They are mapped in the config:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Meal>()
.HasMany(c => c.MealCategories)
.WithMany(x => x.Meals)
.Map(a =>
{
a.ToTable("MealCategoryMapping");
a.MapLeftKey("MealID");
a.MapRightKey("MealCategoryID");
});
}
When adding a new meal with an existing Category it works perfectly, the ids are correctly inserted in the mapping table
Meal Meal = new Meal();
Meal.MealCategories = new List<MealCategory>();
var categoryToBeAdded = (from x in Context.MealCategoreis where x.MealCategoryID == 1 select x).FirstOrDefault();
Meal.MealCategories.Add(categoryToBeAdded);
MealDataContext.Add(Meal);
MealDataContext.SaveChanges();
However, if I try to edit the meal and add new categories nothing happen in the mapping table. The old entries stay the same and the new are not added.
Meal Meal = (from x in MealDataContext where x.MealID == 1 select x).FirstOrDefault();
Meal.MealCategories = new List<MealCategory>();
var categoryToBeAdded = (from x in Context.MealCategoreis where x.MealCategoryID == 4 select x).FirstOrDefault();
Meal.MealCategories.Add(categoryToBeAdded);
MealDataContext.Attach(entityToUpdate);
MealDataContext.Entry(entityToUpdate).State = EntityState.Modified;
MealDataContext.SaveChanges();
No Exception or somethink is thrown.
Can you please tell me how I can remove all old mapping entries and add new one when editing?
Try this. The call to Collection("MealCategories").Load(); should get EF to create a tracked collection.
Meal Meal = (from x in MealDataContext where x.MealID == 1 select x).FirstOrDefault();
MealDataContext.Attach(entityToUpdate);
MealDataContext.Entry(entityToUpdate).State = EntityState.Modified;
MealDataContext.Entry(entityToUpdate).Collection("MealCategories").Load();
var categoryToBeAdded = (
from x in Context.MealCategories
where x.MealCategoryID == 4
select x).FirstOrDefault();
Meal.MealCategories.Add(categoryToBeAdded);
MealDataContext.SaveChanges();
First let me say that you can use Find to fetch an object from the database (or the DbContext cache if it's already present in the local collection):
Meal meal = MealDataContext.Meals.Find(4);
// (lower case variable name is conventional)
You should realize that the meal object at this point has a MealCategories collection that's tracked by Entity Framework. You don't have to, no, should not replace it by a new one. If the collection is loaded at that point, replacing it cuts the categories from the owner meal and when you save changes the junction records are deleted!
In your case the collection is not loaded, so this does not happen.
It is not entirely clear to me why the category that you add to the MealCategories collection is not saved. Maybe because it seems to come from a different context (or is that a typo?). But that should throw an exception. Anyway, make sure that
You do not replace the MealCategories collection, and
Add a category to it that is fetched by the same context instance as the meal
and you should be OK.

Many to Many Relationships not saving

I have two entities with a fairly standard Many to Many relationship that I created in EF 5 Code First. These are Service and ServiceItem. The Service entity contains a collection of ServiceItems and the ServiceItem contains a collection of Services. I can create, change and save data to either of the entities basic properties with no problems. When I try to add a ServiceItem to a Service or a Service to a ServiceItem it seems to work, but nothing is saved. I have verified that all the proper database tables are created, including a ServiceItemService table with the cross keys. The database ServiceItemService table doesn't get any entry when I add the items. There is no error and everything else seems to work perfectly.
I am a bit stumped and could use some help. Below are the classes.
The Service class;
public class Service
{
//Default constructor
public Service()
{
//Defaults
IsActive = true;
ServicePeriod = ServicePeriodType.Monthly;
ServicePeriodDays = 0;
ServiceItems = new Collection<ServiceItem>();
}
public int ServiceID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public ICollection<ServiceItem> ServiceItems { get; set; }
public string TermsOfService { get; set; }
public ServicePeriodType ServicePeriod { get; set; }
public int ServicePeriodDays { get; set; }
public bool IsActive { get; set; }
}
The ServiceItem class;
public class ServiceItem
{
public ServiceItem()
{
IsActive = true;
}
public int ServiceItemID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public ICollection<Service> Services { get; set; }
public string UserRole { get; set; }
public bool IsActive { get; set; }
}
This is the Fluent mapping I did while trying to debug this issue. The same problem happened before and after adding this mapping.
public DbSet<Service> Services { get; set; }
public DbSet<ServiceItem> ServiceItems { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Service>()
.HasMany(p => p.ServiceItems)
.WithMany(r => r.Services)
.Map(mc =>
{
mc.MapLeftKey("ServiceItemID");
mc.MapRightKey("ServiceID");
mc.ToTable("ServiceItemService");
});
}
Here is the code I use to save the Service item that includes 2-3 ServiceItems in the Service.ServiceItems collection. I have carefully verified that the ServiceItems were in the proper collection.
db.Entry(dbService).State = EntityState.Modified;
db.SaveChanges();
The dbService object doesn't seem to get affected in any way. The ServiceItems are still in the proper collection, but no update are made to the ServiceItemService database table. Any advice would be very welcome.
-Thanks
It is expected that nothing happens.
What you want to change or add is a relationship between the entities Service and ServiceItem. But you cannot manipulate relationships by setting the state of an entity to Modified. This only updates scalar and complex properties but no navigation properties (= relationships). (For example setting the state of a Service entity to Modified will mark Service.Title and Service.Description, etc. as modified and ensure that those properties are saved to the database. But it doesn't care about the content of Service.ServiceItems.)
The only exception where you can change a relationship by setting the state to Modified are Foreign Key Associations. These are associations that have foreign key properties exposed in your model entity and they can only occur for one-to-many or one-to-one associations. Many-to-many relationships are always Independent Associations which means they can never have a foreign key property in an entity. (Because the FKs are in the join table, but the join table is not an entity and "hidden" from your model classes.)
There is a way to directly manipulate relationships for a many-to-many association but it requires to go down to the ObjectContext and its RelationshipManager which is - in my opinion - pretty advanced and tricky.
The usual and straight-forward way to add and remove relationship entries to/from a many-to-many association is by just adding items to and removing items from the collections while the entities are attached to the context. EF's change tracking mechanism will recognize the changes you have done and generate the appropriate INSERT, UPDATE and DELETE statements when you call SaveChanges.
The exact procedure depends on if you also want to save Service and/or ServiceItem as new entities or if you only want to add relationships between existing entities. Here are a few examples:
service should be INSERTed, all serviceItems should be INSERTed and the relationships between the entities should be INSERTed into the join table as well:
using (var context = new MyContext())
{
var service = new Service();
var serviceItem1 = new ServiceItem();
var serviceItem2 = new ServiceItem();
service.ServiceItems.Add(serviceItem1);
service.ServiceItems.Add(serviceItem2);
context.Services.Add(service);
context.SaveChanges();
}
Adding the "root" service of the object graph is enough because EF will recognize that all other entities in the graph are not attached to the context and assume that they have to be INSERTed into the database.
service already exists and should NOT be INSERTed, all serviceItems should be INSERTed and the relationships between the entities should be INSERTed into the join table as well:
using (var context = new MyContext())
{
var service = new Service { ServiceID = 15 };
context.Services.Attach(service);
var serviceItem1 = new ServiceItem();
var serviceItem2 = new ServiceItem();
service.ServiceItems.Add(serviceItem1);
service.ServiceItems.Add(serviceItem2);
context.SaveChanges();
}
EF recognizes here (when SaveChanges is called) that service is attached but the other entities are not. No INSERT for service happens but the serviceItem1/2 will be INSERTed together with the relationship entries.
service already exists and should NOT be INSERTed, all serviceItems already exist and should NOT be INSERTed, but the relationships between the entities should be INSERTed into the join table:
using (var context = new MyContext())
{
var service = new Service { ServiceID = 15 };
context.Services.Attach(service);
var serviceItem1 = new ServiceItem { ServiceItemID = 23 };
context.ServiceItems.Attach(serviceItem1);
var serviceItem2 = new ServiceItem { ServiceItemID = 37 };
context.ServiceItems.Attach(serviceItem2);
service.ServiceItems.Add(serviceItem1);
service.ServiceItems.Add(serviceItem2);
context.SaveChanges();
}
For completeness: How to remove relationships between existing entities?
using (var context = new MyContext())
{
var service = context.Services
.Include(s => s.ServiceItems) // load the existing Items
.Single(s => s.ServiceID == 15);
var serviceItem1 = service.ServiceItems
.Single(s => s.ServiceItemID == 23); // query in memory, no DB query
var serviceItem2 = service.ServiceItems
.Single(s => s.ServiceItemID == 37); // query in memory, no DB query
service.ServiceItems.Remove(serviceItem1);
service.ServiceItems.Remove(serviceItem2);
context.SaveChanges();
}
The two relationship rows in the join table that link service 15 with serviceItem 23 and 37 will be deleted.
Alternativly instead of calling Attach you can load the existing entities from the database. It will work as well:
var service = context.Services.Single(s => s.ServiceID == 15);
And the same for existing ServiceItems.

Entity framework, referential integrity constraint violation occurred error on updating entity from disconnected area

I have below model
public class Order
{
[Key]
public virtual string OrderNo {get;set;}
public virtual IList<OrderItem> Items {get;set;}
}
public class OrderItem
{
[Key]
public virtual string ItemNo {get; set;}
public virtual string ParentItemNo {get;set;}
public virtual string OrderNo {get;set;}
public virtual OrderItem ParentItem {get;set;}
public virtual IList<OrderItem> ChildItems {get;set;}
public virtual IList<ItemProperty> ItemProperties {get;set;}
public virtual Order Order {get;set;}
}
public class ItemProperty
{
[Key]
public virtual string PropertyNo {get; set;}
public virtual string ParentPropertyNo {get;set;}
public virtual string OrderItemNo {get;set;}
public virtual ItemProperty ParentProperty {get;set;}
public virtual IList<ItemProperty> ChildProperties {get;set;}
public virtual OrderItem OrderItem {get;set;}
}
I'm running on disconnected area (with service our disconnected from the Entity Framework context)
I create a order and save to database
client:
service.CreateOrder(new Order() { OrderN="fksjdf1" });
server:
using(EfDbContext context = new EfDbContext())
{
context.Orders.Add(order);
context.SaveChanges();
}
I need to add one or more OrderItems to previously added order
client:
var order = service.GetOrder("fksjdf1");
var item1 = new OrderItem();
item1.ItemNo="i1";
item1.Order=order;
item1.OrderNo=order.OrderNo;
item1.ItemProperties.Add(new ItemProperty()
PropertyNo="p1",
OrderItem = item1
})
order.Items.Add(item1);
var item2 = new OrderItem();
item2.ItemNo="i2";
item2.Order=order;
item2.OrderNo=order.OrderNo;
item2.ItemProperties.Add(new ItemProperty()
PropertyNo="p2",
OrderItem = item2
});
item2.ItemProperties.Add(new ItemProperty()
PropertyNo="p3",
OrderItem = item2
})
order.Items.Add(item2);
service.UpdateOrder(order);
server:
using(EfDbContext context = new EfDbContext())
{
DbEntityEntry dbEntityEntry = context.Entry(order);
if (dbEntityEntry.State == EntityState.Detached)
{
// ERROR
context.Set<Order>().Attach(order);
}
dbEntityEntry.State = EntityState.Modified;
context.SaveChanges();
}
Error: 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.
Why I see this error? Why I not update this entity?
How can i use entity framework from disconnected area?
EDIT 1:
public Order GetOrder(string orderNo)
{
using (EfDbContext context = new EfDbContext())
{
context.Configuration.ProxyCreationEnabled = false;
var order = context.Orders
.Include(o => o.OrderItems
.Select(z => z.ItemProperties
.Select(y => y.ChildProperties)))
.Where(o => o.OrderNo == orderNo)
.FirstOrDefault();
}
}
You probably have this error because you don't set OrderItemNo in ItemProperty, like so:
var item1 = new OrderItem();
item1.ItemNo="i1";
item1.Order=order;
item1.OrderNo=order.OrderNo;
item1.ItemProperties.Add(new ItemProperty {
PropertyNo="p1",
OrderItem = item1,
OrderItemNo = item1.ItemNo // = "i1"
});
order.Items.Add(item1);
// and the same for item2
When the order gets attached to the context, related order items and item properties get attached as well. The navigation property OrderItem of ItemProperty refers to an entity with a key you supplied ("i1"), but the foreign key OrderItemNo doesn't have this key value. That's the inconsistency the exception is complaining about.
Edit
Unfortunately it is not that easy to update a detached object graph in the database. Setting the state of an entity to Modified only marks this entity as Modified and no related entity.
Your GetOrder method makes things complicated because you are eagerly loading already existing items and more related stuff to the client, then add new OrderItems on client side and send this new modified object graph back to the server. On server side you now have the problem that you must handle the already existing items differently (don't insert them into the database) than the new items (insert them into the database). To do this, you need to detect which items are old and which are new. If you don't transport some flag in the entities themselves that would indicate if the entity is new or not, you must query the database again and compare the object graph loaded from the DB with the detached object graph sent from the client. A general sketch how it can be done is here: https://stackoverflow.com/a/5540956/270591 (That's only for a parent with child collection. If grandchild collections are involved - like in your model - it is getting more complex.)
In my opinion you can simplify the whole procedure if you would not only have this global service.UpdateOrder(order) method that tries to handle all possible changes in the object graph but also some specialized methods that leverage the knowledge you have about the change of the graph - in this case a specialized method for adding new OrderItems to an existing order. It could look like this:
var order = service.GetOrder("fksjdf1");
var newOrderItems = new List<OrderItem>();
var item1 = new OrderItem();
item1.ItemNo="i1";
item1.OrderNo=order.OrderNo; // leave the Order property null
item1.ItemProperties.Add(new ItemProperty { PropertyNo="p1" });
newOrderItems.Add(item1);
// the same for item2
newOrderItems.Add(item2);
service.AddNewOrderItems(newOrderItems);
And the service method would be:
public void AddNewOrderItems(List<OrderItem> newOrderItems)
{
using(EfDbContext context = new EfDbContext())
{
foreach (var newOrderItem in newOrderItems)
context.Set<OrderItem>().Add(newOrderItem);
context.SaveChanges();
}
}

Entity Framework object real update - no duplication

I'm trying to update an object in my database using entity framework 4.1 with code-first and SQL Server 2008. My object's class has a ID field of int type. The problem is that when I update my object and invoke SaveChanges on my DbContext so the database creates a copy of the datarow rather then update the one I already have. I don't want this, I just would like a clean update like in the old-fashioned SQL command UPDATE. How can I fix this?
Nation nation = (nationDB.Nations.Where(m => m.name == "France")).SingleOrDefault();
if (nation != null)
{
Nation modNation = nationDB.Nations.Find(nation.ID);
modNation.Level++;
}
nationDB.SaveChanges();
public class Nation
{
public int ID { get; set; }
public int name { get; set; }
public int level { get; set; }
}
To update it you must first read it from the database, change it, then save it.
If you create a new entity in code, even with an existing key, EF will treat it as a new entity.
I think that the ObjectStateManager has registered your object as being in the Added state. Try to set its status to Modified (by calling GetObjectStateEntry(Object) and checking/modifying the State property).