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

发布于 2024-07-16 17:50:51 字数 389 浏览 6 评论 0原文

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

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

有这样的事吗?


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

How do I automatically create an enum and subsequently use its values in C# based on values in a database lookup table (using enterprise library data layer)?

For example, If I add a new lookup value in the database, I don't want to have to manually add the extra static enum value declaration in code - I'd like to keep the enum in sync with the database.

Is there such a thing as this?


I don't want to create a code generated static enum (as per The Code Project article Enum Code Generator - Generating enum code automatically from database look up tables) and would prefer it to be completely automatic.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

紫轩蝶泪 2024-07-23 17:50:51

我正在做这件事,但是您需要进行某种代码生成才能使其工作。

在我的解决方案中,我添加了一个项目“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");

解决方案中的我的其他项目引用了此生成的程序集。 因此,我可以在代码中使用动态枚举,并配有智能感知。

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

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

我从 < a href="http://msdn.microsoft.com/en-us/library/system.reflection.emit.enumbuilder.aspx" rel="nofollow noreferrer">这篇 msdn 文章。

I'm doing this exact thing, but you need to do some kind of code generation for this to work.

In my solution, I added a project "EnumeratedTypes". This is a console application which gets all of the values from the database and constructs the enums from them. Then it saves all of the enums to an assembly.

The enum generation code is like this:

// 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");

My other projects in the solution reference this generated assembly. As a result, I can then use the dynamic enums in code, complete with intellisense.

Then, I added a post-build event so that after this "EnumeratedTypes" project is built, it runs itself and generates the "MyEnums.dll" file.

By the way, it helps to change the build order of your project so that "EnumeratedTypes" is built first. Otherwise, once you start using your dynamically generated .dll, you won't be able to do a build if the .dll ever gets deleted. (Chicken and egg kind of problem -- your other projects in the solution need this .dll to build properly, and you can't create the .dll until you build your solution...)

I got most of the above code from this msdn article.

初心未许 2024-07-23 17:50:51

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

来自专业 C# 2008:

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

所以,我不确定您是否可以按照您想要的方式使用枚举。

Enums must be specified at compile time, you can't dynamically add enums during run-time - and why would you, there would be no use/reference to them in the code?

From Professional C# 2008:

The real power of enums in C# is that behind the scenes they are instantiated as structs derived from the base class, System.Enum . This means it is possible to call methods against them to perform some useful tasks. Note that because of the way the .NET Framework is implemented there is no performance loss associated with treating the enums syntactically as structs. In practice, once your code is compiled, enums will exist as primitive types, just like int and float .

So, I'm not sure you can use Enums the way you want to.

聊慰 2024-07-23 17:50:51

它必须是一个实际的枚举吗? 使用 Dictionary 来代替怎么样?

例如

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

Does it have to be an actual enum? How about using a Dictionary<string,int> instead?

for example

Dictionary<string, int> MyEnum = new Dictionary(){{"One", 1}, {"Two", 2}};
Console.WriteLine(MyEnum["One"]);
旧时光的容颜 2024-07-23 17:50:51

我已经使用 T4 模板完成了此操作。 将 .tt 文件放入项目中并设置 Visual Studio 以运行 T4 模板作为预构建步骤非常简单。

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

I've done this with a T4 template. It is fairly trivial to drop a .tt file into your project, and set up Visual Studio to run the T4 template as a pre-build step.

The T4 generates a .cs file, which means you can have it just query the database and build an enum in a .cs file from the result. Wired up as a pre-build task, it would re-create your enum on every build, or you can run the T4 manually as needed instead.

陪你到最终 2024-07-23 17:50:51

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

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

构造一个选择来获取所需的值:

select * from enums e inner join enum_values ev on ev.enums_id=e.id where e.id=0

构造枚举的源代码,您将得到类似的内容:(

String enumSourceCode = "enum " + enumName + "{" + enumKey1 + "=" enumValue1 + "," + enumKey2 + ... + "}";

显然这是在某种循环中构造的。)

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

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

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

Type enumType = result.CompiledAssembly.GetType(enumName);

现在你已经编译了类型并准备使用。
要获取存储在数据库中的枚举值,您可以使用:

[Enum].Parse(enumType, value);

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

Let's say you have the following in your DB:

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

Construct a select to get the values you need:

select * from enums e inner join enum_values ev on ev.enums_id=e.id where e.id=0

Construct the source code for the enum and you'll get something like:

