it-swarm.com.de

Beste Methode zum Speichern von Enum in der Datenbank

Was ist die beste Methode zum Speichern eines Enums in einer Datenbank mit C # And Visual Studio und MySQL Data Connector?.

Ich werde ein neues Projekt mit über 100 Enums erstellen, und die meisten von ihnen müssen in der Datenbank gespeichert werden. Konverter für jeden zu erstellen, wäre ein langwieriger Prozess. Ich frage mich, ob Visual Studio oder jemand über Methoden verfügt, die ich noch nicht gehört habe.

54
Michal Ciechan
    [Required]
    public virtual int PhoneTypeId
    {
        get
        {
            return (int)this.PhoneType;
        }
        set
        {
            PhoneType = (PhoneTypes)value;
        }
    }
    [EnumDataType(typeof(PhoneTypes))]
    public PhoneTypes PhoneType { get; set; }

public enum PhoneTypes
{
    Mobile = 0,
    Home = 1,
    Work = 2,
    Fax = 3,
    Other = 4
}

Klappt wunderbar! Keine Notwendigkeit, (int) Enum oder (Enum) int in Code zu konvertieren. Verwenden Sie einfach das Enum und ef-Code, um das int für Sie zu speichern. p.s .: Das Attribut "[EnumDataType (typeof (PhoneTypes))]" ist nicht erforderlich, es ist lediglich ein Zusatz, wenn Sie zusätzliche Funktionen wünschen.

Alternativ können Sie Folgendes tun:

[Required]
    public virtual int PhoneTypeId { get; set; }
    [EnumDataType(typeof(PhoneTypes))]
    public PhoneTypes PhoneType
    {
        get
        {
            return (PhoneTypes)this.PhoneTypeId;
        }
        set
        {
            this.PhoneTypeId = (int)value;
        }
    }
52
Joao Leme

Wir speichern unsere als Ints oder Longs und dann können wir sie einfach hin und her werfen. Wahrscheinlich nicht die robusteste Lösung, aber das, was wir tun.

wir verwenden typisierte DataSets, so zum Beispiel:

enum BlockTreatmentType 
{
    All = 0
};

// blockTreatmentType is an int property
blockRow.blockTreatmentType = (int)BlockTreatmentType.All;
BlockTreatmentType btt = (BlockTreatmentType)blockRow.blocktreatmenttype;
10
Muad'Dib

Wenn Sie alle Ihre Enumerationswerte speichern möchten, können Sie die folgenden Tabellen verwenden, um Enumerationen und ihre Mitglieder zu speichern, und das Code-Snippet zum Hinzufügen dieser Werte. Ich würde dies jedoch nur zur Installationszeit tun, da sich diese Werte erst ändern, wenn Sie neu kompilieren!

DB-Tabelle:

   create table EnumStore (
    EnumKey int NOT NULL identity primary key,
    EnumName varchar(100)
);
GO

create table EnumMember (
    EnumMemberKey int NOT NULL identity primary key,
    EnumKey int NOT NULL,
    EnumMemberValue int,
    EnumMemberName varchar(100)
);
GO
--add code to create foreign key between tables, and index on EnumName, EnumMemberValue, and EnumMemberName

C # -Ausschnitt:

void StoreEnum<T>() where T: Enum
    {
        Type enumToStore = typeof(T);
        string enumName = enumToStore.Name;

        int enumKey = DataAccessLayer.CreateEnum(enumName);
        foreach (int enumMemberValue in Enum.GetValues(enumToStore))
        {
            string enumMemberName = Enum.GetName(enumToStore, enumMemberValue);
            DataAccessLayer.AddEnumMember(enumKey, enumMemberValue, enumMemberName);
        }
    }
3
cortijon

Einige Dinge, die Sie berücksichtigen sollten. 

Wird die Aufzählungsspalte direkt von anderen Anwendungen wie beispielsweise Berichten verwendet. Dadurch wird die Möglichkeit begrenzt, dass die Aufzählung im Integer-Format gespeichert wird, da der Wert in einem Bericht keine Bedeutung hat, es sei denn, die Berichte verfügen über eine benutzerdefinierte Logik.

