Enum as Key в Entity Framework 5 выдает ошибку при объединении многих ко многим

ОК, это немного длинно/неясно, но я получаю нечетную ошибку в конкретной ситуации, когда я использую 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;
        }

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

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

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

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


person Gene Reddick    schedule 31.08.2012    source источник
comment
Почему ПК диеты имеют тип DietIs? Разве диета не таблица перекрестных ссылок?   -  person zs2020    schedule 07.09.2012
comment
Что ж, в этом примере я хочу добавить некоторую информацию о конкретных диетах, скажем, описать веганство, описать без глютена и т. д. Я также хочу ограничить возможные значения значениями перечисления, которое я использую в коде, и я также не не хочу, чтобы бросить назад и вперед от int.   -  person Gene Reddick    schedule 07.09.2012
comment
Но PK этого объекта Diet должен быть обычным целым числом. И я предполагаю, что вы определили отношение внешнего ключа с помощью FluentAPI.   -  person zs2020    schedule 07.09.2012
comment
@Gene Мы тоже были сбиты с толку этой проблемой; именно так, как вы описали. Мы называем это ошибкой EF и решили, что типы перечислений недопустимы для использования в качестве первичных ключей. В ожидании того, что команда EF решит эту проблему, мы заключаем наше свойство int ID в enum и не рекомендуем использовать примитивное свойство int ID с ObsoleteAttribute. Таким образом, если/когда ошибка будет исправлена, у нас будет больше шансов, что изменения будут ограничены моделями данных. Я был бы рад узнать, если бы вы придумали другое/лучшее решение. Кроме того, вы отправили отчет об ошибке команде EF?   -  person Vinney Kelly    schedule 22.01.2013
comment
@ Винни Келли, я никогда не сообщал им об ошибках, они должны сканировать StackOverflow, как и любой другой уважаемый разработчик! :) Я так и не придумал решения и, к сожалению, также отказался от использования перечислений в качестве первичных ключей. vВозможно, дальше.   -  person Gene Reddick    schedule 23.01.2013
comment
Я согласен, и я также не думаю, что это новость для них. Однако в интересах проактивности я пошел дальше и создал отчет об ошибке. Спасибо за столь тщательное документирование этой проблемы. Это значительно упростило сообщение об ошибке :) -with-multiple-include-paths" rel="nofollow noreferrer">connect.microsoft.com/VisualStudio/feedback/details/777196/   -  person Vinney Kelly    schedule 23.01.2013
comment
Это значительно упростило сообщение об ошибке. Microsoft закрыла эту ошибку, потому что переход по ссылке на SO, по-видимому, был слишком трудоемким.   -  person Godeke    schedule 01.03.2013


Ответы (1)


Проблема, по-видимому, заключается в том, что enum в .NET является типом класса. Из определения на эта страница:

Предоставляет базовый класс для перечислений.

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

Перечисление — это набор именованных констант, базовым типом которых является любой целочисленный тип. Если базовый тип явно не объявлен, используется Int32. Enum — это базовый класс для всех перечислений в .NET Framework.

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

public DietIs DietIs { get; set; }

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

Вы можете преобразовать элемент перечисления в его базовый тип с помощью оператора приведения (в C#) или преобразования (в Visual Basic). В следующем примере операторы регистра или преобразования используются для выполнения преобразований как из целого числа в значение перечисления, так и из значения перечисления в целое число.

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


int value3 = 2;
ArrivalStatus status3 = (ArrivalStatus) value3;
int value4 = (int) status3;
person Only You    schedule 01.03.2013