String enumSourceCode = "enum " + enumName + "{" + enumKey1 + "=" enumValue1 + "," + enumKey2 + ... + "}";

(obviously this is constructed in a loop of some kind.)

Then comes the fun part, Compiling your enum and using it:

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

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

Type enumType = result.CompiledAssembly.GetType(enumName);

Now you have the type compiled and ready for use.
To get a enum value stored in the DB you can use:

[Enum].Parse(enumType, value);

where value can be either the integer value (0, 1, etc.) or the enum text/key (Apple, Banana, etc.)

烟花易冷人易散 2024-07-23 17:50:51

只是显示 Pandincus 的答案以及“现成的”代码和一些解释:
对于这个例子,你需要两种解决方案(我知道也可以通过一种解决方案来完成;),让高级学生展示它......

所以这是表的 DDL SQL:

USE [ocms_dev]
    GO

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

所以这是生成 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 

这里是控制台编程打印输出(记住它必须引用 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

Just showing the answer of Pandincus with "of the shelf" code and some explanation:
You need two solutions for this example ( I know it could be done via one also ; ), let the advanced students present it ...

So here is the DDL SQL for the table :

USE [ocms_dev]
    GO

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

So here is the console program producing the 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 

Here is the Console programming printing the output ( remember that it has to reference the dll ). Let the advance students present the solution for combining everything in one solution with dynamic loading and checking if there is already build 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
听你说爱我 2024-07-23 17:50:51

我们是不是从错误的方向来的?

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

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

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

Aren't we coming to this from the wrong direction?

If the data is likely to change at all during the lifetime of the deployed release then an enum is just not appropriate, and you need to use a dictionary, hash or other dynamic collection.

If you know the set of possible values is fixed for the life of the deployed release, then an enum is preferable.

If you must have something in your database that replicates the enumerated set, then why not add a deployment step to clear and repopulate the database table with the definitive set of enum values?

一片旧的回忆 2024-07-23 17:50:51

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

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

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

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

最后我可以像我想要的那样使用它:

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

我的输出将是:

Element1: foo
Is 'Element2': False

Element2: bar
Is 'Element2': True    

I always like to write my own "custom enum". Than I have one class that is a little bit more complex, but I can reuse it:

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

Now I just need to create my enum I want to use:

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

At last I can use it like I want:

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

And my output would be:

Element1: foo
Is 'Element2': False

Element2: bar
Is 'Element2': True    
自此以后,行同陌路 2024-07-23 17:50:51

你想要 System.Web.Compilation.BuildProvider

我也怀疑这样做是否明智,但也许有一个我想不出的好的用例。

您正在寻找的是构建提供程序 即 System.Web.Compilation.BuildProvider

SubSonic 可以非常有效地使用它们,您可以下载源代码并查看他们如何使用它们,您不需要像他们所做的那样复杂的东西。

希望这可以帮助。

You want System.Web.Compilation.BuildProvider

I also doubt the wisdom of doing this, but then there maybe a good use case that I can't think of.

What you're looking for are Build Providers i.e. System.Web.Compilation.BuildProvider

They're used very effectively by SubSonic, you can download the source and see how they use them, you won't need anything half as intricate as what they're doing.

Hope this helps.

悟红尘 2024-07-23 17:50:51

无论哪种方式,使用动态枚举都是不好的。 您将不得不经历“复制”数据的麻烦,以确保将来代码清晰、简单、易于维护。

如果您开始引入自动生成的库,那么您肯定会给未来的开发人员带来更多的困惑,而这些开发人员必须升级您的代码,而不是简单地将枚举编码在适当的类对象中。

给出的其他示例听起来不错且令人兴奋,但请考虑一下代码维护的开销与您从中获得的收益。 另外,这些值会经常改变吗?

Using dynamic enums is bad no matter which way. You will have to go through the trouble of "duplicating" the data to ensure clear and easy code easy to maintain in the future.

If you start introducing automatic generated libraries, you are for sure causing more confusion to future developers having to upgrade your code than simply making your enum coded within the appropriate class object.

The other examples given sound nice and exciting, but think about the overhead on code maintenance versus what you get from it. Also, are those values going to change that frequently?

忆梦 2024-07-23 17:50:51

说起来,我也厌倦了根据 Id / Name 数据库表列编写枚举,从 SSMS 中的查询中复制和粘贴内容。

下面是一个超级脏存储过程,它将表名、要用于 C# 枚举名称的列名称以及要用于 C# 枚举值的列名称作为输入。

我使用的大多数表名称 a) 以“s”结尾 b) 有一个 [TABLENAME]Id 列 c) 有一个 [TABLENAME]Name 列,因此有几个 if 语句将采用该结构,其中这种情况下,不需要列名参数。

