如何自动创建枚举并随后基于数据库查找表中的值在C#中使用其值(使用企业库数据层)?
例如,如果我在数据库中添加新的查找值,则不需要在代码中手动添加额外的静态枚举值声明-我想使枚举与数据库保持同步。
有这样的事吗?
我不想创建一个代码生成的静态枚举(根据The Code Project文章Enum Code Generator-从数据库查找表自动生成枚举代码),并且希望它是完全自动的。
如何自动创建枚举并随后基于数据库查找表中的值在C#中使用其值(使用企业库数据层)?
例如,如果我在数据库中添加新的查找值,则不需要在代码中手动添加额外的静态枚举值声明-我想使枚举与数据库保持同步。
有这样的事吗?
我不想创建一个代码生成的静态枚举(根据The Code Project文章Enum Code Generator-从数据库查找表自动生成枚举代码),并且希望它是完全自动的。
Answers:
我正在做这件事,但是您需要执行某种代码生成才能使其工作。
在我的解决方案中,我添加了一个项目“ 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");
解决方案中的其他项目引用了此生成的程序集。结果,我可以在代码中使用动态枚举以及完整的intellisense。
然后,我添加了一个构建后事件,以便在构建此“ EnumeratedTypes”项目之后,该项目将自行运行并生成“ MyEnums.dll”文件。
顺便说一句,它有助于更改项目的构建顺序,以便首先构建“ EnumeratedTypes”。否则,一旦开始使用动态生成的.dll,如果.dll曾经被删除,则将无法进行构建。(鸡和蛋类的问题-解决方案中的其他项目需要此.dll才能正确构建,并且只有在构建解决方案后才能创建.dll。)
我从这篇msdn文章中获得了以上大部分代码。
希望这可以帮助!
枚举必须在编译时指定,您不能在运行时动态添加枚举-为什么您在代码中不会使用/引用它们呢?
从Professional C#2008:
C#中枚举的真正功能是在后台将它们实例化为派生自基类System.Enum的结构。这意味着可以针对它们调用方法以执行一些有用的任务。请注意,由于.NET Framework的实现方式,因此在语法上将枚举作为结构对待时不会造成性能损失。实际上,一旦您的代码被编译,枚举将作为基本类型存在,就像int和float一样。
因此,我不确定您是否可以按想要的方式使用枚举。
System.Enum
具有某些其他功能)。而不是写const int Red=0, Green=1, Blue=3;
你写enum { Red, Green, Blue }
。根据定义,常量是常量,不是动态的。
它一定是一个实际的枚举吗?改用a怎么样Dictionary<string,int>
?
例如
Dictionary<string, int> MyEnum = new Dictionary(){{"One", 1}, {"Two", 2}};
Console.WriteLine(MyEnum["One"]);
假设您的数据库中有以下内容:
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);
其中value可以是整数值(0、1等)或枚举文本/键(Apple,Banana等)
只是显示 答案带有“货架”代码的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
flagFileExists
在应用程序的其他地方不使用它时为什么要声明呢?
我一直喜欢写自己的“自定义枚举”。比起我的一门课要复杂一点,但我可以重用它:
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
您需要System.Web.Compilation.BuildProvider
我也怀疑这样做是否明智,但是也许有一个我想不到的好用例。
您正在寻找的是Build Providers,即System.Web.Compilation.BuildProvider
SubSonic 非常有效地使用了它们,你可以下载源代码,看看他们如何使用它们,你将不再需要任何的一半复杂,因为他们在做什么。
希望这可以帮助。
您可以使用CodeSmith生成如下内容:
http://www.csharping.com/PermaLink,guid,cef1b637-7d37-4691-8e49-138cbf1d51e9.aspx
我认为没有一种做您想要的事情的好方法。如果您考虑一下,我不认为这是您真正想要的。
如果您有一个动态的枚举,那也意味着您在引用它时必须给它提供一个动态值。也许您可以通过许多魔术来实现某种IntelliSense,以解决此问题并在DLL文件中为您生成一个枚举。但是,请考虑所需的工作量,访问数据库以获取IntelliSense信息的效率以及控制生成的DLL文件的版本的噩梦。
如果您确实不想手动添加枚举值(无论如何都必须将它们添加到数据库中),请使用代码生成工具,例如T4模板。右键单击并运行,您将在代码中静态定义了枚举,并获得了使用枚举的所有好处。
保留枚举并同时创建值的动态列表的一种方法是将当前拥有的枚举与动态创建的词典一起使用。
由于大多数枚举均在定义要使用的上下文中使用,并且动态进程将支持“动态枚举”,因此可以区分2。
第一步是创建一个表/集合,其中包含动态条目的ID和参考。在表中,您将自动增加一个大于最大Enum值的值。
现在是动态枚举的一部分,我假设您将使用枚举创建一组应用一组规则的条件,其中一些规则是动态生成的。
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.
枚举生成器类
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();