Вопрос по .net-4.5, entity-framework-5, entity-framework, .net, enums – Enum as Key в каркасе сущностей 5, приводящий к ошибке во многих соединениях

25

Хорошо, это немного длинно / неясно, но я получаю странную ошибку в конкретной ситуации, когда я использую Enum в качестве ключа таблицы и пытаюсь выполнить запрос к таблице, в то же время включив более одного связанного объекта «многие ко многим».

Ошибка из приведенного ниже примера кода:

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

В веб-проекте .net 4.5 у меня есть следующая конфигурация объекта:

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;
    }
}

В файле Global.asax.cs я инициализирую базу данных:

 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();
        }

Затем я пытаюсь выполнить запрос к базе данных:

var context = new EnumTestContext();

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

Другие запросы, где я использую один, включают загрузку ожидаемых данных без проблем. Запрос выше, с двумя включениями, выдает ошибку выше. В базе данных я вижу автоматически сгенерированные таблицы соединений (MenuDiets и RecipeDiets), и все данные выглядят правильно. Опять же, как в приведенных выше примерах, я могу запрашивать данные, но не могу включать в себя несколько связанных сущностей без выдачи ошибки.

Если я изменю последний запрос на использование только одного включения, я могу загрузить другую таблицу без проблем:

        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;
        }

Далее & # x2014; хотя это не удовлетворяет моему варианту использования, так как я хочу ограничить таблицу только значениями перечисления, и уникальные ограничения не поддерживаются в EF & # x2014; это сработает, если я изменю класс сущности Diet на отдельный ключ идентификации вместо ключа Enum:

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

Другое возможное решение, которое я исследовал, было явное создание таблиц соединения (MenuDiets и RecipeDiets), чтобы ключ свойства соединения был напечатан как Enum, но это все равно вернуло вышеупомянутую ошибку.

Это действительно, кажется, несколько включений, которые заставляют его задыхаться. Есть идеи, что я делаю что-то не так в настройке модели? Сам запрос? Ошибка в Entity Framework?

Почему ПК Диеты имеют тип Диеты? Не является ли Diet таблица перекрестных ссылок? zs2020
@Gene Мы также были сорваны этой проблемой; именно так, как вы это описали. Мы называем это ошибкой EF и решили, что типы enum недопустимы для использования в качестве первичных ключей. В ожидании того, что команда EF решит эту проблему, мы обертываем наше свойство int ID перечислением и не рекомендуем использовать свойство примитивного int ID с помощью атрибута ObsoleteAttribute. Таким образом, если / когда ошибка будет исправлена, мы, скорее всего, будем ограничивать изменения моделями данных. Я был бы рад узнать, если вы придумали другое / лучшее решение. Кроме того, вы подали отчет об ошибке в команду EF? Vinney Kelly
Я согласен, и я также не думаю, что это новость для них. Тем не менее, в интересах проактивности, я пошел дальше и создал отчет об ошибках. Спасибо за подробное документирование этого вопроса. Это сделало сообщение об ошибке намного проще :)connect.microsoft.com/VisualStudio/feedback/details/777196/… Vinney Kelly
@ Винни Келли, я никогда не сообщал им об ошибке, они должны сканировать StackOverflow, как и любой другой уважаемый разработчик! :) Я никогда не придумывал решения и, к сожалению, также разочаровался в использовании enums в качестве первичных ключей. Следующее возможно. Gene Reddick
"Это сделало сообщение об ошибке намного проще". Microsoft закрыла эту ошибку, потому что переход по ссылке на SO, очевидно, был слишком большой работой. Godeke

Ваш Ответ

1   ответ
5

enum в .NET это тип класса. Из определения наэта страница:

Provides the base class for enumerations.

И это замечание:

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.

Да, он определяет набор констант, тип которых является целочисленным, но когда вы объявляете ваш ключ:

public DietIs DietIs { get; set; }

Ваш ключ на самом деле является типом класса, а не целым типом; вам может потребоваться привести его при сравнении или присвоении значений целочисленного типа. Страница предоставляет этот пример о конверсиях:

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;

Похожие вопросы