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文章中得到了上面的大部分代码.
希望这可以帮助!
Mar*_*s L 47
枚举必须在编译时指定,你不能在运行时动态添加枚举 - 为什么你会在代码中没有使用/引用它们?
来自Professional C#2008:
C#中枚举的真正强大之处在于它们在幕后实例化为从基类System.Enum派生的结构.这意味着可以调用针对它们的方法来执行一些有用的任务.请注意,由于.NET Framework的实现方式,在语法上将枚举视为结构时,不存在性能损失.在实践中,一旦编译了代码,枚举将作为基本类型存在,就像int和float一样.
所以,我不确定你能以你想要的方式使用Enums.
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)
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等)
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)
我们不是从错误的方向来到这里吗?
如果数据在部署版本的生命周期内可能会发生变化,那么枚举就是不合适的,您需要使用字典、哈希或其他动态集合。
如果您知道这组可能值在已部署版本的生命周期内是固定的,则最好使用枚举。
如果您的数据库中必须有一些东西来复制枚举集,那么为什么不添加一个部署步骤来清除并使用一组确定的枚举值重新填充数据库表呢?
我总是喜欢编写自己的“自定义枚举”。比我有一个稍微复杂一点的类,但我可以重用它:
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)
| 归档时间: |
|
| 查看次数: |
121286 次 |
| 最近记录: |