I have an entity with a collection of related entities.
I create a new entity with the same key, and new related entities (collection).
I've added the ned related entities to the collection of the main entity and now I want to save everything to the DB.
I've found the old entity and it's collection and detached everything.
than i've attached the new entity and in foreach loop - all the related entities in the collection.
when I commit - context.SaveChanges() I get
OptimisticCuncurrencyException
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.
Any Ideas ?
code:
var old = (from c in context.Category.Include("Products").Where(x => (x.CatID== CatID) select c).FirstOrDefault();
if (old != null)
{
context.Detach(old);
foreach (Product stwp in old.Products)
{
context.Product.Detach(stwp);
}
}
context.Category.Attach(st);
context.ObjectStateManager.ChangeObjectState(st, System.Data.EntityState.Modified);
foreach (Product stwp in st.Products)
{
context.Product.Attach(stwp);
context.ObjectStateManager.ChangeObjectState(stwp, System.Data.EntityState.Modified);
}
context.SaveChanges();
The solution was to first delete all object and then insert. not my preferred one, but no one had offered a better one.
Related
I am using Automapper to copy the data from my data objects to my view model objects.
cfg.CreateMap<Customer, CustomerVM>().ReverseMap();
cfg.CreateMap<Order, OrderVM>().ReverseMap();
There is a navigation property between the Order and the Customer. This works fine for displaying the complex objects on my view.
#Html.EditorFor(a => a.Customer.AccountName)
#Html.EditorFor(a => a.Notes)
And the objects seem to map back properly in my post action.
[HttpPost]
public ActionResult SaveOrder(OrderVM order)
{
if (ModelState.IsValid)
{
var order2 = AutomapperConfig.MapperConfiguration.CreateMapper().Map<OrderVM, Order>(order);
order2.LastEditDate = DateTime.Now;
order2.LastEditBy = HttpContext.User.Identity.Name;
order2.CreateDate = order.CreateDate;
using (var db = new ProofingContext())
{
db.Entry(order2).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("index");
}
}
return View();
}
When I step thru the code in the post action the view model shows the updated data in all the related objects. But when I save the changes I only get the changes to the order saved to my DB. What do I need to do to save the changes to the related objects too?
From this link(Attaching an existing but modified entity to the context section):
Note that if the entity being attached has references to other
entities that are not yet tracked, then these new entities will
attached to the context in the Unchanged state—they will not
automatically be made Modified. If you have multiple entities that
need to be marked Modified you should set the state for each of these
entities individually.
As an alternative, there is a library (GraphDiff) that allows you update the entire graph of detached entities. For more info take a look this post.In your case it would be:
context.UpdateGraph(order2, map => map.OwnedEntity(x => x.Customer)
.OwnedCollection(p => p.Notes));
I have this code in a Windows Service targeted to .Net 4.5 that uses a database-first Entity Framework layer:
var existingState = DataProcessor.GetProcessState(workerId);
existingState.ProcessStatusTypeId = (int)status;
existingState.PercentProgress = percentProgress;
existingState.ProgressLog = log;
DataProcessor.UpdateProcessState(existingState);
And this code in a data processing class in the same solution:
public ProcessState GetProcessState(int id)
{
using (var context = new TaskManagerEntities())
{
var processes = (from p in context.ProcessStates.Include("ProcessType").Include("ProcessStatusType")
where p.IsActive && p.ProcessStateId == id
select p);
return processes.FirstOrDefault();
}
}
public ProcessState UpdateProcessState(ProcessState processState)
{
using (var context = new TaskManagerEntities())
{
context.ProcessStates.Add(processState);
context.Entry(processState).State = System.Data.EntityState.Modified;
context.SaveChanges();
}
return processState;
}
ProcessState is a parent to two other classes, ProcessStatusType and ProcessType. When I run that code in the windows service, it retrieves a record, updates the entity and saves it. Despite the fact that the ProcessType child is never used in the above code, when the save on the ProcessState entity is performed, EF does an insert on the ProcessType table and creates a new record in it. It then changes the FK in the ProcessStatus entity to point it at the new child and saves it to the database.
It does not do this in the ProcessStatusType table, which is set up with an essentially identical FK parent-child relationship.
I now have a database full of identical ProcessType entries that I don't need, and I don't know why this is occurring. I feel like I'm making some obvious mistake that I can't see because this is my first EF project. Is the issue that I'm allowing the context to expire in between calls but maintaining the same entity?
Using Add will set the state of all elements to Added, which is causing the child elements to be inserted. The parent element is not inserted as you specify EntityState.Modified for this element.
Try using the following in the UpdateProcessState rather than using Add.
context.ProcessStates.Attach(processState);
context.Entry(processState).State = EntityState.Modified;
context.SaveChanges();
Attach will set the state of all elements to Unchanged and by specifying Modified for the parent element you are indicating that only this element should be updated.
On another note. You should use the strongly-typed Include(x => x.ProcessType) rather than Include("ProcessType").
In my model I have two classes Categories and Products. There is a relation many- to many between them.
I set states of all categories on modified manually and when I watched in the debugger before saveChanges() I saw that all of these categories were marked as modified. But after request mapping between categories and product weren't updated in my database. Code of update function.
public void UpdateProduct(Product product)
{
using (EFDbContext context = new EFDbContext())
{
context.Products.Attach(product);
if (product.Categories != null)
{
foreach (var item in product.Categories)
{
context.Entry(item).State = EntityState.Modified;
}
}
context.Entry(product).State = EntityState.Modified;
context.SaveChanges();
}
}
Setting entity to modified says that you have changed its properties (not navigation properties) and you want to save them. If you changed relations (navigation properties) by for example creating new relation between existing product and category or removing relation between existing product and category setting state to modified will not help you. This is actually very hard to solve (it is same in all current EF versions) because that relation has its own state which must be set and state of relation cannot be Modified = you must know if you added or removed relation. Especially removing is hard because you probably don't have information about relations you have removed from Categories navigation property when you are going to attach entity to the context. Moreover DbContext doesn't offer access to state of the relation so you must convert it to ObjectContext and use ObjectStateManager.
The easiest way to solve this issue is to load product with categories from database prior to saving and manually synchronize your detached object graph (the one you are trying to save at the moment) with loaded attached graph. Once you synchronize all changes in attached graph you will save it back to database. Attached graph will know which relations to categories were added or removed.
I'm using POCO generated classes - any way that I can tweek the template so that when I remove from a child collection, the item removed is also deleted from the db ?
Or maybe in the partial class I can override something, catch an event ..?
Basically I want Order.OrderDetails.Remove(orderDetail) to remove the orderDetail from db.
I do not want to access the context and do context.OrderDetails.Delete(orderDetail).
When you remove an object from a collection navigation property, Entity Framework removes the relationship between the objects (nulling the property on the child object that refers to its parent).
If you want to delete a record, you need to mark the object as State = EntityState.Deleted. You can either do that by accessing the context, or if you don't want to, a workaround would be to identify the child objects that have been orphaned in the ChangeTracker, and set their State to Deleted there.
var orphans = context.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified && typeof(e.Entity) is ChildType);
foreach (DbEntityEntry orphan in orphans)
{
orphan.State = EntityState.Deleted;
}
I have a Linq to Sql Entity which has an EntitySet. In my View I display the Entity with it's properties plus an editable list for the child entites. The user can dynamically add and delete those child entities. The DefaultModelBinder works fine so far, it correctly binds the child entites.
Now my problem is that I just can't get Linq To Sql to delete the deleted child entities, it will happily add new ones but not delete the deleted ones. I have enabled cascade deleting in the foreign key relationship, and the Linq To Sql designer added the "DeleteOnNull=true" attribute to the foreign key relationships. If I manually delete a child entity like this:
myObject.Childs.Remove(child);
context.SubmitChanges();
This will delete the child record from the DB.
But I can't get it to work for a model binded object. I tried the following:
// this does nothing
public ActionResult Update(int id, MyObject obj) // obj now has 4 child entities
{
var obj2 = _repository.GetObj(id); // obj2 has 6 child entities
if(TryUpdateModel(obj2)) //it sucessfully updates obj2 and its childs
{
_repository.SubmitChanges(); // nothing happens, records stay in DB
}
else
.....
return RedirectToAction("List");
}
and this throws an InvalidOperationException, I have a german OS so I'm not exactly sure what the error message is in english, but it says something along the lines of that the entity needs a Version (Timestamp row?) or no update check policies. I have set UpdateCheck="Never" to every column except the primary key column.
public ActionResult Update(MyObject obj)
{
_repository.MyObjectTable.Attach(obj, true);
_repository.SubmitChanges(); // never gets here, exception at attach
}
I've read alot about similar "problems" with Linq To Sql, but it seems most of those "problems" are actually by design. So am I right in my assumption that this doesn't work like I expect it to work? Do I really have to manually iterate through the child entities and delete, update and insert them manually? For such a simple object this may work, but I plan to create more complex objects with nested EntitySets and so on. This is just a test to see what works and what not. So far I'm disappointed with Linq To Sql (maybe I just don't get it). Would be the Entity Framework or NHibernate a better choice for this scenario? Or would I run into the same problem?
It will definately work in Entity Framework that comes with .NET 4 (I'm doing similar things in the RC version)
This does not explain the exception but:
You should dispose the ObjectContext that's (most likely) wrapped in your repository. The context caches items, and should only be used for a single unit-of-work.
Try to use a pattern like:
public ActionResult Update(int id, MyObject obj) // obj now has 4 child entities
{
using(var repository = CreateRepository())
{
var obj2 = _repository.GetObj(id);
if(TryUpdateModel(obj2))
{
repository.SubmitChanges();
}
else
.....
}
return RedirectToAction("List");
}
When fetching items, create a new repository as well. They are cheap to create and dispose, and should be disposed as quickly as possible.