这些例子的一些背景信息 - 这里的“Stonk”并不真正意味着“股票”,而是有点,我使用“stonk”的方式意味着“在一段时间内有一些与之相关的数字的东西”但这就是并不重要,这只是具有此 Id / Name 架构的表的示例。 它看起来像这样:

CREATE TABLE StonkTypes (
    StonkTypeId TINYINT IDENTITY(1,1) PRIMARY KEY NOT NULL,
    StonkTypeName VARCHAR(200) NOT NULL CONSTRAINT UQ_StonkTypes_StonkTypeName UNIQUE (StonkTypeName)
)

创建过程后,此语句:

EXEC CreateCSharpEnum 'StonkTypes'

选择此字符串:

public enum StonkTypes { Stonk = 1, Bond = 2, Index = 3, Fund = 4, Commodity = 5, 
PutCallRatio = 6, }

我可以将其复制并粘贴到 C# 文件中。

我有一个 Stonks 表,它有 StonkId 和 StonkName 列,所以这个执行:

EXEC CreateCSharpEnum 'Stonks'

吐出:

public enum Stonks { SP500 = 1, DowJonesInduscialAverage = 2, ..... }

但是对于那个枚举我想要使用“Symbol”列作为枚举名称值,这样:

EXEC CreateCSharpEnum 'Stonks', 'Symbol'

执行此操作并呈现:

public enum Stonks { SPY = 1, DIA = 2, ..... }

言归正传,这就是这肮脏的疯狂。 是的,很脏,但我对自己有点满意 - SQL 代码构造 SQL 代码,SQL 代码构造 C# 代码。 涉及到几个层。


CREATE OR ALTER PROCEDURE CreateCSharpEnum
@TableName VARCHAR(MAX),
@EnumNameColumnName VARCHAR(MAX) = NULL,
@EnumValueColumnName VARCHAR(MAX) = NULL
AS

DECLARE @LastCharOfTableName VARCHAR(1)
SELECT @LastCharOfTableName = RIGHT(@TableName, 1)

PRINT 'Last char = [' + @LastCharOfTableName + ']'

DECLARE @TableNameWithoutS VARCHAR(MAX)
IF UPPER(@LastCharOfTableName) = 'S'
    SET @TableNameWithoutS = LEFT(@TableName, LEN(@TableName) - 1)
ELSE
    SET @TableNameWithoutS = @TableName

PRINT 'Table name without trailing s = [' + @TableNameWithoutS + ']'

IF @EnumNameColumnName IS NULL
    BEGIN
        SET @EnumNameColumnName = @TableNameWithoutS + 'Name'
    END

PRINT 'name col name = [' + @EnumNameColumnName + ']'

IF @EnumValueColumnName IS NULL
    SET @EnumValueColumnName = @TableNameWithoutS + 'Id'

PRINT 'value col name = [' + @EnumValueColumnName + ']'

-- replace spaces and punctuation
SET @EnumNameColumnName  = 'REPLACE(' + @EnumNameColumnName + ', '' '', '''')'
SET @EnumNameColumnName  = 'REPLACE(' + @EnumNameColumnName + ', ''&'', '''')'
SET @EnumNameColumnName  = 'REPLACE(' + @EnumNameColumnName + ', ''.'', '''')'
SET @EnumNameColumnName  = 'REPLACE(' + @EnumNameColumnName + ', ''('', '''')'
SET @EnumNameColumnName  = 'REPLACE(' + @EnumNameColumnName + ', '')'', '''')'

PRINT 'name col name with replace sql = [' + @EnumNameColumnName + ']'

DECLARE @SqlStr VARCHAR(MAX) = 'SELECT ' + @EnumNameColumnName  
+ ' + '' = ''' 
+ ' + LTRIM(RTRIM(STR(' + @EnumValueColumnName + '))) + '','' FROM ' + @TableName + ' ORDER BY ' + @EnumValueColumnName

PRINT 'sql that gets rows for enum body = [' + @SqlStr + ']'

CREATE TABLE #EnumRowsTemp (s VARCHAR(MAX))

