根据数据库查找表中的值自动创建枚举?

bil*_*tom 107 c# database enums dynamic

如何根据数据库查找表中的值(使用企业库数据层)自动创建枚举并随后在C#中使用其值?

例如,如果我在数据库中添加一个新的查找值,我不想在代码中手动添加额外的静态枚举值声明 - 我想让枚举与数据库保持同步.

有这样的事吗?


我不想创建代码生成的静态枚举(根据代码项目文章枚举代​​码生成器 - 从数据库查找表自动生成枚举代码),并希望它是完全自动的.

Pan*_*cus 95

我正在做这件事,但你需要做一些代码生成才能实现.

在我的解决方案中,我添加了一个项目"EnumeratedTypes".这是一个控制台应用程序,它从数据库中获取所有值并从中构造枚举.然后它将所有枚举保存到程序集中.

枚举生成代码是这样的:

// Get the current application domain for the current thread
AppDomain currentDomain = AppDomain.CurrentDomain;

// Create a dynamic assembly in the current application domain,
// and allow it to be executed and saved to disk.
AssemblyName name = new AssemblyName("MyEnums");
AssemblyBuilder assemblyBuilder = currentDomain.DefineDynamicAssembly(name,
                                      AssemblyBuilderAccess.RunAndSave);

// Define a dynamic module in "MyEnums" assembly.
// For a single-module assembly, the module has the same name as the assembly.
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(name.Name,
                                  name.Name + ".dll");

// Define a public enumeration with the name "MyEnum" and an underlying type of Integer.
EnumBuilder myEnum = moduleBuilder.DefineEnum("EnumeratedTypes.MyEnum",
                         TypeAttributes.Public, typeof(int));

// Get data from database
MyDataAdapter someAdapter = new MyDataAdapter();
MyDataSet.MyDataTable myData = myDataAdapter.GetMyData();

foreach (MyDataSet.MyDataRow row in myData.Rows)
{
    myEnum.DefineLiteral(row.Name, row.Key);
}

// Create the enum
myEnum.CreateType();

// Finally, save the assembly
assemblyBuilder.Save(name.Name + ".dll");
Run Code Online (Sandbox Code Playgroud)

我在解决方案中的其他项目引用此生成的程序集.结果,我可以在代码中使用动态枚举,并使用intellisense.

然后,我添加了一个构建后事件,以便在构建此"EnumeratedTypes"项目之后,它会自行运行并生成"MyEnums.dll"文件.

顺便说一句,它有助于更​​改项目的构建顺序,以便首先构建"EnumeratedTypes".否则,一旦开始使用动态生成的.dll,如果.dll被删除,您将无法进行构建.(鸡和鸡蛋的问题 - 解决方案中的其他项目需要这个.dll才能正确构建,并且在构建解决方案之前无法创建.dll ...)

我从这篇msdn文章中得到了上面的大部分代码.

希望这可以帮助!

  • 对于那些不知道如何在post-build上运行生成的可执行文件的人:1)右键单击项目2)单击属性3)单击Build Events 4)在"Post-build event command lines"文本框中输入$(TARGETPATH) (7认同)

Mar*_*s L 47

枚举必须在编译时指定,你不能在运行时动态添加枚举 - 为什么你会在代码中没有使用/引用它们?

来自Professional C#2008:

C#中枚举的真正强大之处在于它们在幕后实例化为从基类System.Enum派生的结构.这意味着可以调用针对它们的方法来执行一些有用的任务.请注意,由于.NET Framework的实现方式,在语法上将枚举视为结构时,不存在性能损失.在实践中,一旦编译了代码,枚举将作为基本类型存在,就像int和float一样.

所以,我不确定你能以你想要的方式使用Enums.

  • 海报和18名赞成有点错过了他的观点.听起来他想要*生成*枚举,而不是运行时动态枚举. (13认同)
  • @Oliver如果你想论证语义,是的,你是对的.但我同意Graphain的评论 - 我相信OP正在寻找_generated_ enums.他希望枚举值来自数据库而不必硬编码. (2认同)