Was braucht der i18n für Ihre Bewerbung? Wenn nur eine Sprache unterstützt wird, können Sie die Aufzählung als Text speichern und eine Hilfsmethode erstellen, die aus einer Beschreibungszeichenfolge konvertiert werden kann. Sie können hierfür [DescriptionAttribute] verwenden, und Methoden für die Konvertierung können wahrscheinlich durch Suchen nach SO gefunden werden.

Wenn Sie andererseits den Zugriff auf Ihre Daten in mehreren Sprachen und mit externen Anwendungen unterstützen müssen, können Sie überlegen, ob die Aufzählung wirklich die Antwort ist. Andere Optionen wie Nachschlagetabellen können in Betracht gezogen werden, wenn das Szenario komplexer ist.

Aufzählungen sind hervorragend, wenn sie im Code selbst enthalten sind ... Wenn sie diese Grenze überschreiten, werden die Dinge etwas unordentlich.


Update:

Sie können von einer Ganzzahl mit der Enum.ToObject-Methode konvertieren. Dies bedeutet, dass Sie die Art der Aufzählung beim Konvertieren kennen. Wenn Sie es vollständig generisch machen möchten, müssen Sie den Aufzählungstyp neben seinem Wert in der Datenbank speichern. Sie können Tabellen für das Datenwörterbuch erstellen, um anzugeben, welche Spalten Enumerationen sind und welcher Typ sie sind.

3
João Angelo

Am Ende benötigen Sie eine gute Möglichkeit, sich wiederholende Codierungsaufgaben wie Enum-Konverter zu erledigen. Sie können einen Code-Generator wie MyGeneration oder CodeSmith unter vielen anderen verwenden oder vielleicht einen ORM-Mapper wie nHibernate , um alles für Sie zu erledigen.

Was die Struktur angeht ... mit Hunderten von Aufzählungen würde ich zuerst versuchen, die Daten in einer einzigen Tabelle zu organisieren, die in etwa wie folgt aussehen könnte: (Pseudo-SQL)

MyEnumTable(
EnumType as int,
EnumId as int PK,
EnumValue as int )

auf diese Weise können Sie Ihre Enum-Informationen in einer einzigen Tabelle speichern. EnumType könnte auch ein Fremdschlüssel für eine Tabelle sein, die die verschiedenen Enummen definiert.

Ihre biz-Objekte würden über EnumId mit dieser Tabelle verknüpft. Der Aufzählungstyp dient nur zur Organisation und zum Filtern in der Benutzeroberfläche. Die Verwendung all dessen hängt natürlich von Ihrer Codestruktur und Problemdomäne ab.

Übrigens, in diesem Szenario möchten Sie einen Clustered-Index für EnumType festlegen, anstatt den Standardcluster idx beizubehalten, der auf dem PKey erstellt wird.

3
Paul Sasik

Wenn Sie in DB-Zeichenfolgewerten des Aufzählungsfelds speichern möchten, gehen Sie am besten wie unten dargestellt dar. __ Zum Beispiel kann es erforderlich sein, wenn Sie SQLite verwenden, das keine Aufzählungsfelder unterstützt.

[Required]
public string PhoneTypeAsString
{
    get
    {
        return this.PhoneType.ToString();
    }
    set
    {
        PhoneType = (PhoneTypes)Enum.Parse( typeof(PhoneTypes), value, true);
    }
}

public PhoneTypes PhoneType{get; set;};

public enum PhoneTypes
{
    Mobile = 0,
    Home = 1,
    Work = 2,
    Fax = 3,
    Other = 4
}
2
trueboroda

Ich bin nicht sicher, ob es am flexibelsten ist, aber Sie könnten einfach die String-Versionen davon speichern. Es ist sicherlich lesbar, aber möglicherweise schwierig zu warten. Enums konvertieren relativ leicht von Strings und zurück:

public enum TestEnum
{
    MyFirstEnum,
    MySecondEnum
}

static void TestEnums()
{
    string str = TestEnum.MyFirstEnum.ToString();
    Console.WriteLine( "Enum = {0}", str );
    TestEnum e = (TestEnum)Enum.Parse( typeof( TestEnum ), "MySecondEnum", true );
    Console.WriteLine( "Enum = {0}", e );
}
1
Mark Wilkins

Sie müssen nichts tun, wenn Sie Ints speichern möchten. Mappen Sie Ihre Immobilie einfach in EF . Wenn Sie sie als Strings speichern möchten, verwenden Sie den Konverter.

