it-swarm.com.de

Automatisches Erstellen eines Enums basierend auf Werten in einer Datenbank-Nachschlagetabelle?

Wie erstelle ich automatisch eine Enumeration und verwende anschließend deren Werte in C # basierend auf Werten in einer Datenbank-Suchtabelle (unter Verwendung der Datenschicht der Unternehmensbibliothek)?

Wenn ich beispielsweise einen neuen Lookup-Wert in die Datenbank einfüge, möchte ich die zusätzliche Deklaration des statischen Enumerationswerts nicht manuell in Code hinzufügen - ich möchte das Enum mit der Datenbank synchronisieren.

Gibt es so etwas? 


Ich möchte keinen Code generieren, der ein statisches Enum generiert (gemäß The Code Project article Enum Code Generator - Generieren von Enum-Code automatisch aus Datenbank-Suchtabellen) und würde dies vorziehen völlig automatisch sein.

104
billfredtom

Ich mache genau das, aber Sie müssen eine Art Code-Generierung durchführen, damit dies funktioniert.

In meiner Lösung habe ich ein Projekt "EnumeratedTypes" hinzugefügt. Dies ist eine Konsolenanwendung, die alle Werte aus der Datenbank abruft und daraus die Aufzählungen erstellt. Dann werden alle Aufzählungen in einer Assembly gespeichert.

Der Code für die Generierung der Aufzählung sieht folgendermaßen aus:

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

Meine anderen Projekte in der Lösung verweisen auf diese generierte Assembly. Infolgedessen kann ich dann die dynamischen Aufzählungen im Code einschließlich Intellisense verwenden.

Dann habe ich ein Post-Build-Ereignis hinzugefügt, damit dieses "EnumeratedTypes" -Projekt nach seiner Erstellung von selbst ausgeführt und die "MyEnums.dll" -Datei generiert wird.

Übrigens hilft es, die Erstellungsreihenfolge Ihres Projekts so zu ändern, dass "EnumeratedTypes" zuerst erstellt wird. Andernfalls können Sie, sobald Sie mit der Verwendung Ihrer dynamisch generierten DLL-Datei beginnen, keine Erstellung mehr durchführen, wenn die DLL-Datei jemals gelöscht wird. (Huhn und Ei Art von Problem - Ihre anderen Projekte in der Lösung benötigen diese DLL, um richtig zu erstellen, und Sie können die DLL nicht erstellen, bis Sie Ihre Lösung erstellen ...)

Ich habe den größten Teil des obigen Codes von dieser MSDN-Artikel .

Hoffe das hilft!

93
Pandincus

Enumerationen müssen zum Zeitpunkt des Kompilierens angegeben werden. Sie können zur Laufzeit keine Enumerationen dynamisch hinzufügen.

Von Professional C # 2008:

Die eigentliche Kraft von Enums in C # besteht darin, dass sie hinter den Kulissen als von der Basisklasse System.Enum abgeleitete Strukturen instanziiert werden. Das heißt, es ist möglich, Methoden gegen sie aufzurufen, um einige nützliche Aufgaben auszuführen. Beachten Sie, dass aufgrund der Art und Weise, wie .NET Framework implementiert wird, kein Leistungsverlust auftritt, wenn die Aufzählungen syntaktisch als Strukturen behandelt werden. In der Praxis werden Enumerationen nach der Kompilierung des Codes als primitive Typen definiert, genau wie int und float.

Ich bin mir nicht sicher, ob Sie Enums so verwenden können, wie Sie es möchten.

47
Marcus L

Muss es eine tatsächliche Enumeration sein? Wie wäre es stattdessen einen Dictionary<string,int> zu verwenden?

zum Beispiel 

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

Nehmen wir an, Sie haben folgendes in Ihrer Datenbank: 

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

Konstruieren Sie eine Auswahl, um die benötigten Werte zu erhalten:

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

Erstellen Sie den Quellcode für das Enum und Sie erhalten so etwas wie:

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

(Offensichtlich ist dies in irgendeiner Art von Schleife aufgebaut.)

Dann kommt der lustige Teil: Kompilieren und verwenden:

CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters cs = new CompilerParameters();
cp.GenerateInMemory = True;

CompilerResult result = provider.CompileAssemblyFromSource(cp, enumSourceCode);

Type enumType = result.CompiledAssembly.GetType(enumName);

Jetzt haben Sie den Typ kompiliert und können verwendet werden.
Um einen Aufzählungswert in der DB abzurufen, können Sie Folgendes verwenden:

[Enum].Parse(enumType, value);

dabei kann der Wert entweder der ganzzahlige Wert (0, 1 usw.) oder der Auflistungstext/Schlüssel (Apple, Banana usw.) sein.

11

Zeigen Sie einfach die answer von Pandincus mit dem Code "of the shelf" und einer Erklärung: Sie benötigen zwei Lösungen für dieses Beispiel (ich weiß, dass dies auch über eine der beiden möglich ist); ...

Hier ist also die DDL-SQL für die Tabelle: 

USE [ocms_dev]
    GO

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

Hier ist also das Konsolenprogramm, das die DLL erzeugt: 

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 

Hier ist die Konsolenprogrammierung, die die Ausgabe druckt (denken Sie daran, dass sie auf die DLL verweisen muss). Lassen Sie die Fortgeschrittenen Studenten die Lösung präsentieren, um alles in einer Lösung mit dynamischem Laden zu kombinieren und zu überprüfen, ob bereits DLL-Build vorhanden ist. 

// 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
10
Yordan Georgiev

