This is my first post here, so I hope everything is fine.
Here is my problem:
I have a table in my database called UserTypes. It has:
ID;
IsPrivate;
Parent_ID;
The relevant ones are the first and the third one.
I have another table called UserTypes_T which has information for the different types, that is language specific. The fields are:
Language_ID;
UserType_ID;
Name;
What I'm trying to achieve is load the entire hierarchy from the UserTypes table and show it in a TreeView (this is not relevant for now). Then, by selecting some of the user types I can edit them in separate edit box (the name) and a combo box (the parent).
Everything works fine until I try to persist the changes in the database. EF has generated for me two entity classes for those tables:
The class for the user types has:
ID;
IsPrivate;
Parent_ID;
A navigational property for the self-reference (0..1);
A navigational property for the child elements;
Another navigational property for the UserTypes_T table (1..*);
The class for the translated information has:
UserType_ID;
Language_ID;
Name;
A navigational property to the UserTypes table (*..1);
A navigational property to the Languages table (*..1);
I get the data I need using:
return context.UserTypes.Include("UserTypes_T").Where(ut => ut.IsPrivate==false).ToList();
in my WCF Web service. I can add new user types with no problems, but when I try to update the old ones, some strange things happen.
If I update a root element (Parent_ID==null) everything works!
If I update an element where Parent_ID!=null I get the following error:
AcceptChanges cannot continue because the object’s key values conflict with another object in the ObjectStateManager.
I searched all over the internet and read the blog post from Diego B Vega (and many more) but my problem is different. When I change a parent user type, I actually change the Parent_ID property, not the navigational property. I always try to work with the IDs, not the generated navigational properties in order to avoid problems.
I did a little research, tried to see what is the object graph that I get and saw that there were lots of duplicate entities:
The root element had a list of its child elements. Each child element had a back reference to the root or to its parent and so on. You can imagine. As I wasn't using those navigational properties, because I used the IDs to get/set the data I needed, I deleted them from the model. To be specific I deleted points 4 and 5 from the UserTypes entity class. Then I had an object graph with each element only once. I tried a new update but I had the same problem:
The root element was updated fine, but the elements, that had some parents, threw the same exception.
I saw that I had a navigational property in the UserTypes_T entity class, pointing to a user type, so I deleted it too. Then this error disappeared. All the items in the object graph were unique. But the problem remained - I could update my root element with no problems, but when trying to update the children (with no exclusions) I got a null reference exception in the generated Model.Context.Extensions class:
if (!context.ObjectStateManager.TryGetObjectStateEntry(entityInSet.Item2, out entry))
{
context.AddObject(entityInSet.Item1, entityInSet.Item2);//here!
}
I tried to update only the name (which is in UserTypes_T) but the error is the same.
I'm out of ideas and I've been trying to solve this problem for 8 hours now, so I'll appreciate if someone gives me ideas or share their experience.
PS:
The only way I succeeded updating a child object was using the following code to retrieve the data:
var userTypes = argoContext.UserTypes.Include("UserTypes_T").Where(ut => ut.IsPrivate==false).ToList();
foreach (UserType ut in userTypes)
{
ut.UserType1 = null;
ut.UserTypes1 = null;
}
return userTypes;
where UserType1 is the navigational property, pointing to the parent user type and UserTypes1 is the navigational property, holding a list of the child element. The problem here was that EF "fixups" the objects and changes the Parent_ID to null. If I set it back again, EF sets the UserTypes1, too... Maybe there is a way to stop this behavior?
OK everybody, I just found what the problem was and I'm posting the answer if anybody else encounters the same issue.
The problem was that I was making some validation on the server in order to see if there isn't a circular reference between the user types. So, my method on the server looked something like:
using (MyEntities context = new MyEntities())
{
string errMsg = MyValidator.ValidateSomething(context.UserTypes,...);
if (!string.IsNullOrEmpty(errMsg)) throw new FaultException(errMsg);
//some other code here...
context.UserTypes.ApplyChanges(_userType);//_userType is the one that is updated
context.UserTypes.SaveChanges();
}
The problem is that when making the validation, the context is filled and when trying to save the changes, there are objects with the same key values.
The solution is simple - to use different context for validating things on the server:
using (MyEntities validationContext = new MyEntities())
{
//validation goes here...
}
using (MyEntities context = new MyEntities())
{
//saving changes and other processing...
}
Another one can be:
using (MyEntities context = new MyEntities())
{
using (MyEntities validationContext = new MyEntities())
{
//validation
}
//saving changes and other processing...
}
That's it! I hope it can be useful to somebody!
Related
I am having trouble when trying to delete a field of an Entity using Entity Framework (version 6.1.3).
Let's say I have two Entities: Person and Work.
I can change the work of a person without any issue, but when I try to express that the person is unemployed it does not work properly:
person.Work = null;
db.SaveChanges();
After running this code the person still will have the previous work, but if I use the debugger and check the Work property of person before running
person.Work = null;, everything will behave as expected.
Can someone please explain why reading the value first makes the code work properly and how to correctly delete the field?
var work = person.Work; \\ with this line here everything works as expected
person.Work = null;
db.SaveChanges();
Two things that are contributing to your issue:
Entity Framework determines what needs to updated during SaveChanges by tracking changes to property values.
You probably have lazy loading enabled (both in general and for the Work property), which means that if the person has an associated Work, that associated entity doesn't get loaded until the first time you access that property.
Putting those together, when you set person.Work = null without accessing person.Work (which would trigger a load), the context thinks nothing has changed. But if you load the property first, setting the property to null tells EF to remove that association. Edit: According to the page that octavioccl linked, this is true for .NET 4.0., but for .NET 4.5+ (and EF 5+), loading first is unneeded.
Possible solutions
If you want to remove the association without loading the related entity, you'll need to add a foreign key property to your Person entity, then you can set that to null instead of setting the navigation property to null. For example:
public class Person
{
// other properties...
public int? WorkId { get; set; }
public virtual Work { get; set; }
}
person.WorkId = null;
db.SaveChanges();
octavioccl's answer quoted another option:
context.Entry(person).Reference(p => p.Work).CurrentValue = null;
From this msdn page:
To delete the relationship, set the navigation property to null. If
you are working with the Entity Framework that is based on .NET 4.0,
then the related end needs to be loaded before you set it to null. For
example:
context.Entry(person).Reference(p => p.Work).Load();
person.Work = null;
Starting with the Entity Framework 5.0, that is based on .NET 4.5, you
can set the relationship to null without loading the related end. You
can also set the current value to null using the following method:
context.Entry(person).Reference(p => p.Work).CurrentValue = null;
i'm currently using GXT 3 to display elements in a Tree.
These elements are retrieved from database and identified in the Tree by their id (by that, I mean that the id is the ModelKeyProvider of my store).
I also made it possible for users to create objects locally in the tree with the following code:
private Tree<EntityDAO, String> tree;
private TreeStore<EntityDAO> store;
int count = 1;
// instanciation and irrelevant stuff
...
EntityDAO sel = tree.getSelectionModel().getSelectedItem();
EntityDAO child = new EntityDAO();
child.setId((long) count);
store.add(store.getParent(sel), child);
count++;
tree.setExpanded(sel, true);
tree.getSelectionModel().select(child, false);
As you can see, i set a temporary id (count) to my local object.
The issue occurs when I save my object in database. A permanent id is then set to my EntityDAO but when i try to set this id to my local object to sync it with the database, it doesn't work.
I've tried to modify the child id directly
child.setId(result);
tree.update(child);
I've tried to add a copy of my object with the proper id, and then to remove my object from the tree
EntityDAO newPR = child;
newPR.setId(result);
store.add(store.getParent(child), newPR);
store.remove(child);
But the display is never updated. Any clue?
Let's discuss about the first way you tried, the update method:
child.setId(result);
tree.update(child);
From the update method API state this :
Replaces the item that matches the key of the given item, and fires a
StoreUpdateEvent to indicate that this change has occurred. Any
changes to the previous model via it's record instance will be lost
and the record will be removed. This will not cause the sort or filter
to be re-applied to the object. Overrides: update(...) in Store
Parameters: item the new item to take its place in the Store.
So basically, the update method will replace the item inside the store that have the same key with your parameter. Your data have a new key that doesn't exist inside the store, that's why it doesn't effected anything to your tree display.
Second, let's discuss the create a copy of your object and set it with the proper id:
EntityDAO newPR = child;
newPR.setId(result);
store.add(store.getParent(child), newPR);
store.remove(child);
This way actually will work, but you only have one small problem. The first line of your code actually just give you a variable that have a reference to your old object (the child object), so whenever you remove the child, the newPR also removed. You should really create a new object using the constructor, here how I think you should do it:
EntityDAO newPR = new EntityDAO();
newPR.setId(result);
newPR.setOtherProperty(child.getOtherProperty());
// just copy all property of child to newPR
store.add(store.getParent(child), newPR);
store.remove(child);
Hope this can help you.
I have the following scenario:
I am using EF with Repository pattern and Unit Of Work from here
Then I extended my partial class and have created a Finder class which have GetAll and other methods.
You can this see below:
Below you can see that I am using a unit of work class with repositories of all classes to get the instance from the generic repository:
protected Repository<Category> _CategoryRepository;
public Repository<Category> CategoryRepository
{
get { return _CategoryRepository ?? (_CategoryRepository = new Repository<Category>(context)); }
}
So in this way I had different repositories and when getting an entity from db and updating it from a different context caused problems. So I used this method to use for context lifetime management. And it resolved that issue.
Now the problem I am facing is in the following code:
var cat = Instance.Category.GetSingle(c => c.CategoryID == 7);
var orignal = cat.CategoryName;
var expected = cat.CategoryName + " Test Catg Update";
cat.CategoryName = expected;
cat.Update(); //This doesn't actually update due to a validation in place (which is correct)
cat = Instance.Category.GetSingle(c => c.CategoryID == 7);
Assert.AreEqual(cat.CategoryName, expected);
When I use Update and I have some validators in it, and the Update fails (for example due to size of the string exceeds 15 characters). When I try to call GetSingle (2nd last line of above code) again, it brings me the same old record which is [cat.CategoryName + " Test Catg Update"]. Is this a normal way? If not how can this be fixed so I can reload the object from database.
Let me know if you need any other code or reference.
in your Update() method, if the validation fails, simply set the state of the entity to EntityState.Unchanged. Under the covers, changing the state of an entity from Modified to Unchanged first sets the values of all properties to the original values that were read from the database when it was queried, and then marks the entity as Unchanged. This will also reject changes to FK relationships since the original value of the FK will be restored.
You may be able to take advantage of the ObjectContext.Refresh() method to handle this as well. However, this is on the ObjectContext, so you would need a method to handle it in your UoW. The refresh method would be something like context.Refresh(RefreshMode.ServerWins, entity);
I'm using .NET 4.0 with EF POCO's. I have a conceptual model from which a part is shown in the image. Everything works fine, except one thing which I have been almost busy with for 2 days and still don't get it to work!
In my conceptual model, a WebShop can have one or more related WebShopCategory instances. Each WebShopCategory associates a WebShop with a Category and enables me to specify additional properties for a WebShop/Category combination.
See image here: http://postimage.org/image/djrn4xh37/
When creating a new WebShop instance of course I need to save it, which I do in an Update method of a WebShopRepository which uses the EF ObjectContext as shown in the code below:
using (MyEntities entities = new MyEntities()) {
if (webShop.ID == 0) {
entities.Countries.Attach(webShop.Country);
foreach (PaymentMethod paymentMethod in webShop.PaymentMethods) {
entities.PaymentMethods.Attach(paymentMethod);
}
foreach (QualityMark qualityMark in webShop.QualityMarks) {
entities.QualityMarks.Attach(qualityMark);
}
foreach (WebShopCategory category in webShop.Categories) {
entities.Categories.Attach(category.Category);
}
entities.WebShops.AddObject(webShop);
}
entities.SaveChanges();
}
One thing to note is that all related items already exist in the data source. As they are not yet in the ObjectContext, I've attached them before adding the created WebShop to the ObjectContext. This seems to work as expected: all entities that have been attached have an EntityState.Unchaged state which is correct. The added WebShop is in the Added state which also is correct.
However, it can happen that when I attach the Category instances (category.Category) EF gives an exception stating that there is already an item in the object context with the same key. I have found this ObjectStateEntry instance and what I noticed was that the EntityKey only consists of the EntitySet name, and not with the ID also in the key. Other EntityKeys that have been added to the context are all in the form of EntitySetName;ID=value
To me it seems that the entity key is somehow not being determined correctly by the EF, because at the point where it goes wrong I've checked and made sure that the added category is indeed valid: it is a category already known in the sytem and has its primary key (ID) value set to 1 (which is the first added category in the system).
I've tried many ways to fix this but cannot get this to work. Any help would be greatly appreciated!
Thank you all very much in advance!
To facilitate control reuse we created a solution with three separate projects: a control library, Silverlight client, and ASP.NET backend. The control library has no reference to the RIA Services-generated data model classes so when it needs to interact with it, we use reflection.
This has worked fine so far but I've hit a bump. I have a DataGrid control where the user can select a row, press the 'delete' button, and it should remove the entity from the collection. In the DataGrid class I have the following method:
private void RemoveEntity(Entity entity)
{
// Use reflection to remove the item from the collection
Type sourceType = typeof(System.Windows.Ria.EntityCollection<>);
Type genericType = sourceType.MakeGenericType(entity.GetType());
System.Reflection.MethodInfo removeMethod = genericType.GetMethod("Remove");
removeMethod.Invoke(this._dataGrid.ItemsSource, new object[] { entity });
// Equivalent to: ('Foo' derives from Entity)
// EntityCollection<Foo> ec;
// ec.Remove(entity);
}
This works on the client side but on the domain service the following error gets generated during the Submit() method:
"The UPDATE statement conflicted with
the FOREIGN KEY constraint
"********". The conflict occurred in
database "********", table "********",
column '********'. The statement has
been terminated."
One thing I noticed is the UpdateFoo() service method is being called instead of the DeleteFoo() method on the domain service. Further inspection shows the entity is going into the ModifiedEntities ChangeSet instead of the RemovedEntities ChangeSet. I don't know if that's the problem but it doesn't seem right.
Any help would be appreciated, thanks,
UPDATE
I've determined that the problem is definitely coming from the reflection call to the EntityCollection.Remove() method. For some reason calling it causes the entity's EntityState property to change to EntityState.Modified instead of EntityState.Deleted as it should.
Even if I try to remove from the collection by completely circumventing the DataGrid I get the exact same issue:
Entity selectedEntity = this.DataContext.GetType().GetProperty("SelectedEntity").GetValue(this.DataContext, null) as Entity;
object foo = selectedEntity.GetType().GetProperty("Foo").GetValue(selectedEntity, null);
foo.GetType().InvokeMember("Remove", BindingFlags.InvokeMethod, null, foo, new object[] { entity });
As a test, I tried modifying the UpdateFoo() domain service method to implement a delete and it worked successfully to delete the entity. This indicates that the RIA service call is working correctly, it's just calling the wrong method (Update instead of Delete.)
public void UpdateFoo(Foo currentFoo)
{
// Original update implementation
//if ((currentFoo.EntityState == EntityState.Detached))
// this.ObjectContext.AttachAsModified(currentFoo, this.ChangeSet.GetOriginal(currentFoo));
// Delete implementation substituted in
Foo foo = this.ChangeSet.GetOriginal(currentFoo);
if ((foo.EntityState == EntityState.Detached))
this.ObjectContext.Attach(foo);
this.ObjectContext.DeleteObject(foo);
}
I've been researching a similar issue.
I believe the issue is you are calling remove with a reference for an EntityCollections within the DomainContext as the root reference rather than using the DomainContext itself as the root.
So...
ParentEntityCollection.EntityCollectionForTEntity.Remove(TEntity);
Produces the EntityState.Modified instead of EntityState.Deleted
Try instead...
DomainContext.EntityCollectionForTEntity.Remove(TEntity);
I think this will produce the result you are seeking.
Hope this helps.
What is the "column" in the "FOREIGN KEY constraint" error? Is this a field in the grid row and collection that coorosponds to that column? Is it possible that the entity you are trying to remove is a column in the row rather than the row itself which is causing an update to the row (to null the column) rather than to delete the row?
I read your update and looks like you've determined that the problem is the reflection.
Have you tried to take the reflection out of the picture?
As in:
private void RemoveEntity(Entity entity)
{
// Use reflection to remove the item from the collection
Type sourceType = typeof(System.Windows.Ria.EntityCollection<>);
Type genericType = sourceType.MakeGenericType(entity.GetType());
// Make sure we have the right type
// and let the framework take care of the proper invoke routine
if (genericType.IsAssignableFrom(this._dataGrid.ItemsSource.GetType()))
((Object) this._dataGrid.ItemsSource).Remove(entity);
}
Yes, I know it's ugly, but some times...
Edited to add
I've updated the code to remove the is keyword.
Now about using the object to make the call to the Remove method, I believe it might work due the late binding of it.