Int (Db-Typ ist Smallint):

public override void Configure(EntityTypeBuilder<MyEfEntity> b)
{
    ...
    b.Property(x => x.EnumStatus);
}

String (Db-Typ ist Varchar (50)):

public override void Configure(EntityTypeBuilder<MyEfEntity> b)
{
    ...
    b.Property(x => x.EnumStatus).HasConversion<EnumToStringConverter>();
}

Wenn Sie die Datenbankdaten speichern möchten, verwenden Sie smallint als Spalte in db. Daten sind jedoch nicht für Menschen lesbar und Sie sollten einen Index für jedes Enumerationselement festlegen und sich nicht mit ihnen beschäftigen:

public enum EnumStatus
{
    Active = 0, // Never change this index
    Archived = 1, // Never change this index
}

Wenn Sie Daten in db lesbarer machen möchten, können Sie sie als Zeichenfolgen speichern (z. B. varchar (50)). Sie müssen sich keine Sorgen um Indizes machen, und Sie brauchen nur Update-Zeichenfolgen in db, wenn Sie die Namen der Namen ändern. Nachteile: Die Spaltengröße verteuert die Datennutzung. Das bedeutet, wenn Sie eine Tabelle innerhalb von 1.000.000 Zeilen haben, kann dies Auswirkungen auf die Größe und Leistung der Datenbank haben.

Auch als Lösung können Sie kurze Namen verwenden:

public enum EnumStatus
{
    [Display(Name = "Active")]
    Act,
    [Display(Name = "Archived")]
    Arc,
}

Oder verwenden Sie Ihren eigenen Konverter, um Namen in DB kürzer zu machen:

public enum EnumStatus
{
    [Display(Name = "Active", ShortName = "Act")]
    Active,
    [Display(Name = "Archived", ShortName = "Arc")]
    Archived,
}
...
public override void Configure(EntityTypeBuilder<MyEfEntity> b)
{
    ...
    b.Property(x => x.EnumStatus).HasConversion<MyShortEnumsConverter>();
}

Weitere Informationen finden Sie hier: EF: https://docs.Microsoft.com/en-us/ef/ef6/modeling/code-first/data-types/enums EFCore: https://docs.Microsoft.com/de-de/ef/core/modeling/value-conversions

0
ADM-IT

Warum sollten Sie nicht versuchen, die Aufzählungen insgesamt von der DB zu trennen? Ich fand diesen Artikel eine gute Referenz, während ich an etwas Ähnlichem arbeitete:

http://stevesmithblog.com/blog/reducing-sql-lookup-tables-and-function-properties-in-nhibernate/

Die Ideen sollten unabhängig davon, welche Datenbank Sie verwenden, gelten. In MySQL können Sie beispielsweise den Datentyp "enum" verwenden, um die Einhaltung Ihrer codierten Enums durchzusetzen:

http://dev.mysql.com/doc/refman/5.0/de/enum.html

Prost

0
Paul Hanssen

Ein erster DB-Ansatz kann verwendet werden, indem für jede Aufzählung eine konsistente Tabelle erstellt wird, bei der der Name der Id-Spalte dem Tabellennamen entspricht. Es ist vorteilhaft, Aufzählungswerte in der Datenbank zur Verfügung zu haben, um Fremdschlüsseleinschränkungen und benutzerspezifische Spalten in Ansichten zu unterstützen. Wir unterstützen derzeit ~ 100 Aufzählungstypen, die in zahlreichen versionierten Datenbanken verstreut sind. 

Bei einer Code-First-Einstellung könnte die unten dargestellte T4-Strategie möglicherweise umgekehrt werden, um in die Datenbank zu schreiben.

create table SomeSchema.SomeEnumType (
  SomeEnumTypeId smallint NOT NULL primary key,
  Name varchar(100) not null,
  Description nvarchar(1000),
  ModifiedUtc datetime2(7) default(sysutcdatetime()),
  CreatedUtc datetime2(7) default(sysutcdatetime()),
);

