Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enum as Key in entity framework 5 throwing error on many to many joins

OK, this is a bit lengthy / obscure, but I'm getting an odd error in specific situation where I use an Enum as a table Key and attempt to query against the table while including more than one many-to-many related entities.

The error, from the example code below is:

The type of the key field 'DietIs' is expected to be 'MvcApplication8.Models.DietIs', but the value provided is actually of type 'System.Int32'.

In a .net 4.5 web project, I have the following entity configuration :

public enum DietIs {
    None,
    Kosher,
    Paleo,
    Vegetarian
}

public class Diet {

    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public DietIs DietIs { get; set; }

    public string Description { get; set; }
    public virtual ICollection<Recipe> Recipes { get; set; }
    public virtual ICollection<Menu> Menus { get; set; }
}

public class Recipe {
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Diet> Diets { get; set; }
}

public class Menu {
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Diet> Diets { get; set; }
}

public class EnumTestContextInit : DropCreateDatabaseAlways<EnumTestContext> {}

public class EnumTestContext : DbContext {
    public DbSet<Diet> Diets { get; set; }
    public DbSet<Menu> Menus { get; set; }
    public DbSet<Recipe> Recipes { get; set; }

    public EnumTestContext() : base("EnumTestContext") {
        Configuration.LazyLoadingEnabled = false;
        Configuration.ProxyCreationEnabled = false;
    }
}

In the Global.asax.cs file I initialize the database:

 Database.SetInitializer(new EnumTestContextInit());
        using (var context = new EnumTestContext()) {

            var noDiet = new Diet { DietIs = DietIs.None, Description = "Whatever you want" };
            var paleoDiet = new Diet { DietIs = DietIs.Paleo, Description = "Like paleolithic peoples" };
            var vegDiet = new Diet { DietIs = DietIs.Vegetarian, Description = "No meat" };

            context.Menus.Add(new Menu { Name = "Cheese burger with Fries Menu", Diets = new List<Diet> { noDiet } });
            context.Menus.Add(new Menu { Name = "Mammoth Steak Tartar with Nuts Menu", Diets = new List<Diet> { paleoDiet, noDiet } });
            context.Menus.Add(new Menu { Name = "Soy Cheese Pizza Menu", Diets = new List<Diet> { vegDiet, noDiet } });

            context.Recipes.Add(new Recipe {Name = "Cheese burger", Diets = new List<Diet> {noDiet}});
            context.Recipes.Add(new Recipe { Name = "Mammoth Steak Tartar", Diets = new List<Diet> { paleoDiet, noDiet} });
            context.Recipes.Add(new Recipe { Name = "Cheese Pizza", Diets = new List<Diet> { vegDiet, noDiet } });

            context.SaveChanges();
        }

Then, I attempt to query against the database:

var context = new EnumTestContext();

        var dietsWithMenusAndRecipes = context.Diets
                  .Include(e => e.Menus)
                  .Include(e => e.Recipes)
                  .ToList();

Other queries where I use a single include load the expected data without issue. The query above, with two includes throws the error above. In the database I see autogenerated join tables (MenuDiets and RecipeDiets) and all the data looks correct. Again, as in the examples above I can query against the data but can't include multiple related entities without throwing the error.

If I change the last query to only use a single include, I can load the other table without issue:

        var dietsWithMenusAndRecipes = context.Diets
                 .Include(e => e.Menus).ToList();

        foreach (var item in dietsWithMenusAndRecipes) {
            context.Entry(item).Collection(e => e.Recipes).Load();
            var rec = item.Recipes;
        }

Further — though this does not satisfy my use case as I want to restrict the table to just the enum values and unique constraints aren't supported in EF — this will work if I change the Diet entity class to use a separate identity key, rather than the Enum key:

    public int Id { get; set; }
    public DietIs DietIs { get; set; }

Another possible solution I explored was to explicitly create the join tables (MenuDiets and RecipeDiets) so that the join property key was typed as the Enum, but this still returned the above error.

It really seems to be the multiple Includes that are causing it to choke. Any ideas as to whether I'm doing something wrong in the model setup? The query itself? A bug in Entity Framework?

like image 876
Gene Reddick Avatar asked Aug 31 '12 18:08

Gene Reddick


1 Answers

The issue seems to be the fact that enum in .NET is a class type. From the definition on this page:

Provides the base class for enumerations.

And this remark:

An enumeration is a set of named constants whose underlying type is any integral type. If no underlying type is explicitly declared, Int32 is used. Enum is the base class for all enumerations in the .NET Framework.

Yes it defines a set of constants whose type is an integral type but when you declare yor key:

public DietIs DietIs { get; set; }

Your key is actually a class type not an integral type; you may have to cast it when comparing or assigning values of integral type. The page provides this example about conversions:

You can convert between an enumeration member and its underlying type by using a casting (in C#) or conversion (in Visual Basic) operator. The following example uses casing or conversion operators to perform conversions both from an integer to an enumeration value and from an enumeration value to an integer.

public enum ArrivalStatus { Late=-1, OnTime=0, Early=1 };


int value3 = 2;
ArrivalStatus status3 = (ArrivalStatus) value3;
int value4 = (int) status3;
like image 140
Only You Avatar answered Nov 11 '22 07:11

Only You