Ich habe das mit einer T4 Vorlage gemacht. Es ist ziemlich trivial, eine .tt-Datei in Ihrem Projekt abzulegen und Visual Studio so einzustellen, dass die T4-Vorlage als Vorbereitungsschritt ausgeführt wird. 

Der T4 generiert eine CS-Datei, dh, Sie können die Datenbank einfach abfragen und aus dem Ergebnis eine Enumeration in einer CS-Datei erstellen. Als Pre-Build-Aufgabe verdrahtet, wird das Enum für jeden Build neu erstellt, oder Sie können den T4 stattdessen manuell ausführen, wenn dies erforderlich ist.

10
CodingWithSpike

Kommen wir nicht aus der falschen Richtung dazu?

Wenn sich die Daten wahrscheinlich während der Laufzeit der bereitgestellten Version ändern, ist eine Aufzählung einfach nicht geeignet, und Sie müssen ein Wörterbuch, einen Hash oder eine andere dynamische Sammlung verwenden.

Wenn Sie wissen, dass der Satz möglicher Werte für die Lebensdauer der bereitgestellten Version festgelegt ist, ist eine Aufzählung vorzuziehen.

Wenn Sie müssen etwas in Ihrer Datenbank haben, das die Aufzählungsgruppe repliziert, können Sie einen Bereitstellungsschritt hinzufügen, um die Datenbanktabelle zu löschen und mit den endgültigen Aufzählungswerten aufzufüllen.

3
Brian Lowe

Ich schreibe immer gerne mein eigenes "Custom Enum". Dann habe ich eine Klasse, die etwas komplexer ist, aber ich kann sie wiederverwenden:

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

Jetzt muss ich nur noch ein Enum erstellen, das ich verwenden möchte:

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

Endlich kann ich es benutzen, wie ich will:

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

Und meine Ausgabe wäre:

Element1: foo
Is 'Element2': False

Element2: bar
Is 'Element2': True    
2
Traummaennlein

Sie möchten System.Web.Compilation.BuildProvider

Ich bezweifle auch die Weisheit, dies zu tun, aber dann gibt es vielleicht einen guten Anwendungsfall, an den ich nicht denken kann.

Sie suchen nach Build Providers d. H. System.Web.Compilation.BuildProvider

Sie werden very effektiv von SubSonic verwendet. Sie können die Quelle herunterladen und sehen, wie sie verwendet werden. Sie brauchen nichts halb so kompliziertes wie das, was sie tun.

Hoffe das hilft.

2
Binary Worrier

Sie können CodeSmith verwenden, um Folgendes zu generieren:

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

1
Rick Boarman

Die Verwendung dynamischer Enumerationen ist auf jeden Fall schlecht. Sie müssen sich die Mühe machen, die Daten zu "duplizieren", um einen klaren und einfachen Code zu gewährleisten, der in der Zukunft leicht zu warten ist.

Wenn Sie mit der Einführung automatisch generierter Bibliotheken beginnen, sind Sie mit Sicherheit mehr Verwirrung für zukünftige Entwickler, die Ihren Code aktualisieren müssen, als nur das Enum innerhalb des entsprechenden Klassenobjekts zu codieren.

Die anderen Beispiele klingen schön und aufregend, denken Sie jedoch an den Aufwand für die Codewartung im Vergleich zu dem, was Sie davon bekommen. Werden sich diese Werte auch so häufig ändern?

0
Martin

Eine Möglichkeit, die Aufzählungslisten beizubehalten und gleichzeitig eine dynamische Liste von Werten zu erstellen, besteht darin, die derzeit vorhandenen Aufzählungen mit einem dynamisch erstellten Wörterbuch zu verwenden.

Da die meisten Enums in dem Kontext verwendet werden, für den sie definiert sind, und die "dynamischen Enums" von dynamischen Prozessen unterstützt werden, können Sie die 2 unterscheiden.

Im ersten Schritt erstellen Sie eine Tabelle/Sammlung, in der die IDs und Verweise für die dynamischen Einträge enthalten sind. In der Tabelle werden Sie automatisch viel größer als Ihr größter Enum-Wert.

Nun kommt der Teil für Ihre dynamischen Enums. Ich gehe davon aus, dass Sie die Enums verwenden werden, um eine Reihe von Bedingungen zu erstellen, die eine Reihe von Regeln anwenden. Einige davon werden dynamisch generiert.

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.
0
Jackstine

Ich glaube nicht, dass es eine gute Möglichkeit gibt, das zu tun, was Sie wollen. Und wenn Sie darüber nachdenken, glaube ich nicht, dass Sie das wirklich wollen.

Wenn Sie über eine dynamische Enumeration verfügen, bedeutet dies auch, dass Sie beim Verweisen einen dynamischen Wert angeben müssen. Vielleicht könnten Sie mit viel Magie eine Art von IntelliSense erreichen, das sich darum kümmert und ein Enum für Sie in einer DLL -Datei generiert. Berücksichtigen Sie jedoch die Menge an Arbeit, die erforderlich wäre, und wie unwirksam es wäre, auf die Datenbank zuzugreifen, um IntelliSense-Informationen abzurufen, sowie den Albtraum der Version, die die generierte Datei DLL steuert.

Wenn Sie die Aufzählungswerte wirklich nicht manuell hinzufügen möchten (Sie müssen sie ohnehin der Datenbank hinzufügen), verwenden Sie stattdessen ein Code-Generierungswerkzeug, z. B. T4 templates. Rechtsklick + Ausführen und Sie haben Ihr Enum statisch im Code definiert und Sie erhalten alle Vorteile der Verwendung von Enums.

0
Runeborg

enum-Builder-Klasse 

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

ein Objekt erstellen

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