2010-04-15 25 views
46

सी # और विजुअल स्टूडियो और माईएसQL डेटा कनेक्टर का उपयोग कर डेटाबेस में एनम स्टोर करने का सबसे अच्छा तरीका क्या है।डाटाबेस में एनम स्टोर करने के लिए सबसे अच्छी विधि

मैं 100 से अधिक Enums के साथ एक नई परियोजना बनाने जा रहा हूं, और उनमें से अधिकतर डेटाबेस में संग्रहीत किया जाना होगा। प्रत्येक के लिए कन्वर्टर्स बनाना एक लंबी हवादार प्रक्रिया होगी इसलिए मैं सोच रहा हूं कि दृश्य स्टूडियो या किसी के पास इसके लिए कोई तरीका है जिसे मैंने नहीं सुना है।

+0

+1 मुझे इस राय में दिलचस्पी है कि इस तरह की चीज को अलग-अलग तालिकाओं और एफके बाधा के साथ लागू किया जाना चाहिए या किसी के पास कोई नियमित बाधा है? –

+2

@ मार्टिन उत्तर देने की योजना नहीं बना रहा क्योंकि यह काफी व्यक्तिपरक होगा, लेकिन ओएलटीपी/ओडीएस के लिए मैं एफके बाधा के साथ अलग-अलग तालिकाओं का उपयोग करूंगा। एक डीएसएस रिपोर्टिंग समाधान के लिए, मैं अन्य तथ्यों के साथ रिपोर्टिंग तालिका में enum के प्रतीकात्मक नाम (या विवरण) को denormalize और स्टोर करेगा। –

उत्तर

42
[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 
} 

एक आकर्षण की तरह काम करता है! कोड में int (En) Enum या (Enum) int को बदलने की आवश्यकता नहीं है। बस enum और ef code का उपयोग करें, पहले आपके लिए int को बचाएगा। पीएस .: "[EnumDataType (टाइपऑफ (फोनटाइप))]" विशेषता आवश्यक नहीं है, अगर आप अतिरिक्त कार्यक्षमता चाहते हैं तो बस एक अतिरिक्त।

वैकल्पिक रूप से आप कर सकते हैं:

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

अंत में आपको enum converters जैसे दोहराए गए कोडिंग कार्यों से निपटने के लिए एक शानदार तरीका चाहिए। आप कोड जनरेटर जैसे MyGeneration या CodeSmith कई अन्य लोगों के बीच या शायद ORM mapper like nHibernate का उपयोग कर सकते हैं ताकि आपके लिए सबकुछ संभाला जा सके।

संरचना के लिए के रूप में ... के साथ enums मैं पहली बार कुछ इस तरह लग सकता है कि एक ही तालिका में डेटा को व्यवस्थित करने की कोशिश कर रहा पर विचार करेंगे के सैकड़ों: (छद्म एसक्यूएल)

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

है कि आप की अनुमति होगी अपनी enum जानकारी को एक टेबल में स्टोर करने के लिए। EnumType भी एक टेबल के लिए एक विदेशी कुंजी हो सकता है जो विभिन्न enums परिभाषित करता है।

आपकी बिज़ ऑब्जेक्ट्स इस तालिका से EnumId के माध्यम से जुड़ी होंगी। एनम प्रकार केवल यूआई में संगठन और फ़िल्टरिंग के लिए है। इस सब का उपयोग करना आपके कोड संरचना और समस्या डोमेन पर निर्भर करता है।

बीटीडब्ल्यू, इस परिदृश्य में आप PKey पर बनाए गए डिफ़ॉल्ट क्लस्टर आईडीएक्स को छोड़ने के बजाय EnumType पर क्लस्टर्ड इंडेक्स सेट करना चाहते हैं।

+0