Aut*_*act 15

它必须是一个真正的枚举?用一个Dictionary<string,int>代替怎么样?

例如

Dictionary<string, int> MyEnum = new Dictionary(){{"One", 1}, {"Two", 2}};
Console.WriteLine(MyEnum["One"]);
Run Code Online (Sandbox Code Playgroud)

  • 我不会这样做.您失去了编译时间检查并且容易出现输入错误.枚举的所有好处都消失了.你可以引入字符串常量,但是你回到了你开始的地方. (9认同)
  • +1.使用字典或HashSet最接近动态枚举.完全动态意味着它在运行时发生,因此必须在运行时进行错误检查. (4认同)
  • 这是一个很好的答案,我更愿意这样做,而不是在启动时编译 dll (2认同)

Cod*_*ike 12

我用T4模板完成了这个.将.tt文件放入项目并设置Visual Studio以将T4模板作为预构建步骤运行是相当简单的.

T4生成.cs文件,这意味着您只需查询数据库并从结果中在.cs文件中构建枚举.作为预构建任务接线,它会在每次构建时重新创建枚举,或者您可以根据需要手动运行T4.


San*_*nen 11

假设您的数据库中包含以下内容:

table enums
-----------------
| id | name     |
-----------------
| 0  | MyEnum   |
| 1  | YourEnum |
-----------------

table enum_values
----------------------------------
| id | enums_id | value | key    |
----------------------------------
| 0  | 0        | 0     | Apple  |
| 1  | 0        | 1     | Banana |
| 2  | 0        | 2     | Pear   |
| 3  | 0        | 3     | Cherry |
| 4  | 1        | 0     | Red    |
| 5  | 1        | 1     | Green  |
| 6  | 1        | 2     | Yellow |
----------------------------------
Run Code Online (Sandbox Code Playgroud)

构造一个select以获取所需的值:

select * from enums e inner join enum_values ev on ev.enums_id=e.id where e.id=0
Run Code Online (Sandbox Code Playgroud)

构造枚举的源代码,你会得到类似的东西:

String enumSourceCode = "enum " + enumName + "{" + enumKey1 + "=" enumValue1 + "," + enumKey2 + ... + "}";
Run Code Online (Sandbox Code Playgroud)

(显然这是在某种循环中构建的.)

然后是有趣的部分,编译你的枚举并使用它:

CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters cs = new CompilerParameters();
cp.GenerateInMemory = True;

CompilerResult result = provider.CompileAssemblyFromSource(cp, enumSourceCode);

Type enumType = result.CompiledAssembly.GetType(enumName);
Run Code Online (Sandbox Code Playgroud)

现在您已经编译并准备好使用了.
要获取存储在DB中的枚举值,您可以使用:

[Enum].Parse(enumType, value);
Run Code Online (Sandbox Code Playgroud)

其中value可以是整数值(0,1,等)或枚举文本/键(Apple,Banana等)

  • 这会以什么方式实际帮助?没有类型安全,也没有智能感知.基本上它只是一种使用常量的更复杂的方式,因为他无论如何都必须提供值. (4认同)
  • 萨尼 - 完美!这正是我所需要的.对于那些质疑这样的原因的人,我使用的是供应商库,需要将属性设置为枚举的名称.枚举限制同一对象的不同属性的有效值范围.在我的情况下,我正在加载元数据,包括数据库中的有效值范围; 不,供应商代码不支持将任何类型的集合传递给属性.谢谢 (2认同)

Yor*_*iev 10

只是用"货架"代码和一些解释来展示Pandincus 的 答案:你需要两个解决方案用于这个例子(我知道它也可以通过一个来完成;),让高级学生介绍它......

所以这是表的DDL SQL:

USE [ocms_dev]
    GO

