3

Using EF4 Self-tracking entities.

I have a "User" entity that has a collection of "Groups" the user can belong to. I want to add/remove some "Groups" to this user given just a list of Group IDs.

    public void UserAddGroups(int userID, List<int> groups)
    {
        var user = Context.Users.Include("Groups").FirstOrDefault(u => u.ID == userID);
        if (user != null)
        {
            // Iterate through the groups that the user already belongs to.
            foreach (var group in user.Groups.ToList())
            {
                // Remove any groups from the user if the new list does not have it.
                if (!groups.Contains(group.ID)) user.Groups.Remove(group);
                // Else remove it from the list of new groups to avoid duplication.
                else groups.Remove(group.ID);
            }
            // Iterate through the group list and add it to the user's list
            // (only a stubby is created)
            foreach (var group in groups) user.Groups.Add(new Group { ID = group }.MarkAsUnchanged());
            Context.Users.ApplyChanges(user);
            Context.SaveChanges();
        }
    }

The result in this method throws an error at Context.SaveChanges(). The error reports that "Group" entities does not allow null for Name property.

This is expected if I were INSERTING new groups, but thats obviously not what I'm trying to do. How can I fix this problem?

Tri Q Tran
  • 5,500
  • 2
  • 37
  • 58

2 Answers2

2

You actually ARE inserting. By creating a new group and adding it to the collection in essense you are saying, here is a new group now add it. You need to load the group from the database first to activate tracking on it. then add the group to the users group collection.

amit_g's solution will work but will result in several DB calls ( A DB call per group). I would pre load all the groups up front

Slappy
  • 4,042
  • 2
  • 29
  • 41
  • You're telling me that .MarkAsUnchanged does nothing to the entity and still forces an insert into the context? That seems more like a work around than something that you can do intuitively. – Tri Q Tran Mar 17 '11 at 00:23
  • 1
    Hey I never designed it. But it has to do with wiring up the DB key to the Datacontext, it also enforces concurrency. The problem with doing something "intuitively" is that you are abstracted from the mechanics of the system. In this case you hitting the DB multiple times using intuition. Performance will suffer. I see a LOT of people make this mistake with ORM's. Just cause the metaphor has shifted from DB requests to object requests, doesnt mean those DB calls arent happening. – Slappy Mar 22 '11 at 02:49
  • Sorry if my comment came through as an accusation. Thanks for the reply and you are right about the abstraction can cause many to forget that it still needs to make the DB calls. I'm still very noob with EF and STE. – Tri Q Tran Mar 23 '11 at 02:50
  • No worries. Just remember these 3 rules: #1: You are NOT dealing with objects on the client when you perform a query, #2 You are NOT dealing with data ON the DB server (ala Management Studio queries). #3 You ARE dealing with a communication PIPE between client and server. This is a request/response architecture (ala HTTP) These observations may seem obvious, but are often forgotten. – Slappy Mar 23 '11 at 02:59
1

Try it with

user.Groups.Add(Context.Groups.FirstOrDefault(g => g.ID == group));
amit_g
  • 30,880
  • 8
  • 61
  • 118