Jede Tabelle kann mit einem T4-Template (* .tt) -Skript in C # importiert werden. 

  1. Erstellen Sie ein "Aufzählungsprojekt". Fügen Sie die unten gezeigte .tt-Datei hinzu.
  2. Erstellen Sie einen Unterordner für jeden Datenbankschemanamen. 
  3. Erstellen Sie für jeden Aufzählungstyp eine Datei mit dem Namen SchemaName.TableName.tt. Der Inhalt der Datei Ist immer dieselbe Zeile: <# @ include File = "..\EnumGenerator.ttinclude" #>
  4. Um das Enum zu erstellen/aktualisieren, klicken Sie mit der rechten Maustaste auf eine oder mehrere Dateien und "Benutzerdefiniertes Programm ausführen" (Auto-Update ist noch nicht verfügbar). Es wird eine .cs-Datei zum Projekt hinzufügen/aktualisieren:
using System.CodeDom.Compiler; 
 Namespace TheCompanyNamespace.Enumerations.Config 
 {
 [GeneratedCode ("Auto Enum vom DB Generator", "10")] 
 public enum DatabasePushJobState 
 {
 Nicht definiert = 0, 
 Erstellt = 1, 
 } 
 öffentliche Teilklasse EnumDescription 
 {
 public static string Beschreibung (DatabasePushJobState-Enumeration) 
 {
 string description = "unbekannt"; 
 switch (Aufzählung) 
 {
 case DatabasePushJobState.Undefined: 
 description = "Undefiniert"; 
 brechen;

 case DatabasePushJobState.Created: 
 description = "Erstellt"; 
 brechen; 
 } 
 Beschreibung zurückgeben; 
 } 
 } 
 // Wählen Sie DatabasePushJobStateId, Name, coalesce (Beschreibung, Name) als Beschreibung .__ aus. // aus TheDefaultDatabase. [SchName]. [DatabasePushJobState] 
 // Wobei 1 = 1 Reihenfolge nach DatabasePushJobStateId 
 } 

Und zum Schluss das etwas knifflige T4-Skript (vereinfacht durch zahlreiche Problemumgehungen). Es muss an Ihre Umgebung angepasst werden. Ein Debug-Flag kann Meldungen in das C # ausgeben. Es gibt auch eine Option "Debug T4 Template", wenn Sie mit der rechten Maustaste auf die .tt-Datei klicken. EnumGenerator.ttinclude:

<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".generated.cs" #>
<#@ Assembly Name="EnvDTE" #>
<#@ Assembly Name="System.Core" #>
<#@ Assembly Name="System.Data" #>
<#@ Assembly name="$(TargetPath)" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#  
    bool doDebug = false;   // include debug statements to appear in generated output    

    string schemaTableName = Path.GetFileNameWithoutExtension(Host.TemplateFile);
    string schema = schemaTableName.Split('.')[0];
    string tableName = schemaTableName.Split('.')[1];

    string path = Path.GetDirectoryName(Host.TemplateFile);    
    string enumName = tableName;
    string columnId = enumName + "Id";
    string columnName = "Name"; 
    string columnDescription = "Description";

    string currentVersion = CompanyNamespace.Enumerations.Constants.Constants.DefaultDatabaseVersionSuffix;

    // Determine Database Name using Schema Name
    //
    Dictionary<string, string> schemaToDatabaseNameMap = new Dictionary<string, string> {
        { "Cfg",        "SomeDbName" + currentVersion },
        { "Common",     "SomeOtherDbName" + currentVersion }
        // etc.     
    };

    string databaseName;
    if (!schemaToDatabaseNameMap.TryGetValue(schema, out databaseName))
    {
        databaseName = "TheDefaultDatabase"; // default if not in map
    }

    string connectionString = @"Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=" + databaseName + @";Data Source=Machine\Instance";

    schema = "[" + schema + "]";
    tableName = "[" + tableName + "]";

    string whereConstraint = "1=1";  // adjust if needed for specific tables

  // Get containing project
  IServiceProvider serviceProvider = (IServiceProvider)Host;
  DTE dte = (DTE)serviceProvider.GetService(typeof(DTE));
  Project project = dte.Solution.FindProjectItem(Host.TemplateFile).ContainingProject;
#>
using System;
using System.CodeDom.Compiler;