INSERT 
INTO #EnumRowsTemp
EXEC(@SqlStr)

--SELECT * FROM #EnumRowsTemp

DECLARE @csharpenumbody VARCHAR(MAX) 
SELECT @csharpenumbody = COALESCE(@csharpenumbody + ' ', '') + s FROM #EnumRowsTemp

--PRINT @csharpenumbody

DECLARE @csharpenum VARCHAR(MAX) = 'public enum ' + @TableName + ' { ' + @csharpenumbody + ' }'

PRINT @csharpenum

SELECT @csharpenum

DROP TABLE #EnumRowsTemp

请保持批判性。 我不明白的一件奇怪的事情是,为什么我必须创建并删除这个 #EnumRowsTemp 表,而不仅仅是“SELECT INTO #EnumRowsTemp”来动态创建临时表? 我不知道答案,我尝试过,但没有成功。 这可能是这段代码中最少的问题......

尽管它可能很脏......我希望这可以节省你们中的一些呆子的时间。

Word up, I as well got tired of writing out enumerations based on Id / Name db table columns, copying and pasting stuff from queries in SSMS.

Below is a super dirty stored procedure that takes as input a table name, the column name you want to use for the c# enumeration name, and the column name that you want to use for the c# enumeration value.

Most of theses table names I work with a) end with "s" b) have a [TABLENAME]Id column and c) have a [TABLENAME]Name column, so there are a couple if statements that will assume that structure, in which case, the column name parameters are not required.

A little context for these examples - "Stonk" here doesn't really mean "stock" but kinda, the way I'm using "stonk" it means "a thing that has some numbers associated to it for a time period" But that's not important, it's just an example of table with this Id / Name schema. It looks like this:

CREATE TABLE StonkTypes (
    StonkTypeId TINYINT IDENTITY(1,1) PRIMARY KEY NOT NULL,
    StonkTypeName VARCHAR(200) NOT NULL CONSTRAINT UQ_StonkTypes_StonkTypeName UNIQUE (StonkTypeName)
)

After I create the proc, this statement:

EXEC CreateCSharpEnum 'StonkTypes'

Selects this string:

public enum StonkTypes { Stonk = 1, Bond = 2, Index = 3, Fund = 4, Commodity = 5, 
PutCallRatio = 6, }

Which I can copy and paste into a C# file.

I have a Stonks table and it has StonkId and StonkName columns so this exec:

EXEC CreateCSharpEnum 'Stonks'

Spits out:

public enum Stonks { SP500 = 1, DowJonesIndustrialAverage = 2, ..... }

But for that enum I want to use the "Symbol" column for the enum name values so this:

EXEC CreateCSharpEnum 'Stonks', 'Symbol'

Does the trick and renders:

public enum Stonks { SPY = 1, DIA = 2, ..... }

Without further ado, here is this dirty piece of craziness. Yeah, very dirty, but I'm kind of pleased with myself - it's SQL code that constructs SQL code that constructs C# code. Couple layers involved.


CREATE OR ALTER PROCEDURE CreateCSharpEnum
@TableName VARCHAR(MAX),
@EnumNameColumnName VARCHAR(MAX) = NULL,
@EnumValueColumnName VARCHAR(MAX) = NULL
AS

DECLARE @LastCharOfTableName VARCHAR(1)
SELECT @LastCharOfTableName = RIGHT(@TableName, 1)

PRINT 'Last char = [' + @LastCharOfTableName + ']'

DECLARE @TableNameWithoutS VARCHAR(MAX)
IF UPPER(@LastCharOfTableName) = 'S'
    SET @TableNameWithoutS = LEFT(@TableName, LEN(@TableName) - 1)
ELSE
    SET @TableNameWithoutS = @TableName

PRINT 'Table name without trailing s = [' + @TableNameWithoutS + ']'

IF @EnumNameColumnName IS NULL
    BEGIN
        SET @EnumNameColumnName = @TableNameWithoutS + 'Name'
    END

PRINT 'name col name = [' + @EnumNameColumnName + ']'

IF @EnumValueColumnName IS NULL
    SET @EnumValueColumnName = @TableNameWithoutS + 'Id'

PRINT 'value col name = [' + @EnumValueColumnName + ']'