CREATE TABLE [dbo].[Role](
    [RoleId] [int] IDENTITY(1,1) NOT NULL,
    [RoleName] [varchar](50) NULL
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)

所以这是生成dll的控制台程序:

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.Data.Common;
using System.Data;
using System.Data.SqlClient;

namespace DynamicEnums
{
    class EnumCreator
    {
        // after running for first time rename this method to Main1
        static void Main ()
        {
            string strAssemblyName = "MyEnums";
            bool flagFileExists = System.IO.File.Exists (
                   AppDomain.CurrentDomain.SetupInformation.ApplicationBase + 
                   strAssemblyName + ".dll"
            );

            // Get the current application domain for the current thread
            AppDomain currentDomain = AppDomain.CurrentDomain;

            // Create a dynamic assembly in the current application domain,
            // and allow it to be executed and saved to disk.
            AssemblyName name = new AssemblyName ( strAssemblyName );
            AssemblyBuilder assemblyBuilder = 
                    currentDomain.DefineDynamicAssembly ( name,
                            AssemblyBuilderAccess.RunAndSave );

            // Define a dynamic module in "MyEnums" assembly.
            // For a single-module assembly, the module has the same name as
            // the assembly.
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule (
                    name.Name, name.Name + ".dll" );

            // Define a public enumeration with the name "MyEnum" and
            // an underlying type of Integer.
            EnumBuilder myEnum = moduleBuilder.DefineEnum (
                    "EnumeratedTypes.MyEnum",
                    TypeAttributes.Public,
                    typeof ( int )
            );

            #region GetTheDataFromTheDatabase
            DataTable tableData = new DataTable ( "enumSourceDataTable" );

            string connectionString = "Integrated Security=SSPI;Persist " +
                    "Security Info=False;Initial Catalog=ocms_dev;Data " +
                    "Source=ysg";

            using (SqlConnection connection = 
                    new SqlConnection ( connectionString ))
            {

                SqlCommand command = connection.CreateCommand ();
                command.CommandText = string.Format ( "SELECT [RoleId], " + 
                        "[RoleName] FROM [ocms_dev].[dbo].[Role]" );

                Console.WriteLine ( "command.CommandText is " + 
                        command.CommandText );

                connection.Open ();
                tableData.Load ( command.ExecuteReader ( 
                        CommandBehavior.CloseConnection
                ) );
            } //eof using

            foreach (DataRow dr in tableData.Rows)
            {
                myEnum.DefineLiteral ( dr[1].ToString (),
                        Convert.ToInt32 ( dr[0].ToString () ) );
            }
            #endregion GetTheDataFromTheDatabase

            // Create the enum
            myEnum.CreateType ();

            // Finally, save the assembly
            assemblyBuilder.Save ( name.Name + ".dll" );
        } //eof Main 
    } //eof Program
} //eof namespace 
Run Code Online (Sandbox Code Playgroud)

这是Console编程打印输出(记住它必须引用dll).让高级学生提出解决方案,将一个解决方案中的所有内容与动态加载相结合,并检查是否已经构建了dll.

// add the reference to the newly generated dll
use MyEnums ; 

class Program
{
    static void Main ()
    {
        Array values = Enum.GetValues ( typeof ( EnumeratedTypes.MyEnum ) );

        foreach (EnumeratedTypes.MyEnum val in values)
        {
            Console.WriteLine ( String.Format ( "{0}: {1}",
                    Enum.GetName ( typeof ( EnumeratedTypes.MyEnum ), val ),
                    val ) );
        }

        Console.WriteLine ( "Hit enter to exit " );
        Console.ReadLine ();
    } //eof Main 
} //eof Program
Run Code Online (Sandbox Code Playgroud)

  • 我想这是一个错误,而不是 ; 一世) (2认同)

Bri*_*owe 6

我们不是从错误的方向来到这里吗?