namespace <#= project.Properties.Item("DefaultNamespace").Value #><#= Path.GetDirectoryName(Host.TemplateFile).Remove(0, Path.GetDirectoryName(project.FileName).Length).Replace("\\", ".") #>
{
    /// <summary>
    /// Auto-generated Enumeration from Source Table <#= databaseName + "." + schema + "." + tableName #>.  Refer to end of file for SQL.
    /// Please do not modify, your changes will be lost!
    /// </summary>
    [GeneratedCode("Auto Enum from DB Generator", "10")]
    public enum <#= enumName #>
    {       
<#
        SqlConnection conn = new SqlConnection(connectionString);
        // Description is optional, uses name if null
        string command = string.Format(
            "select {0}, {1}, coalesce({2},{1}) as {2}" + "\n  from {3}.{4}.{5}\n where {6} order by {0}", 
                columnId,           // 0
                columnName,         // 1
                columnDescription,  // 2
                databaseName,       // 3
                schema,             // 4
                tableName,          // 5
                whereConstraint);   // 6
        #><#= DebugCommand(databaseName, command, doDebug) #><#

        SqlCommand comm = new SqlCommand(command, conn);

        conn.Open();

        SqlDataReader reader = comm.ExecuteReader();
        bool loop = reader.Read();

        while(loop)
        {
#>      /// <summary>
        /// <#= reader[columnDescription] #>
        /// </summary>
        <#= Pascalize(reader[columnName]) #> = <#= reader[columnId] #><# loop = reader.Read(); #><#= loop ? ",\r\n" : string.Empty #>
<#
        }
#>    }


    /// <summary>
    /// A helper class to return the Description for each enumeration value
    /// </summary>
    public partial class EnumDescription
    {
        public static string Description(<#= enumName #> enumeration)
        {
            string description = "Unknown";

            switch (enumeration)
            {<#
    conn.Close();
    conn.Open();
    reader = comm.ExecuteReader();
    loop = reader.Read();

    while(loop)
    {#>                 
                    case <#= enumName #>.<#= Pascalize(reader[columnName]) #>:
                        description = "<#= reader[columnDescription].ToString().Replace("\"", "\\\"") #>";
                        break;
                    <# loop = reader.Read(); #>
<#
      }
      conn.Close();
#> 
            }

            return description;
        }
    }
    /*
        <#= command.Replace("\n", "\r\n        ") #>
    */
}
<#+     
    private string Pascalize(object value)
    {
        Regex rxStartsWithKeyWord = new Regex(@"^[0-9]|^abstract$|^as$|^base$|^bool$|^break$|^byte$|^case$|^catch$|^char$|^checked$|^class$|^const$|^continue$|^decimal$|^default$|^delegate$|^do$|^double$|^else$|^enum$|^event$|^explicit$|^extern$|^$false|^finally$|^fixed$|^float$|^for$|^foreach$|^goto$|^if$|^implicit$|^in$|^int$|^interface$|^internal$|^is$|^lock$|^long$|^namespace$|^new$|^null$|^object$|^operator$|^out$|^overrride$|^params$|^private$|^protected$|^public$|^readonly$|^ref$|^return$|^sbyte$|^sealed$|^short$|^sizeof$|^stackalloc$|^static$|^string$|^struct$|^switch$|^this$|^thorw$|^true$|^try$|^typeof$|^uint$|^ulong$|^unchecked$|^unsafe$|^ushort$|^using$|^virtual$|^volatile$|^void$|^while$", RegexOptions.Compiled);

        Regex rx = new Regex(@"(?:[^a-zA-Z0-9]*)(?<first>[a-zA-Z0-9])(?<reminder>[a-zA-Z0-9]*)(?:[^a-zA-Z0-9]*)");
        string rawName = rx.Replace(value.ToString(), m => m.Groups["first"].ToString().ToUpper() + m.Groups["reminder"].ToString());

        if (rxStartsWithKeyWord.Match(rawName).Success)
            rawName =  "_" + rawName;

        return rawName;    
    }

    private string DebugCommand(string databaseName, string command, bool doDebug)
    {       
        return doDebug
            ? "        // use " + databaseName + ";  " + command + ";\r\n\r\n"
            : "";
    }   
#>

Wir hoffen, dass das Entity-Framework eines Tages eine Kombination dieser Antworten unterstützen wird, um eine starke Typisierung von C # -Enummen in Datensätzen und Datenbankspiegelung von Werten zu ermöglichen. 

0
crokusek