views:

409

answers:

1

In my data model I have a fairly common division between my objects/tables/data:

  • "Transactional" entities that represent the work that is being done by the system. These entities are created by the system and are only important in specific contexts. They are regularly created on the fly. (Aside: Is there a proper name for this type of entity?)
  • "Data Dictionary" entities that represent common properties of the transactional entities. These are defined irregularly (mostly at the start of the project) and have a much more static lifecycle. They are typically created by me.

So, for example, I might have a User (transactional) entity and a UserType (data dictionary) entity.

I want to (hard)code references to instances of the Data Dictionary entities into my code. This gives me the ability to code business logic with an easy-to-understand language. (So, for example, I might have a UserType of "Plain" and a UserType of "Admin", and then a rule that says "allow access only if the User's UserType equals Admin").

I'm using LINQ-to-Entities as my data access technology/ORM. To implement the Data Dictionary references, I'm storing EntityKeys. My understanding is that they're detached from the object context and so are suitable for this purpose. (As they don't contain entity state, I also don't have to worry about that state going stale.)

However, this is giving me problems when I try to add a new transactional entity with a DD-EntityKey-reference. Continuing my example, I'm doing this:

UserEntities userEntities = new UserEntitites()
User user = new User()
user.UserType = new UserType()
user.UserType.EntityKey = adminEntityKey
userEntities.AddToUser(user)

...and this gives me the following error:

System.InvalidOperationException : The object cannot be added to the ObjectStateManager because it already has an EntityKey. Use ObjectContext.Attach to attach an object that has an existing key.

If I try to call userEntities.Attach(user) instead of AddToUser, I get this:

System.InvalidOperationException : An object with a null EntityKey value cannot be attached to an object context.

Both of these errors make sense, given the mixing of new and preexisting entities that I'm doing. What I'm not sure about is how to get around this issue. Is there some way I can have detached references to DD-entities and assign them to new attached objects without requiring me to load the entire DD-entity state?

+1  A: 

Hello,

I'll try to explain why are you getting these exceptions:

System.InvalidOperationException : The object cannot be added to the ObjectStateManager because it already has an EntityKey. Use ObjectContext.Attach to attach an object that has an existing key.

New User and UserType entities are created. The EntityKey on UserType has been set but when you pass it to userEntities.Add() EF tries to set them status ADDED. Since UserType has an EntityKey EF realises something is wrong and throws exception.

System.InvalidOperationException : An object with a null EntityKey value cannot be attached to an object context.

In this exception everything is ok with UserType, but the exception is thrown because User entity - it has no EntityKey and therefore cannot be attached.

This is how I would solve this problem (if, as you say, hardcoding of data dictionary references is ok):

On UserType entity I would create static references to UserType id's:

public partial class UserType
    {
        public const int AdminUserTypeID = 1;
        public const int PlainUserTypeID = 2;
    }

On User entity there would be extender property for easier access to the UserType foreign key:

public partial class User
{
    public int? UserTypeID
    {
        set
        {
           UserTypeReference.EntityKey = new EntityKey("UserEntities.UserType", "UserTypeID", value);
        }
        get
        {
            if (UserTypeReference.EntityKey == null)
                return null;

            if (UserTypeReference.EntityKey.EntityKeyValues.Count() > 0)
                return (int)UserTypeReference.EntityKey.EntityKeyValues[0].Value;
            else
                return null;
        }
    }
}

Finally, the use case for creating new user would look like this:

using(UserEntities userEntities = new UserEntitites())
{
    User user = new User();
    user.UserTypeID = UserType.AdminUserTypeID;
    userEntities.AddToUser(user);
    userEntities.SaveChanges()
}
Misha N.