开发者

Problem with EF STE and Self-Referencing tables

开发者 https://www.devze.com 2023-03-11 17:40 出处:网络
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:

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:

  1. ID;
  2. IsPrivate;
  3. 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:

  1. Language_ID;
  2. UserType_ID;
  3. 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:

  1. ID;
  2. IsPrivate;
  3. Parent_ID;
  4. A navigational property for the self-reference (0..1);
  5. A navigational property for the child elements;
  6. Another navigational property for the UserTypes_T table (1..*);

The class for the translated information has:

  1. UserType_ID;
  2. Language_ID;
  3. Name;
  4. A navigational property to the UserTypes table (*..1);
  5. 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开发者_C百科 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!

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号