如果数据在部署版本的生命周期内可能会发生变化,那么枚举就是不合适的,您需要使用字典、哈希或其他动态集合。

如果您知道这组可能值在已部署版本的生命周期内是固定的,则最好使用枚举。

如果您的数据库中必须有一些东西来复制枚举集,那么为什么不添加一个部署步骤来清除并使用一组确定的枚举值重新填充数据库表呢?

  • 是的,也不是,是的,因为你是对的,整个要点是枚举是静态的。您可以避免输入错误并了解可用的内容。有了字典和数据库 - 可以是任何东西。但有时你想要两棵树的果实,而只允许你从一棵树上摘果子。 (2认同)

Tra*_*ein 5

我总是喜欢编写自己的“自定义枚举”。比我有一个稍微复杂一点的类,但我可以重用它:

public abstract class CustomEnum
{
    private readonly string _name;
    private readonly object _id;

    protected CustomEnum( string name, object id )
    {
        _name = name;
        _id = id;
    }

    public string Name
    {
        get { return _name; }
    }

    public object Id
    {
        get { return _id; }
    }

    public override string ToString()
    {
        return _name;
    }
}

public abstract class CustomEnum<TEnumType, TIdType> : CustomEnum
    where TEnumType : CustomEnum<TEnumType, TIdType>
{
    protected CustomEnum( string name, TIdType id )
        : base( name, id )
    { }

    public new TIdType Id
    {
        get { return (TIdType)base.Id; }
    }

    public static TEnumType FromName( string name )
    {
        try
        {
            return FromDelegate( entry => entry.Name.Equals( name ) );
        }
        catch (ArgumentException ae)
        {
            throw new ArgumentException( "Illegal name for custom enum '" + typeof( TEnumType ).Name + "'", ae );
        }
    }

    public static TEnumType FromId( TIdType id )
    {
        try
        {
            return FromDelegate( entry => entry.Id.Equals( id ) );
        }
        catch (ArgumentException ae)
        {
            throw new ArgumentException( "Illegal id for custom enum '" + typeof( TEnumType ).Name + "'", ae );
        }
    }

    public static IEnumerable<TEnumType> GetAll()
    {
        var elements = new Collection<TEnumType>();
        var infoArray = typeof( TEnumType ).GetFields( BindingFlags.Public | BindingFlags.Static );

        foreach (var info in infoArray)
        {
            var type = info.GetValue( null ) as TEnumType;
            elements.Add( type );
        }

        return elements;
    }

    protected static TEnumType FromDelegate( Predicate<TEnumType> predicate )
    {
        if(predicate == null)
            throw new ArgumentNullException( "predicate" );

        foreach (var entry in GetAll())
        {
            if (predicate( entry ))
                return entry;
        }

        throw new ArgumentException( "Element not found while using predicate" );
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我只需要创建我想使用的枚举:

 public sealed class SampleEnum : CustomEnum<SampleEnum, int>
    {
        public static readonly SampleEnum Element1 = new SampleEnum( "Element1", 1, "foo" );
        public static readonly SampleEnum Element2 = new SampleEnum( "Element2", 2, "bar" );

        private SampleEnum( string name, int id, string additionalText )
            : base( name, id )
        {
            AdditionalText = additionalText;
        }

        public string AdditionalText { get; private set; }
    }
Run Code Online (Sandbox Code Playgroud)

最后我可以按照我想要的方式使用它:

 static void Main( string[] args )
        {
            foreach (var element in SampleEnum.GetAll())
            {
                Console.WriteLine( "{0}: {1}", element, element.AdditionalText );
                Console.WriteLine( "Is 'Element2': {0}", element == SampleEnum.Element2 );
                Console.WriteLine();
            }

            Console.ReadKey();
        }
Run Code Online (Sandbox Code Playgroud)

我的输出将是:

Element1: foo
Is 'Element2': False

Element2: bar
Is 'Element2': True    
Run Code Online (Sandbox Code Playgroud)