enums संग्रहीत करने का दिलचस्प तरीका। लेकिन मुख्य कारण मैं एनम्स (सी # आधारित) का उपयोग क्यों करना चाहता हूं, पठनीयता के लिए है, उदाहरण के लिए। अगर (कुछ। टाइप == Enum.EnumType) यह प्रोजेक्ट एक बहुत लंबी और जटिल परियोजनाएं होगी और 100 के enum के साथ ट्रैक रखना और डेटाबेस में हर बार वापस जाना मुश्किल होगा। –

10

हम इन्हें स्याही या लम्बाई के रूप में स्टोर करते हैं और फिर हम इसे आगे और आगे निकाल सकते हैं। शायद सबसे मजबूत समाधान नहीं, लेकिन हम जो करते हैं।

हम टाइप डेटासेट का उपयोग कर रहे हैं, इसलिए, उदाहरण के लिए:

eunum BlockTreatmentType 
{ 
    All = 0 
}; 

// blockTreatmentType is an int property 
blockRow.blockTreatmentType = (int)BlockTreatmentType.All; 
BlockTreatmentType btt = (BlockTreatmentType)blockRow.blocktreatmenttype; 
+0

आप कैसे महारत हासिल करते हैं? और आप क्या स्टोर करते हैं? क्रमिक मूल्य, या एक कस्टम विशेषता? –

+0

ने एक कोड स्निपेट –

+2

जोड़ा है यह स्वीट एक स्वीकार्य समाधान की तरह है। मैं थोड़ी देर के लिए खुला प्रश्न छोड़ दूंगा कि लोग क्या देखते हैं। मुझे लगता है कि आप आदेश/कार्डिनल बदलने से रोकने के लिए मैन्युअल रूप से enum का int मान निर्धारित करते हैं? –

3

कुछ बातें आप को ध्यान में रखना चाहिए।

क्या गणना कॉलम अन्य अनुप्रयोगों द्वारा सीधे उपयोग की जा रही है जैसे उदाहरण रिपोर्ट। यह गणना के पूर्णांक प्रारूप में संग्रहीत होने की संभावना को सीमित कर देगा क्योंकि जब तक रिपोर्ट में कस्टम तर्क नहीं होता है तब तक उस मान में कोई अर्थ नहीं होगा।

आपके आवेदन के लिए i18n आवश्यकताएं क्या हैं? यदि यह केवल एक भाषा का समर्थन करता है तो आप गणना के रूप में गणना को सहेज सकते हैं और विवरण स्ट्रिंग से कनवर्ट करने के लिए एक सहायक विधि बना सकते हैं। आप इसके लिए [DescriptionAttribute] का उपयोग कर सकते हैं और रूपांतरण के तरीकों को शायद एसओ खोजकर पाया जा सकता है।

यदि दूसरी तरफ आपको अपने डेटा पर एकाधिक भाषा और बाहरी एप्लिकेशन तक पहुंच का समर्थन करने की आवश्यकता है तो आप विचार करना शुरू कर सकते हैं कि गणना वास्तव में उत्तर है या नहीं। लुकअप टेबल जैसे अन्य विकल्प पर विचार किया जा सकता है यदि परिदृश्य अधिक जटिल है।

गणनाएं उत्कृष्ट होती हैं जब वे कोड में स्वयं निहित होते हैं ... जब वे उस सीमा को पार करते हैं, तो चीजें थोड़ा गन्दा हो जाती हैं।


अद्यतन:

आप एक पूर्णांक Enum.ToObject विधि का उपयोग करने से बदल सकते हैं। इसका मतलब यह है कि कनवर्ट करते समय आप गणना के प्रकार को जानते हैं।यदि आप इसे पूरी तरह सामान्य बनाना चाहते हैं तो आपको डेटाबेस में इसके मूल्य के साथ गणना के प्रकार को स्टोर करने की आवश्यकता है। आप डेटा कॉलम समर्थन तालिका बना सकते हैं ताकि आपको यह बताने के लिए कि कौन से कॉलम गणनाएं हैं और वे किस प्रकार हैं।

+0

आदर्श रूप से रिपोर्ट उसी लाइब्रेरी का उपयोग करके बनाई जाएगी जो enums रखती है, इसलिए डेटाबेस में पूर्णांक संग्रह करना बिल्कुल ठीक है। एकमात्र समस्या यह है कि, जब आप डेटाबेस से निकाले जाते हैं तो आप मूल enum में कैसे परिवर्तित करते हैं? (5+ मानों वाले प्रत्येक के 100+ enms) –

1

मुझे यकीन नहीं है कि यह सबसे लचीला है, लेकिन आप बस उनके स्ट्रिंग संस्करणों को स्टोर कर सकते हैं। यह निश्चित रूप से पठनीय है, लेकिन शायद बनाए रखना मुश्किल है। Enums तार से बदलने और वापस सुंदर आसानी से:

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

आप अपने enums सभी मान की एक दुकान है, तो आप enums और उनके सदस्यों स्टोर करने के लिए नीचे दी गई तालिकाओं कोशिश कर सकते हैं चाहते हैं, और उन मूल्यों को जोड़ने के लिए कोड का टुकड़ा। मैं केवल इंस्टॉलेशन समय पर ऐसा ही करूंगा, हालांकि, जब तक आप पुन: संकलित नहीं करेंगे तब तक वे मान कभी नहीं बदलेंगे!

डीबी तालिका:

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 

सी # स्निपेट:

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

क्यों डीबी से पूरी तरह अलग करने enums नहीं की कोशिश?

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

उस में विचारों क्या DB आप उपयोग की परवाह किए बिना लागू करना चाहिए: मैं थोड़ी देर के समान कुछ पर काम कर एक महान संदर्भ होने के लिए इस लेख पाया। उदाहरण के लिए, MySQL में आप अपने कोडित enums के साथ अनुपालन लागू करने "enum" के डेटा प्रकार का उपयोग कर सकते हैं:

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

चीयर्स

2

आप enum क्षेत्र के डीबी स्ट्रिंग मूल्यों में स्टोर, बेहतर नीचे शो पसंद करते हैं की जरूरत है। उदाहरण के लिए, यदि आप SQLite का उपयोग कर रहे हैं, तो इसकी आवश्यकता हो सकती है, जो enum फ़ील्ड का समर्थन नहीं करता है।

[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 
} 
+1

निर्भर करते हुए कि आप इन्हें कितनी बार कनवर्ट करते हैं (उदाहरण के लिए 50k ऑब्जेक्ट्स) आप टॉस्ट्रिंग और एनम के रूप में एक स्विच विधि के साथ कनवर्टर/एक्सटेंशन विधियों के बेहतर होते हैं। पर्स प्रतिबिंब का उपयोग करता है। बस एफवाईआई –

+0

हां। मैं सहमत हूँ। लेकिन एक छोटे से लोड के लिए आसान समाधान उपयुक्त है। – trueboroda

0

एक डीबी पहले दृष्टिकोण प्रत्येक enum जहां ईद स्तंभ नाम से मेल खाता तालिका नाम के लिए एक सुसंगत तालिका बनाने के द्वारा इस्तेमाल किया जा सकता। विदेशी कुंजी बाधाओं और विचारों में अनुकूल कॉलम का समर्थन करने के लिए डेटाबेस के भीतर enum मान उपलब्ध कराने के लिए फायदेमंद है। वर्तमान में हम कई संस्करण वाले डेटाबेसों में बिखरे हुए ~ 100 enum प्रकारों का समर्थन कर रहे हैं।

कोड-प्रथम वरीयता के लिए, नीचे दिखाए गए टी 4 रणनीति को शायद डेटाबेस में लिखने के लिए उलट दिया जा सकता है।

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

प्रत्येक तालिका को T4 template (*.tt) script का उपयोग करके सी # में आयात किया जा सकता है।

  1. "गणना परियोजना" बनाएं। नीचे दिखाए गए .tt फ़ाइल जोड़ें।
  2. प्रत्येक डेटाबेस स्कीमा नाम के लिए उप-फ़ोल्डर बनाएं।
  3. प्रत्येक enum प्रकार के लिए, एक फ़ाइल बनाएं जिसका नाम SchemaName.TableName.tt है। फ़ाइल सामग्री हमेशा एक ही एकल लाइन कर रहे हैं: < # @ फ़ाइल को शामिल = ".. \ EnumGenerator.ttinclude" #>
  4. फिर बनाने के लिए/enums अद्यतन करते हैं, सही 1 या अधिक फ़ाइलों पर क्लिक करें और "रन कस्टम टूल "(हमारे पास अभी तक ऑटो अपडेट नहीं है)। यह परियोजना के लिए जोड़ देगा/अद्यतन एक .cs फ़ाइल:
using System.CodeDom.Compiler; 
namespace TheCompanyNamespace.Enumerations.Config 
{ 
    [GeneratedCode("Auto Enum from DB Generator", "10")] 
    public enum DatabasePushJobState 
    {  
      Undefined = 0, 
      Created = 1,   
    } 
    public partial class EnumDescription 
    { 
     public static string Description(DatabasePushJobState enumeration) 
     { 
      string description = "Unknown"; 
      switch (enumeration) 
      {     
       case DatabasePushJobState.Undefined: 
        description = "Undefined"; 
        break; 

       case DatabasePushJobState.Created: 
        description = "Created"; 
        break;     
      } 
      return description; 
     } 
    } 
    // select DatabasePushJobStateId, Name, coalesce(Description,Name) as Description 
    // from TheDefaultDatabase.[SchName].[DatabasePushJobState] 
    // where 1=1 order by DatabasePushJobStateId 
} 

और अंत में, कुछ हद तक ऐंठा हुआ टी -4 लिपि (कई समाधानों से सरलीकृत)। इसे आपके पर्यावरण में अनुकूलित करने की आवश्यकता होगी। एक डीबग ध्वज सी # में संदेशों को आउटपुट कर सकता है। .tt फ़ाइल पर राइट क्लिक करते समय "डीबग टी 4 टेम्पलेट" विकल्प भी है। 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" 
      : ""; 
    } 
#> 

उम्मीद है कि इकाई की रूपरेखा किसी दिन इन उत्तरों का एक संयोजन रिकॉर्ड और डेटाबेस मूल्यों की मिररिंग भीतर सी # enum मजबूत टाइपिंग की पेशकश करने का समर्थन करेंगे।

संबंधित मुद्दे

 संबंधित मुद्दे