2012-06-23 5 views
24

Я хотел бы определить перечисление для EF5 для использования и соответствующую таблицу поиска. Я знаю, что EF5 теперь поддерживает перечисления, но из-за-коробки, кажется, он поддерживает это только на уровне объекта и по умолчанию не добавляет таблицу для этих значений поиска.EF5 Code First Enums and Lookup Tables

К примеру, у меня есть объект пользователя:

public class User 
{ 
    int Id { get; set; } 
    string Name { get; set; } 
    UserType UserType { get; set; } 
} 

И в UserType перечисление:

public enum UserType 
{ 
    Member = 1, 
    Moderator = 2, 
    Administrator = 3 
} 

Я хотел бы для генерации базы данных для создания таблицы, что-то вроде:

create table UserType 
(
    Id int, 
    Name nvarchar(max) 
) 

Возможно ли это?

+0

голос пользователя для этого в EF http://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/2683498-enhance-enums-by- create-a-lookup-table-with-the –

ответ

13

Это непосредственно не возможно. EF поддерживает перечисления на том же уровне, что и .NET, поэтому значение enum просто называется integer => enum property в классе всегда является целым столбцом в базе данных. Если вы хотите также иметь таблицу, вам необходимо создать ее вручную в своем инициализаторе базы данных вместе с внешним ключом в User и заполнить ее значениями перечисления.

Я сделал несколько proposal on user voice, чтобы обеспечить более сложные отображения. Если вы сочтете это полезным, вы можете проголосовать за это предложение.

+1

Thanks Ladislav. Я поддержал ваше предложение. –

+1

@ Ladislav Mrnka, поддержка Enum EF не ограничивается целым числом. «перечисление может иметь следующие базовые типы: байты, Int16, Int32, Int64 или SByte». https://msdn.microsoft.com/en-us/data/hh859576 – itanex

-2

вы должны настроить свой рабочий процесс генерации

1. Copy your default template of generation TablePerTypeStrategy 

Location : \Microsoft Visual Studio 10.0\Common7\IDE\Extensions\Microsoft\Entity Framework Tools\DBGen. 

2. Add custom activity who realize your need (Workflow Foundation) 

3. Modify your section Database Generation Workflow in your project EF 
+0

В сертификате 70-516 у вас есть ответ на этот вопрос –

8

Я написал небольшой вспомогательный класс, который создает таблицу базы данных для перечислений, указанных в классе UserEntities. Он также создает внешний ключ в таблицах, ссылающихся на перечисление.

Так вот:

public class EntityHelper 
{ 

    public static void Seed(DbContext context) 
    { 
     var contextProperties = context.GetType().GetProperties(); 

     List<PropertyInfo> enumSets = contextProperties.Where(p =>IsSubclassOfRawGeneric(typeof(EnumSet<>),p.PropertyType)).ToList(); 

     foreach (var enumType in enumSets) 
     { 
      var referencingTpyes = GetReferencingTypes(enumType, contextProperties); 
      CreateEnumTable(enumType, referencingTpyes, context); 
     } 
    } 

    private static void CreateEnumTable(PropertyInfo enumProperty, List<PropertyInfo> referencingTypes, DbContext context) 
    { 
     var enumType = enumProperty.PropertyType.GetGenericArguments()[0]; 

     //create table 
     var command = string.Format(
      "CREATE TABLE {0} ([Id] [int] NOT NULL,[Value] [varchar](50) NOT NULL,CONSTRAINT pk_{0}_Id PRIMARY KEY (Id));", enumType.Name); 
     context.Database.ExecuteSqlCommand(command); 

     //insert value 
     foreach (var enumvalue in Enum.GetValues(enumType)) 
     { 
      command = string.Format("INSERT INTO {0} VALUES({1},'{2}');", enumType.Name, (int)enumvalue, 
            enumvalue); 
      context.Database.ExecuteSqlCommand(command); 
     } 

     //foreign keys 
     foreach (var referencingType in referencingTypes) 
     { 
      var tableType = referencingType.PropertyType.GetGenericArguments()[0]; 
      foreach (var propertyInfo in tableType.GetProperties()) 
      { 
       if (propertyInfo.PropertyType == enumType) 
       { 
        var command2 = string.Format("ALTER TABLE {0} WITH CHECK ADD CONSTRAINT [FK_{0}_{1}] FOREIGN KEY({2}) REFERENCES {1}([Id])", 
         tableType.Name, enumProperty.Name, propertyInfo.Name 
         ); 
        context.Database.ExecuteSqlCommand(command2); 
       } 
      } 
     } 
    } 

