在C#中dynamic枚举

如何在C#中基于数据库查找表中的值(使用企业库数据层)创builddynamic枚举(并随后使用枚举选项)?

例如,如果我在数据库中添加一个新的查找值,我不想在代码中添加额外的静态枚举值声明。

有这样的事情吗? 我不想创build一个代码生成静态枚举( 根据代码项目文章枚举代​​码生成器 – 从数据库查找表自动生成枚举代码 ),并希望它是完全dynamic的。

我正在做这件事情,但你需要做一些代码生成这个工作。

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

枚举生成代码是这样的:

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

我在解决scheme中的其他项目引用此生成的程序集。 因此,我可以在代码中使用dynamic枚举,并使用intellisense。

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

顺便说一下,它有助于更​​改项目的构build顺序 ,以便首先构build“EnumeratedTypes”。 否则,一旦开始使用dynamic生成的.dll,如果.dll被删除,则无法进行构build。 (鸡和鸡蛋类的问题 – 您的解决scheme中的其他项目需要这个.dll来正确构build,并且您不能创build.dll,直到您构build您的解决scheme…)

我从这个msdn文章中获得了大部分上述代码。

希望这可以帮助!

你知道枚举必须在编译时指定吗? 你不能在运行时dynamic添加枚举 – 为什么你会在代码中没有使用/引用它们?

从专业C#2008:

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

所以,我不确定你可以用你想要的方式使用枚举。

它是否必须是一个实际的枚举? 如何使用Dictionary<string,int>来代替?

例如

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

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

所以这里是表的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)。 让先进的学生提出解决scheme,将一个解决scheme中的所有内容与dynamic加载相结合,并检查是否已经有构buildDLL。

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

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

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

构build一个select来获取您需要的值:

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

构build枚举的源代码,你会得到像这样的东西:

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

(显然这是在某种forms的循环中构build的。)

然后来了有趣的部分,编译你的枚举和使用它:

 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等)或枚举文本/键(苹果,香蕉等)

我用T4模板做了这个。 将一个.tt文件放到您的项目中是相当简单的,并且设置Visual Studio来运行T4模板作为预生成步骤。

T4生成一个.cs文件,这意味着你可以让它只是查询数据库,并从结果中build立一个.cs文件的枚举。 作为一个预先构build的任务,它将在每个构build中重新创build枚举,或者可以根据需要手动运行T4。

你想要System.Web.Compilation.BuildProvider

我也怀疑这样做的智慧,但是可能有一个我想不出来的好用例。

你正在寻找的是build立提供者,即System.Web.Compilation.BuildProvider

他们被SubSonic 非常有效地使用,你可以下载源代码,看看他们如何使用它们,你不需要任何一半的东西,他们正在做的事情。

希望这可以帮助。

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

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

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

  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 

你可以使用CodeSmith来产生这样的东西:

http://www.csharping.com/PermaLink,guid,cef1b637-7d37-4691-8e49-138cbf1d51e9.aspx

我不认为有一个好的方法来做你想做的事情。 如果你想一想,我不认为这是你真正想要的。

如果你有一个dynamic的枚举,这也意味着当你引用它时,你必须用一个dynamic值来提供它。 也许有很多魔法,你可以实现一些智能感知 ,将照顾这一点,并在DLL文件中生成一个枚举。 但是考虑一下要花费的工作量,访问数据库来获取IntelliSense信息以及控制生成的DLL文件的版本的噩梦是多么的无效。

如果你真的不想手动添加枚举值(你将不得不将它们添加到数据库),而是使用代码生成工具,例如T4模板。 右键单击+运行,你得到了你的枚举在代码中静态定义,并获得使用枚举的所有好处。

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

如果您开始引入自动生成的库,那么您肯定会对将来的开发人员更新代码进行更多的混淆,而不是简单地将您的枚举编码到相应的类对象中。

其他的例子给出了很好的和令人兴奋的,但是考虑一下代码维护的开销和你从中得到的东西。 而且,这些价值观是否会经常改变呢?

保持枚举值并同时创build一个dynamic值列表的一种方法是使用当前拥有dynamic创build的字典的枚举值。

由于大多数枚举是在被定义使用的上下文中使用的,dynamic过程支持“dynamic枚举”,所以可以区分2。

第一步是创build一个表格/集合,其中包含dynamic条目的ID和引用。 在表中,你会自动增加比你最大的Enum值大得多的值。

现在来为你的dynamic枚举部分,我假设你将使用枚举来创build一组条件,应用一组规则,一些是dynamic生成的。

 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.