【问题标题】:EF5 Code First Enums and Lookup TablesEF5 代码优先枚举和查找表
【发布时间】:2012-06-25 10:18:19
【问题描述】:

我想定义一个供 EF5 使用的枚举,以及一​​个相应的查找表。我知道 EF5 现在支持枚举,但开箱即用,它似乎只在对象级别支持此功能,并且默认情况下不为这些查找值添加表。

例如,我有一个用户实体:

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)
)

这可能吗?

【问题讨论】:

标签: entity-framework ef-code-first entity-framework-5


【解决方案1】:

这是我之前制作的一个 nuget 包,它生成查找表并应用外键,并使查找表行与枚举保持同步:

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

将其添加到您的项目并调用 Apply 方法。

github上的文档:https://github.com/timabell/ef-enum-to-lookup

【讨论】:

    【解决方案2】:

    这是不可能的。 EF 支持与 .NET 相同级别的枚举,因此枚举值仅命名为整数 => 类中的枚举属性始终是数据库中的整数列。如果您还想拥有表,则需要在您自己的数据库初始化程序中手动创建它以及 User 中的外键并用枚举值填充它。

    我做了一些proposal on user voice 以允许更复杂的映射。如果您觉得它有用,您可以为该提案投票。

    【讨论】:

    • @Ladislav Mrnka,EF 的 Enum 支持不仅限于整数。 “枚举可以具有以下基础类型:Byte、Int16、Int32、Int64 或 SByte。” msdn.microsoft.com/en-us/data/hh859576
    • 我看到您的proposal 已被接受。来自 EF 项目经理,2018 年 3 月 4 日:“该功能是 EF Core 2.1 的一部分,目前是预览中。如果您发现任何问题,请在我们的问题跟踪器中创建问题。” 也许您可以更新您的答案?
    【解决方案3】:

    我写了一个小助手类,它为 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);
            }
        }
    
    }
    

    【讨论】:

    【解决方案4】:

    我已经为它创建了一个包

    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 server 中为每个 Enum 创建表,并用正确的数据填充它。

    【讨论】:

    • 您需要发布有关此功能的更多信息,以及何时进行上述调用。一个更具体的示例,例如 DbContext 的结构、枚举的放置位置等,也会有所帮助。
    • 它似乎还缺少许可证和指向源代码的链接,这可能对某些项目造成问题。
    • 对于奖励积分,我无论如何也无法使用它。它运行没有错误,但没有创建任何查找表。
    • @TimAbell 不幸的是它不在 github 上(它托管在 Visual Studio Online 上),但如果你愿意,我可以将源代码通过电子邮件发送给你,如果你可以给我你的电子邮件。适合我
    • 谢谢。如果您希望共享源代码,我建议将其与许可证文件一起推送到 github。就我个人而言,我使用了其他答案的来源,效果很好。谢谢你的提议。
    【解决方案5】:

    我已经包含了这个答案,因为我对@HerrKater 做了一些额外的更改

    我对@9​​87654321@ 做了一个小补充(也是基于 Tim Abell 的评论)。更新是使用一种方法从 DisplayName 属性中获取枚举值(如果存在),否则拆分 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 ");
     }
    

    更新 Herr Katers 示例以调用该方法:

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

    枚举示例

    public enum PaymentMethod
    {
        [Display(Name = "Credit Card")]
        CreditCard = 1,
    
        [Display(Name = "Direct Debit")]
        DirectDebit = 2
    }
    

    【讨论】:

      【解决方案6】:

      你必须自定义你的生成工作流

      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
      

      【讨论】:

      • 在 70-516 认证中你有关于这个主题的答案
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-14
      • 1970-01-01
      • 2012-11-14
      相关资源
      最近更新 更多