列挙型を自動的に作成し、データベースルックアップテーブルの値に基づいてC#でその値を使用するにはどうすればよいですか(エンタープライズライブラリデータレイヤーを使用)?
たとえば、データベースに新しいルックアップ値を追加する場合、コード内に余分な静的列挙値宣言を手動で追加する必要はありません-データベースと列挙の同期を維持したいと思います。
このようなものはありますか?
コード生成の静的列挙型を作成したくない( The Code Project articleEnum Code Generator-データベースの外観から列挙型コードを自動的に生成する) up tables))そして、完全に自動であることが望ましいでしょう。
私はこれを正確にやっていますが、これを機能させるためには何らかのコード生成を行う必要があります必要です。
私のソリューションでは、プロジェクト「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を作成できません...)
上記のコードのほとんどは このmsdnの記事 から取得しました。
お役に立てれば!
列挙型はコンパイル時に指定する必要があります。実行時に動的に列挙型を追加することはできません-なぜ、コードにそれらの使用/参照がないのでしょうか?
Professional C#2008から:
C#の列挙型の本当の力は、基本クラスSystem.Enumから派生した構造体としてインスタンス化されることです。これは、それらに対してメソッドを呼び出して、いくつかの有用なタスクを実行できることを意味します。 .NET Frameworkの実装方法により、enumを構造体として構文的に扱うことに関連するパフォーマンスの低下がないことに注意してください。実際には、コードがコンパイルされると、列挙型はintやfloatのようなプリミティブ型として存在します。
だから、私はあなたが望むように列挙型を使用できるかどうかわからない。
実際の列挙型である必要がありますか?代わりにDictionary<string,int>
を使用してはどうですか?
例えば
Dictionary<string, int> MyEnum = new Dictionary(){{"One", 1}, {"Two", 2}};
Console.WriteLine(MyEnum["One"]);
T4 テンプレートでこれを行いました。プロジェクトに.ttファイルをドロップし、Visual StudioをセットアップしてT4テンプレートをビルド前のステップとして実行するのは、かなり簡単です。
T4は.csファイルを生成します。つまり、データベースにクエリを実行し、その結果から.csファイルに列挙を作成できます。ビルド前タスクとして接続され、ビルドごとに列挙型を再作成します。または、代わりに必要に応じて手動でT4を実行できます。
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 |
----------------------------------
Selectを構築して、必要な値を取得します。
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 + ... + "}";
(明らかにこれは何らかのループで構築されます。)
次に、enumをコンパイルして使用する楽しい部分があります。
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters cs = new CompilerParameters();
cp.GenerateInMemory = True;
CompilerResult result = provider.CompileAssemblyFromSource(cp, enumSourceCode);
Type enumType = result.CompiledAssembly.GetType(enumName);
これで、型がコンパイルされ、使用できる状態になりました。
DBに格納されている列挙値を取得するには、次を使用できます。
[Enum].Parse(enumType, value);
valueは整数値(0、1など)または列挙型テキスト/キー(Apple、Bananaなど)のいずれかです。
answer を「棚の」コードといくつかの説明とともに表示します:この例には2つのソリューションが必要です(1つでも解決できることはわかっています;)、上級生に提示させてください...
したがって、表の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を参照する必要があることに注意してください)。上級の学生に、すべてを1つのソリューションに動的ロードと組み合わせて、既にビルド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
私たちは間違った方向からこれに来ていませんか?
デプロイされたリリースの存続期間中にデータがまったく変更される可能性がある場合、enumは適切ではなく、辞書、ハッシュ、またはその他の動的コレクションを使用する必要があります。
展開されたリリースの存続期間中に可能な値のセットが固定されていることがわかっている場合は、enumが推奨されます。
must列挙セットをレプリケートするデータベースに何かがある場合は、データベーステーブルを列挙値の明確なセットでクリアして再入力する展開ステップを追加してみませんか?
私はいつも自分の「カスタム列挙型」を書くのが好きです。少し複雑なクラスが1つありますが、再利用できます。
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
動的列挙型の使用は、どの方法であっても悪いです。将来保守しやすい明確で簡単なコードを確保するために、データを「複製」する手間をかける必要があります。
自動生成ライブラリの導入を開始する場合、enumを適切なクラスオブジェクト内でコーディングするだけでなく、コードをアップグレードする必要がある将来の開発者に混乱を引き起こすことは確かです。
他の例は、素晴らしくエキサイティングに聞こえますが、コードメンテナンスのオーバーヘッドと、そこから得られるものについて考えてください。また、それらの値は頻繁に変わるのでしょうか?
列挙を保持し、同時に値の動的リストを作成する1つの方法は、動的に作成されたディクショナリで現在持っている列挙を使用することです。
ほとんどの列挙型は、使用するように定義されているコンテキストで使用され、「動的列挙型」は動的プロセスでサポートされるため、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.
あなたがしたいことをする良い方法はないと思います。そして、あなたがそれについて考えるならば、私はこれがあなたが本当に望むものであるとは思いません。
動的な列挙型がある場合は、参照するときに動的な値を渡す必要があることも意味します。たぶん多くの魔法を使えば、これを処理し、DLLファイルに列挙型を生成する IntelliSense を実現できます。ただし、必要な作業量、データベースにアクセスしてIntelliSense情報を取得するのがどれだけ効果的であるか、生成されたDLLファイルを制御する悪夢を考慮してください。
本当に列挙値を手動で追加したくない場合(とにかくデータベースに追加する必要があります)、代わりにコード生成ツールを使用してください。たとえば、 T4 templates。右クリックして実行すると、enumがコードで静的に定義され、enumを使用するすべての利点が得られます。
列挙型ビルダークラス
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();
CodeSmithを使用して、次のようなものを生成できます。
http://www.csharping.com/PermaLink,guid,cef1b637-7d37-4691-8e49-138cbf1d51e9.aspx