-- replace spaces and punctuation
SET @EnumNameColumnName  = 'REPLACE(' + @EnumNameColumnName + ', '' '', '''')'
SET @EnumNameColumnName  = 'REPLACE(' + @EnumNameColumnName + ', ''&'', '''')'
SET @EnumNameColumnName  = 'REPLACE(' + @EnumNameColumnName + ', ''.'', '''')'
SET @EnumNameColumnName  = 'REPLACE(' + @EnumNameColumnName + ', ''('', '''')'
SET @EnumNameColumnName  = 'REPLACE(' + @EnumNameColumnName + ', '')'', '''')'

PRINT 'name col name with replace sql = [' + @EnumNameColumnName + ']'

DECLARE @SqlStr VARCHAR(MAX) = 'SELECT ' + @EnumNameColumnName  
+ ' + '' = ''' 
+ ' + LTRIM(RTRIM(STR(' + @EnumValueColumnName + '))) + '','' FROM ' + @TableName + ' ORDER BY ' + @EnumValueColumnName

PRINT 'sql that gets rows for enum body = [' + @SqlStr + ']'

CREATE TABLE #EnumRowsTemp (s VARCHAR(MAX))

INSERT 
INTO #EnumRowsTemp
EXEC(@SqlStr)

--SELECT * FROM #EnumRowsTemp

DECLARE @csharpenumbody VARCHAR(MAX) 
SELECT @csharpenumbody = COALESCE(@csharpenumbody + ' ', '') + s FROM #EnumRowsTemp

--PRINT @csharpenumbody

DECLARE @csharpenum VARCHAR(MAX) = 'public enum ' + @TableName + ' { ' + @csharpenumbody + ' }'

PRINT @csharpenum

SELECT @csharpenum

DROP TABLE #EnumRowsTemp

Please, be critical. One funky thing I didn't understand, how come I have to create and drop this #EnumRowsTemp table and not just "SELECT INTO #EnumRowsTemp" to create the temp table on the fly? I don't know the answer, I tried that and it didn't work. That's probably the least of the problems of this code...

As dirty as it may be... I hope this saves some of you fellow dorks a little bit of time.

你的呼吸 2024-07-23 17:50:51

我不认为有一个好的方法可以做你想做的事。 如果你仔细想想,我认为这并不是你真正想要的。

如果您有一个动态枚举,这也意味着您在引用它时必须为其提供动态值。 也许通过很多魔法,你可以实现某种 IntelliSense 来解决这个问题,并且在 DLL 文件中为您生成一个枚举。 但请考虑一下这将花费多少工作量、访问数据库来获取 IntelliSense 信息的效率有多低以及对生成的 DLL 文件进行版本控制的噩梦。

如果您确实不想手动添加枚举值(无论如何您都必须将它们添加到数据库中),请使用代码生成工具,例如 T4 模板。 右键单击+运行,您将在代码中静态定义枚举,并且您将获得使用枚举的所有好处。

I don't think there is a good way of doing what you want. And if you think about it I don't think this is what you really want.

If you would have a dynamic enum, it also means you have to feed it with a dynamic value when you reference it. Maybe with a lot of magic you could achieve some sort of IntelliSense that would take care of this and generate an enum for you in a DLL file. But consider the amount of work it would take, how uneffective it would be to access the database to fetch IntelliSense information as well as the nightmare of version controlling the generated DLL file.

If you really don't want to manually add the enum values (you'll have to add them to the database anyway) use a code generation tool instead, for example T4 templates. Right click+run and you got your enum statically defined in code and you get all the benefits of using enums.

森罗 2024-07-23 17:50:51

保留枚举并同时创建动态值列表的一种方法是将当前拥有的枚举与动态创建的字典一起使用。

由于大多数枚举都是在定义要使用的上下文中使用的,并且动态进程将支持“动态枚举”,因此您可以区分这两者。

第一步是创建一个表/集合,其中包含 ID 和动态条目的参考。 在表中,您将自动增量远大于最大的枚举值。

现在是动态枚举的部分,我假设您将使用枚举来创建一组应用一组规则的条件,其中一些是动态生成的。

Get integer from database
If Integer is in Enum -> create Enum -> then run Enum parts
If Integer is not a Enum -> create Dictionary from Table -> then run Dictionary parts.

One way to keep the Enums and to create a Dynamic list of values at the same time is to use the Enums that you currently have with a Dynamically created Dictionary.

Since most Enums are used in the context that they are defined to be used, and the "dynamic enums" will be supported by dynamic processes, you can distinguish the 2.