    private static List<PropertyInfo> GetReferencingTypes(PropertyInfo enumProperty, IEnumerable<PropertyInfo> contextProperties) 
    { 
     var result = new List<PropertyInfo>(); 
     var enumType = enumProperty.PropertyType.GetGenericArguments()[0]; 
     foreach (var contextProperty in contextProperties) 
     { 

      if (IsSubclassOfRawGeneric(typeof(DbSet<>), contextProperty.PropertyType)) 
      { 
       var tableType = contextProperty.PropertyType.GetGenericArguments()[0]; 

       foreach (var propertyInfo in tableType.GetProperties()) 
       { 
        if (propertyInfo.PropertyType == enumType) 
         result.Add(contextProperty); 
       } 
      } 
     } 

     return result; 
    } 

    private static bool IsSubclassOfRawGeneric(Type generic, Type toCheck) 
    { 
     while (toCheck != null && toCheck != typeof(object)) 
     { 
      var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; 
      if (generic == cur) 
      { 
       return true; 
      } 
      toCheck = toCheck.BaseType; 
     } 
     return false; 
    } 

    public class EnumSet<T> 
    { 
    } 
} 

используя код:

public partial class UserEntities : DbContext{ 
    public DbSet<User> User { get; set; } 
    public EntityHelper.EnumSet<UserType> UserType { get; set; } 

    public static void CreateDatabase(){ 
     using (var db = new UserEntities()){ 
      db.Database.CreateIfNotExists(); 
      db.Database.Initialize(true); 
      EntityHelper.Seed(db); 
     } 
    } 

} 
+0

Работает с удовольствием. Мои изменения - «Имя» в качестве второго столбца и разделение имен перечислений перед сохранением с помощью регулярных выражений - http://stackoverflow.com/a/155487/10245 –

1

Я создал пакет для него

https://www.nuget.org/packages/SSW.Data.EF.Enums/1.0.0

Использование

EnumTableGenerator.Run("your object context", "assembly that contains enums"); 

«ваш объект контекст» - это ваш EntityFramework DbContext «узел, который содержит перечисления» - сборка, которая содержит ваши перечислений

Вызов EnumTableGenerator.Run как часть вашей функции семян. Это создаст таблицы в sql-сервере для каждого Enum и заполнит его правильными данными.

+1

Вам нужно разместить дополнительную информацию о том, что это делает, и когда сделать вышеупомянутый вызов. Более конкретный пример, например, как структура DbContext должен быть структурой, местом размещения перечислений и т. Д., Также будет полезен. –

+0

Также кажется, что отсутствует лицензия и ссылка на исходный код, который может быть проблематичным для некоторых проектов. –

+0

И для бонусных очков я не могу заставить его работать в любом случае. Он работает без ошибок, но ни одна из таблиц поиска не создается. –

1

Я включил этот ответ, как я сделал некоторые дополнительные изменения от @HerrKater

Я сделал небольшое дополнение к Herr Kater's Answer (также основанному на комментарии Тима Эйбеллы). Обновление состоит в использовании метода для получения значения перечисления из атрибута DisplayName, если существует else, разделяющее значение перечисления PascalCase.

private static string GetDisplayValue(object value) 
{ 
    var fieldInfo = value.GetType().GetField(value.ToString()); 

    var descriptionAttributes = fieldInfo.GetCustomAttributes(
    typeof(DisplayAttribute), false) as DisplayAttribute[]; 

    if (descriptionAttributes == null) return string.Empty; 
    return (descriptionAttributes.Length > 0) 
    ? descriptionAttributes[0].Name 
    : System.Text.RegularExpressions.Regex.Replace(value.ToString(), "([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))", "$1 "); 
} 

Обновление господина Katers примера для вызова метода:

command = string.Format("INSERT INTO {0} VALUES({1},'{2}');", enumType.Name, (int)enumvalue, 
             GetDisplayValue(enumvalue)); 

Enum Пример

public enum PaymentMethod 
{ 
    [Display(Name = "Credit Card")] 
    CreditCard = 1, 

    [Display(Name = "Direct Debit")] 
    DirectDebit = 2 
} 
20

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

https://www.nuget.org/packages/ef-enum-to-lookup

Добавьте это в свой проект и вызовите метод Apply.

Документация на GitHub: https://github.com/timabell/ef-enum-to-lookup

+7

Pure ** EF ** - пальцы блестящие. Период. – Hallmanac

+2

Удивительно, спасибо. Жаль, что я мог бы дать +10 – Andrey

+1

Вас приветствуют :-) –

Смежные вопросы