Entity Framework 4.1 Code First – Mapping Enums to Lookup Tables

This afternoon I Built a mechanism for mapping Enums to Lookup Tables in EF Code First - using complex types and implicit operators. The steps are as follows:
 
Step 1 – the Enum
 

// Avoid 0 values – SQL INT default value

 
    public enum JobInstanceStateEnum : int
    {
        Ok = 1,
        MissingResources = 2,
        DataFailure = 3,
        Aborted = 4
    }

 

 
Step 2 – the POCO for generating the Lookup Table
 

// As we are using id as FK, and it must match the enum, overide convention of identity keygen

 
    public class JobInstanceStateLookup
    {
        [DatabaseGenerated(DatabaseGenerationOption.None)]
        public int JobInstanceStateLookupId { getset; }
        public string Value { getset; }
    }

 

 
Step 3 – the POCO for generating the Entity Table

// Entity

 
    public class JobInstance
    {
        public int JobInstanceId { getset; }  // convention PK      
        public JobInstanceState Status { getset; } // enum ComplexType mapper
        public string blah { getset; }
    }

 

 
Step 4 – the ComplexType POCO for mapping the enum

 // Map

 
    [ComplexType]
    public class JobInstanceState
    {
        // FK + navigation property (must be virtual)
        [ForeignKey("JobInstanceStateLookup")]
        public int JobInstanceStateLookupId { getset; }
        public virtual JobInstanceStateLookup JobInstanceStateLookup { getset; }
        
        // ctors
        public JobInstanceState() : this(JobInstanceStateEnum.Ok) {}
        public JobInstanceState(JobInstanceStateEnum value)
        {
            JobInstanceStateLookupId = (int)value;
        }
        // implicit operators for auto casting the complex type to enum val
        public static implicit operator JobInstanceStateEnum(JobInstanceState type)
        {
            return (JobInstanceStateEnum)type.JobInstanceStateLookupId;
        }
        public static implicit operator JobInstanceState(JobInstanceStateEnum type)
        {
            return new JobInstanceState(type);
        } 
    }

 

 
Step 5 – DBContext and DBSets

 // DbContext – the Generator

 
    public class SpitfireContext : DbContext
    {   
        public DbSet<JobInstanceStateLookup> JobInstanceStateLookup { getset; }
        public DbSet<JobInstance> JobInstances { getset; }
        
    }
Step 6 – DB Initializer
// Initialize the DB – iterate over enum to populate lookup table
        public class Initializer : IDatabaseInitializer<MyContext>
        {
            public void InitializeDatabase(MyContext context)
            {
                if (!context.Database.Exists() || !context.Database.CompatibleWithModel(false))
                {
                    context.Database.Delete();
                    context.Database.Create();
                    var jobInstanceStateList = EnumExtensions.ConvertEnumToDictionary<JobInstanceStateEnum>().ToList();
                    jobInstanceStateList.ForEach(kvp => context.JobInstanceStateLookup.Add(
                        new JobInstanceStateLookup()
                            {
                                JobInstanceStateLookupId = kvp.Value,
                                Value = kvp.Key
                            }));
 
                    context.SaveChanges();
                }          
            }
        }
Step 7  - Unit Test
        [TestMethod]
        public void TestComplexTypeWithEnumToLookup()
        {
            // Arrange:
            DbDatabase.SetInitializer(new MyContext.Initializer());
            var db = new MyContext();
            var ji = new JobInstance()
                         { 
                           Blah   = "xxx",
                           Status = JobInstanceStateEnum.Aborted
                         };
            ji.Status = JobInstanceStateEnum.MissingResources; // The power of implicit operators
            // Act:
            db.JobInstances.Add(ji);
            db.SaveChanges();
 
// The type 'JobInstanceState' is mapped as a complex type. 
            // The Set method, DbSet objects, and DbEntityEntry objects can only be used with entity types, not complex types.
            // so theres no easy way to navigate to db.JobInstances.First().Status.JobInstanceStateLookup
            // which is good (albiet a leaky abstraction) because Code First doesnt support RO !
 
            var ji2 = db.JobInstances.First().Status;
            var enumString = JobInstanceStateEnum.MissingResources.ToString();
            var lookup = db.JobInstanceStateLookup.Where(l => l.Value == enumString).FirstOrDefault();
 
            // Assert:
            Assert.IsTrue(db.JobInstances.Count() > 0);
            
            Assert.AreEqual(ji2.JobInstanceStateLookupId, lookup.JobInstanceStateLookupId);
        }

 

 

Enjoy!

Print | posted on Wednesday, May 18, 2011 3:57 PM

Feedback

# re: Entity Framework 4.1 Code First – Mapping Enums to Lookup Tables

Left by Antony Koch at 5/23/2011 6:02 PM
Gravatar Hiya

I can't get this to work. I keep getting the error


The type 'My.Name.Space.UnifiedQuoteStatus' has already been configured as a complex type. It cannot be reconfigured as an entity type.

Any idea what this might mean?

# re: Entity Framework 4.1 Code First – Mapping Enums to Lookup Tables

Left by Antony Koch at 5/23/2011 6:47 PM
Gravatar Actually, implementing your code as a spike throws the same error. I'm using EF 4.1.

Can you re-post it with it working? Are yuo using the older version of EF? I ask because

DatabaseGenerationOption is now defunct and

[DatabaseGenerated(DatabaseGeneratedOption.None)]

Is the correct syntax.

# re: Entity Framework 4.1 Code First – Mapping Enums to Lookup Tables

Left by Josh Reuben at 5/24/2011 7:12 PM
Gravatar I'm using CTP 5 - It works on my machine! (cringe) - will check it with release version as soon as time permits.
cheers,
Josh

# re: Entity Framework 4.1 Code First – Mapping Enums to Lookup Tables

Left by Tom Miller at 5/26/2011 10:21 PM
Gravatar the usual developer answer ;)

please let us know as I'm experiencing exactly the same problem using ef 4.1 referenced using nuget.

didn't know CTP 5 was available.

cheers

# re: Entity Framework 4.1 Code First – Mapping Enums to Lookup Tables

Left by Ben at 6/6/2011 9:54 AM
Gravatar I get the same error.

According to http://msdn.microsoft.com/en-us/library/bb738472.aspx navigation properties on Complex Types are not allowed. This might have kicked in after CTP5 was released which would explain why it's not working with 4.1.

# re: Entity Framework 4.1 Code First – Mapping Enums to Lookup Tables

Left by Stefan Andersson at 11/8/2011 6:45 PM
Gravatar If you just need enum, try this out: http://blogs.planetcloud.co.uk/mygreatdiscovery/post/Handling-enumerations-in-entity-framework.aspx

Your comment:





 
 

Copyright © JoshReuben

Design by Bartosz Brzezinski

Design by Phil Haack Based On A Design By Bartosz Brzezinski