The first step is to create a table/collection that houses the IDs and References for the Dynamic Entries. In the table you will autoincrement much larger than your largest Enum value.

Now comes the part for your dynamic Enums, I am assuming that you will be using the Enums to create a set of conditions that apply a set of rules, some are dynamically generated.

Get integer from database
If Integer is in Enum -> create Enum -> then run Enum parts
If Integer is not a Enum -> create Dictionary from Table -> then run Dictionary parts.
断爱 2024-07-23 17:50:51

枚举构建器类

public class XEnum
{
    private EnumBuilder enumBuilder;
    private int index;
    private AssemblyBuilder _ab;
    private AssemblyName _name;
    public XEnum(string enumname)
    {
        AppDomain currentDomain = AppDomain.CurrentDomain;
        _name = new AssemblyName("MyAssembly");
        _ab = currentDomain.DefineDynamicAssembly(
            _name, AssemblyBuilderAccess.RunAndSave);

        ModuleBuilder mb = _ab.DefineDynamicModule("MyModule");

        enumBuilder = mb.DefineEnum(enumname, TypeAttributes.Public, typeof(int));


    }
    /// <summary>
    /// adding one string to enum
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    public FieldBuilder add(string s)
    {
        FieldBuilder f = enumBuilder.DefineLiteral(s, index);
        index++;
        return f;
    }
    /// <summary>
    /// adding array to enum
    /// </summary>
    /// <param name="s"></param>
    public void addRange(string[] s)
    {
        for (int i = 0; i < s.Length; i++)
        {
            enumBuilder.DefineLiteral(s[i], i);
        }
    }
    /// <summary>
    /// getting index 0
    /// </summary>
    /// <returns></returns>
    public object getEnum()
    {
        Type finished = enumBuilder.CreateType();
        _ab.Save(_name.Name + ".dll");
        Object o1 = Enum.Parse(finished, "0");
        return o1;
    }
    /// <summary>
    /// getting with index
    /// </summary>
    /// <param name="i"></param>
    /// <returns></returns>
    public object getEnum(int i)
    {
        Type finished = enumBuilder.CreateType();
        _ab.Save(_name.Name + ".dll");
        Object o1 = Enum.Parse(finished, i.ToString());
        return o1;
    }
}

创建一个对象

string[] types = { "String", "Boolean", "Int32", "Enum", "Point", "Thickness", "long", "float" };
XEnum xe = new XEnum("Enum");
        xe.addRange(types);
        return xe.getEnum();

enum builder class

public class XEnum
{
    private EnumBuilder enumBuilder;
    private int index;
    private AssemblyBuilder _ab;
    private AssemblyName _name;
    public XEnum(string enumname)
    {
        AppDomain currentDomain = AppDomain.CurrentDomain;
        _name = new AssemblyName("MyAssembly");
        _ab = currentDomain.DefineDynamicAssembly(
            _name, AssemblyBuilderAccess.RunAndSave);

        ModuleBuilder mb = _ab.DefineDynamicModule("MyModule");

        enumBuilder = mb.DefineEnum(enumname, TypeAttributes.Public, typeof(int));


    }
    /// <summary>
    /// adding one string to enum
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    public FieldBuilder add(string s)
    {
        FieldBuilder f = enumBuilder.DefineLiteral(s, index);
        index++;
        return f;
    }
    /// <summary>
    /// adding array to enum
    /// </summary>
    /// <param name="s"></param>
    public void addRange(string[] s)
    {
        for (int i = 0; i < s.Length; i++)
        {
            enumBuilder.DefineLiteral(s[i], i);
        }
    }
    /// <summary>
    /// getting index 0
    /// </summary>
    /// <returns></returns>
    public object getEnum()
    {
        Type finished = enumBuilder.CreateType();
        _ab.Save(_name.Name + ".dll");
        Object o1 = Enum.Parse(finished, "0");
        return o1;
    }
    /// <summary>
    /// getting with index
    /// </summary>
    /// <param name="i"></param>
    /// <returns></returns>
    public object getEnum(int i)
    {
        Type finished = enumBuilder.CreateType();
        _ab.Save(_name.Name + ".dll");
        Object o1 = Enum.Parse(finished, i.ToString());
        return o1;
    }
}

create an object

string[] types = { "String", "Boolean", "Int32", "Enum", "Point", "Thickness", "long", "float" };
XEnum xe = new XEnum("Enum");
        xe.addRange(types);
        return xe.getEnum();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文