Persisting the Type Safe Enum Pattern with EF 6

Persisting the Type Safe Enum Pattern with EF 6

I’ve written about Enum Alternatives in C#, and a common problem developers encounter when they try to use such an approach is persistence. By default, ORM tools like Entity Framework don’t know what to do with these custom types. However, you can add support to let EF map these types to database columns as you would expect without too much effort. Here’s a quick example you can try yourself with a new MVC 5 project.

Step 1. Create a new Web Application in Visual Studio.

NewWebApp

Make sure to specify that you want individual user accounts.

Step 2. In the Models folder, find the ApplicationUser class.

We’re going to give it a new Role property that will use the type-safe enum pattern. Add this property:

Step 3. Define the Role Class

The Role class is our type-safe enum implementation. It must include whatever value you want persisted in the database to indicate that an ApplicationUser is in this role. This might be an Id property or a Value property, and it could be an int or a string or whatever makes sense for your application and data structure. In this case, it’s going to have a Value property and it’s going to start at 0 to properly emulate an enum.

Step 4. Map the Property for Entity Framework

Ok, so far, so good. If you try to generate a new schema now using EF migrations, the Role property is likely to be completely ignored. Let’s look at what we need to add to the DbContext to properly map this property:

This mapping tells EF to map the Role property based on its Value property, and to use a columntype of int. It also tells it to ignore the Name property on Role (by default every property on a complex type is mapped, and until recently, had to be mapped).

With this in place, you can enable migrations and add a new migration:

Looking at the generated ‘DATESTAMP_RoleProperty.cs’ file, you’ll find a new Role_Value column on AspNetUsers:

If you want to test that everything works, just create a new user and assign it to a role (or whatever enum you’re using). Then fetch it from persistence and confirm it has the value¬†you expected.

If you like, you can certainly write integration tests to confirm the same behavior.

This pattern is much more powerful than the standard enum keyword. I use it frequently and I hope you find it helpful.

  • Nice follow up to your last post! I’m not sure if I’ve misunderstood, but I can’t see how comparing the role objects directly is going to work. Won’t that be doing a reference comparison rather than comparing the Value property?

    Also, the role instantiated by EF won’t have the correct Name property will it?

    I had a quick play in LinqPad, assuming that EF uses Activator.CreateInstance to create the Role, and that it sets the Value property using reflection (not shown for simplicity):

    Which gives the results shown. Am I missing something? Is there some way we can hook in to EF when it creates the new Role and get it to use FromValue?

    Thanks!

    • Naeem Sarfraz

      Good point @andrewlockdotnet:disqus. I guess you should implement Equality for each of these type of classes.

      I’m using this pattern but I implemented the Value property fully in order to get round the fact the Name has not been set and results in a partially initialised object when EntityFramework is involved. Something like this:

      • Ah, of course, nice suggestion @Naeem Sarfraz:disqus, so a full implementation could look more like this, with the equals functions and operator overridden to get expected behaviour? I was worried about the implications for that in EF but it looks like overriding Equals and GetHashode is ok in EF6, you just have to watch out in EF5 (and I’m guessing in EF Core).

        
        public class Role
        {
            public static Role Author { get; } = new Role(0, "Author");
            public static Role Editor { get; } = new Role(1, "Editor");
            public static Role Administrator { get; } = new Role(2, "Administrator");
            public static Role SalesRep { get; } = new Role(3, "Sales Representative");
         
            private Role(int val, string name)
            {
                _value = val;
                Name = name;
            }
         
            private Role()
            {
                // required for EF
            }
         
            public string Name { get; private set; }
            private int _value;
            public int Value
            {
                get { return _value; }
                private set
                {
                    Name = FromValue(value).Name;
                    _value = value;
                }
            }
         
            public IEnumerable List()
            {
                return new[] { Author, Editor, Administrator, SalesRep };
            }
         
            public Role FromString(string roleString)
            {
                return List().FirstOrDefault(r => String.Equals(r.Name, roleString, StringComparison.OrdinalIgnoreCase));
            }
         
            public Role FromValue(int value)
            {
                return List().FirstOrDefault(r => r.Value == value);
            }
        	
            public override bool Equals(System.Object obj)
            {
                // If parameter is null return false.
        	Role other = obj as Role ;
                if (other == null){ return false; }
        
                return Equals(other);
            }
        
            public bool Equals(Role  other)
            {
                if ((object)other == null){ return false; }
        
                // Return true if the values match:
                return (Value == other.Value);
            }
        
            public override int GetHashCode()
            {
                return Value;
            }
        	
            public static bool operator == (Role a, Role b)
            {
                // If both are null, or both are same instance, return true.
        	if (System.Object.ReferenceEquals(a, b)){ return true; }	    
                // If one is null, but not both, return false.
        	if ((object)a == null || (object)b == null){ return false; }
        	
                // Return true if Equal match:
                return a.Equals(b);
            }
        
            public static bool operator != (Role a, Role b)
            {
                return !(a == b);
            }
        }
        </pre
        • Naeem Sarfraz

          Yeah, I use Resharper to generate the Equality comparisons. The ones I have look similar.

          I made a comment on Steve’s other article about enums and that was the FromString, FromValue & List methods should be static?

          • Yep, agreed, definitely should be static I think!

        • Naeem Sarfraz

          Here’s the full example again bu using Resharper’s Equality using members implementation.

          https://gist.github.com/naeemsarfraz/8b78d8ed228eca21e466fa1f8eb18e0b

  • Jonathan Edwards

    Hi

    I like my type-safe enums to use reference equality. I don’t want to implement equality code. To achieve that, FromId (or FromValue) must always be used. If that can be done when mapping from the database, it would be great. Any ideas??

    var role = Role.FromValue(intValueFromDatabase);