Crea automaticamente un Enum in base ai valori in una tabella di ricerca del database?

Come posso creare automaticamente un enum e successivamente utilizzare i suoi valori in C # in base ai valori in una tabella di ricerca del database (utilizzando il livello dati della libreria aziendale)?

Ad esempio, se aggiungo un nuovo valore di ricerca nel database, non voglio dover aggiungere manualmente la dichiarazione del valore enum statico in eccesso nel codice: mi piacerebbe mantenere l’enum in sincronia con il database.

C’è una cosa come questa?


Non voglio creare un enum statico generato dal codice (come per l’ articolo del codice del progetto Enum Code Generator – Generare automaticamente il codice enum dalle tabelle di ricerca del database ) e preferire che sia completamente automatico.

Sto facendo esattamente questa cosa, ma devi fare una specie di generazione di codice per far funzionare tutto questo.

Nella mia soluzione, ho aggiunto un progetto “EnumeratedTypes”. Questa è un’applicazione console che ottiene tutti i valori dal database e costruisce da essi le enumerazioni. Quindi salva tutte le enumerazioni in un assembly.

Il codice di generazione enum è così:

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

I miei altri progetti nella soluzione fanno riferimento a questo assembly generato. Di conseguenza, posso quindi utilizzare le enumerazioni dinamiche nel codice, complete di intellisense.

Quindi, ho aggiunto un evento post-build in modo che dopo che questo progetto “EnumeratedTypes” sia stato creato, viene eseguito automaticamente e genera il file “MyEnums.dll”.

A proposito, aiuta a cambiare l’ ordine di costruzione del tuo progetto in modo che “EnumeratedTypes” sia costruito per primo. Altrimenti, una volta che inizi a utilizzare il file .dll generato dynamicmente, non sarai in grado di creare una build se il file .dll viene cancellato. (Pollo e uovo tipo di problema – gli altri progetti nella soluzione hanno bisogno di questo .dll per essere compilati correttamente e non è ansible creare il file .dll finché non si crea la soluzione …)

Ho ottenuto la maggior parte del codice sopra da questo articolo di msdn .

Spero che questo ti aiuti!

Le enumerazioni devono essere specificate al momento della compilazione, non è ansible aggiungere dynamicmente le enumerazioni durante il tempo di esecuzione e perché, nel codice, non ci sarebbe alcun uso / riferimento ad esse?

Dal Professional C # 2008:

Il vero potere delle enumerazioni in C # è che dietro le quinte vengono istanziate come strutture derivate dalla class base, System.Enum. Ciò significa che è ansible chiamare metodi contro di loro per eseguire alcune attività utili. Si noti che, a causa del modo in cui .NET Framework è implementato, non vi è alcuna perdita di prestazioni associata al trattamento sintattico delle enumerazioni come struct. In pratica, una volta compilato il codice, le enumerazioni esisteranno come tipi primitivi, proprio come int e float.

Quindi, non sono sicuro che tu possa usare Enum nel modo che preferisci.

Deve essere un vero enum? Che ne dici di usare un Dictionary ?

per esempio

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

Diciamo che hai il seguente nel tuo 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 | ---------------------------------- 

Costruisci una selezione per ottenere i valori necessari:

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

Costruisci il codice sorgente per l’enum e otterrai qualcosa come:

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

(ovviamente questo è costruito in un ciclo di qualche tipo).

Poi arriva la parte divertente, compilando il tuo enum e usandolo:

 CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); CompilerParameters cs = new CompilerParameters(); cp.GenerateInMemory = True; CompilerResult result = provider.CompileAssemblyFromSource(cp, enumSourceCode); Type enumType = result.CompiledAssembly.GetType(enumName); 

Ora hai il tipo compilato e pronto per l’uso.
Per ottenere un valore enum memorizzato nel DB è ansible utilizzare:

 [Enum].Parse(enumType, value); 

dove valore può essere il valore intero (0, 1, ecc.) o il testo enum / chiave (Apple, Banana, ecc.)

Sto solo mostrando la risposta di Pandincus con il codice “di scaffale” e qualche spiegazione: per questo esempio hai bisogno di due soluzioni (so che si potrebbe fare anche con una di queste;), lascia che siano gli studenti avanzati a presentarla …

Quindi ecco l’SQL DDL per la tabella:

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

Quindi, ecco il programma della console che produce la 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 

Ecco la programmazione della console che stampa l’output (ricorda che deve fare riferimento alla dll). Lascia che gli studenti avanzati presentino la soluzione per combinare tutto in una soluzione con caricamento dinamico e verificare se c’è già una 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 

Ho fatto questo con un modello T4 . È abbastanza semplice rilasciare un file .tt nel progetto e configurare Visual Studio per eseguire il modello T4 come fase preliminare.

Il T4 genera un file .cs, il che significa che puoi averlo solo interrogare il database e creare un enum in un file .cs dal risultato. Collegata come attività pre-compilazione, ricrea il tuo enum su ogni build, oppure puoi eseguire manualmente il T4 secondo necessità.

Vuoi System.Web.Compilation.BuildProvider

Dubito anche della saggezza di farlo, ma poi forse c’è un buon caso d’uso a cui non riesco a pensare.

Quello che stai cercando sono Build Provider ovvero System.Web.Compilation.BuildProvider

Sono usati in modo molto efficace da SubSonic , puoi scaricare la fonte e vedere come li usano, non avrai bisogno di qualcosa di altrettanto complicato quanto quello che stanno facendo.

Spero che questo ti aiuti.

Mi piace sempre scrivere il mio “enum personalizzato”. Ho una class un po ‘più complessa, ma posso riutilizzarla:

 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 : CustomEnum where TEnumType : CustomEnum { 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 GetAll() { var elements = new Collection(); 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 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" ); } } 

Ora ho solo bisogno di creare il mio enum che voglio usare:

  public sealed class SampleEnum : CustomEnum { 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; } } 

Finalmente posso usarlo come voglio:

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

E il mio risultato sarebbe:

 Element1: foo Is 'Element2': False Element2: bar Is 'Element2': True 

Non stiamo arrivando a questo dalla direzione sbagliata?

Se è probabile che i dati cambino durante la durata della versione distribuita, un enum non è appropriato e devi utilizzare un dizionario, un hash o un’altra raccolta dynamic.

Se sai che l’insieme di valori possibili è fisso per la durata della versione distribuita, allora è preferibile un enum.

Se è necessario avere qualcosa nel database che replica il set enumerato, allora perché non aggiungere un passo di distribuzione per cancellare e ripopolare la tabella del database con l’insieme definitivo di valori enum?

Potresti usare CodeSmith per generare qualcosa come questo:

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

Non penso che ci sia un buon modo di fare quello che vuoi. E se ci pensi, non penso che questo sia ciò che vuoi veramente.

Se tu avessi un enum dinamico, significa anche che devi nutrirlo con un valore dinamico quando lo fai riferimento. Magari con un sacco di magia potresti ottenere una sorta di IntelliSense che si prenderà cura di questo e genererà un enum per te in un file DLL. Ma considerate la quantità di lavoro che ci vorrebbe, quanto sarebbe stato inefficace accedere al database per recuperare le informazioni di IntelliSense e l’incubo della versione che controlla il file DLL generato.

Se davvero non vuoi aggiungere manualmente i valori enum (dovrai comunque aggiungerli al database) usa invece uno strumento di generazione del codice, ad esempio i modelli T4 . Fai clic con il tasto destro del mouse + Esegui e ottieni il tuo enum definito staticamente nel codice e ottieni tutti i benefici dell’uso di enumerazioni.

Usare le enumerazioni dinamiche è male, non importa in quale direzione. Dovrai affrontare il problema della “duplicazione” dei dati per garantire un codice semplice e chiaro di facile manutenzione in futuro.

Se si avvia l’introduzione di librerie generate automaticamente, si sta sicuramente causando maggiore confusione per gli sviluppatori futuri che devono aggiornare il proprio codice piuttosto che semplicemente codificare enum all’interno dell’object di class appropriato.

Gli altri esempi forniti sono belli ed entusiasmanti, ma pensate al sovraccarico sulla manutenzione del codice rispetto a ciò che ottenete. Inoltre, questi valori cambieranno frequentemente?

Un modo per mantenere gli Enum e per creare un elenco di valori dinamico allo stesso tempo è quello di utilizzare l’enumerazione che si ha attualmente con un dizionario creato dynamicmente.

Poiché la maggior parte delle Enum sono utilizzate nel contesto in cui sono definite per essere utilizzate, e le “enum dinamiche” saranno supportate da processi dinamici, è ansible distinguere il 2.

Il primo passo è creare una tabella / collezione che contenga gli ID e i riferimenti per le voci dinamiche. Nella tabella, l’autoincremento sarà molto più grande del valore Enum più grande.

Ora viene la parte per il tuo Enum dinamico, suppongo che userete l’Enum per creare una serie di condizioni che applicano un insieme di regole, alcune sono generate dynamicmente.

 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. 

class enum builder

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

creare un object

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