2010-01-26 10 views
27

सबसे पहले, मुझे वर्तमान स्थिति की व्याख्या करने दें: मैं डेटाबेस से रिकॉर्ड पढ़ रहा हूं और उन्हें बाद में उपयोग के लिए किसी ऑब्जेक्ट में डाल रहा हूं; आज डेटाबेस प्रकार के बारे में एक सवाल सी # प्रकार रूपांतरण (कास्टिंग?) उठ गया।कैसे (कुशलतापूर्वक) कन्वर्ट (कास्ट?) एक SqlDataReader फ़ील्ड को इसके संबंधित सी # प्रकार में कैसे करें?

एक उदाहरण देखते हैं:

namespace Test 
{ 
    using System; 
    using System.Data; 
    using System.Data.SqlClient; 

    public enum MyEnum 
    { 
     FirstValue = 1, 
     SecondValue = 2 
    } 

    public class MyObject 
    { 
     private String field_a; 
     private Byte field_b; 
     private MyEnum field_c; 

     public MyObject(Int32 object_id) 
     { 
      using (SqlConnection connection = new SqlConnection("connection_string")) 
      { 
       connection.Open(); 

       using (SqlCommand command = connection.CreateCommand()) 
       { 
        command.CommandText = "sql_query"; 

        using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SingleRow)) 
        { 
         reader.Read(); 

         this.field_a = reader["field_a"]; 
         this.field_b = reader["field_b"]; 
         this.field_c = reader["field_c"]; 
        } 
       } 
      } 
     } 
    } 
} 

यह (जाहिर है) में नाकाम रहने की वजह से तीन this.field_x = reader["field_x"]; कॉल Cannot implicitly convert type 'object' to 'xxx'. An explicit conversion exists (are you missing a cast?). संकलक त्रुटि फेंक रहे है।

सही करने के लिए इस मैं वर्तमान में दो तरीके (के field_b उदाहरण का उपयोग करते हैं) का पता: नंबर एक this.field_b = (Byte) reader["field_b"]; है और नंबर दो this.field_b = Convert.ToByte(reader["field_b"]); है।

विकल्प नंबर एक के साथ समस्या यह है कि

DBNull क्षेत्रों अपवाद फेंक रहे हैं के रूप में कास्ट (यहां तक ​​कि String के रूप में नल प्रकार के साथ), दो नंबर के साथ समस्या यह चींटी विफल हो रहा है कि यह शून्य मान संरक्षण नहीं कर रहा है है (Convert.ToString(DBNull) पैदावार एक String.Empty), और मैं उन्हें enums के साथ भी उपयोग नहीं कर सकता।

तो, इंटरनेट पर लुकअप के एक जोड़े के बाद और यहाँ StackOverflow, क्या मैं के साथ आया था पर है:

public static class Utilities 
{ 
    public static T FromDatabase<T>(Object value) where T: IConvertible 
    { 
     if (typeof(T).IsEnum == false) 
     { 
      if (value == null || Convert.IsDBNull(value) == true) 
      { 
       return default(T); 
      } 
      else 
      { 
       return (T) Convert.ChangeType(value, typeof(T)); 
      } 
     } 
     else 
     { 
      if (Enum.IsDefined(typeof(T), value) == false) 
      { 
       throw new ArgumentOutOfRangeException(); 
      } 

      return (T) Enum.ToObject(typeof(T), value); 
     } 
    } 
} 

इस तरह से मैं हर मामले को संभाल चाहिए।

प्रश्न है: क्या मुझे कुछ याद आ रही है? क्या मैं एक WOMBAT कर रहा हूं (पैसा, मस्तिष्क और समय का अपशिष्ट) क्योंकि ऐसा करने के लिए एक तेज और साफ तरीका है? यह सब सही है? फायदा?

+3

यह सुंदर व्यापक और मेरे लिए सामान्य लग रहा है। SqlDataReader क्लास में कुछ .GetInt32(), .GetBytes() प्रकार परिवर्तित करने के लिए प्रकार हैं जो आपके लिए कुछ ऐसा करेंगे, लेकिन मुझे लगता है कि आपको अभी भी नल की जांच करने की आवश्यकता है। मैं LINQ या ORM में भी देखता हूं, वे आपके लिए इस तरह के विवरण का ख्याल रखते हैं। –

+0

डेटा रीडर के विभिन्न GetXXX विधियों को देखें। शायद वे वही हैं जो आप खोज रहे हैं। –

+0

आपने *** FromDatabase *** का उपयोग करने का प्रयास किया था? *** int, int ?, स्ट्रिंग, डेटटाइम का उपयोग करके इसके बारे में अंतिम सरल ?, एनम *** मान? – Kiquenet

उत्तर

36

के लिए एक कॉल में एक्सेसर रैप करने के लिए एक क्षेत्र nulls अनुमति देता है, नियमित रूप से आदिम प्रकार का उपयोग नहीं करते है। C# nullable type और as keyword का उपयोग करें।

int? field_a = reader["field_a"] as int?; 
string field_b = reader["field_a"] as string; 

किसी भी गैर-व्यर्थ सी # प्रकार के लिए एक ? जोड़ा जा रहा है यह "नल" बनाता है। as कीवर्ड का उपयोग किसी ऑब्जेक्ट को निर्दिष्ट प्रकार पर डालने का प्रयास करेगा। यदि कास्ट विफल रहता है (जैसे कि यह प्रकार DBNull है), तो ऑपरेटर null देता है।

नोट:as उपयोग का एक और छोटा सा लाभ यह है कि यह सामान्य कास्टिंग से slightly faster है। चूंकि इसमें कुछ डाउनसाइड्स भी हो सकते हैं, जैसे कि यदि आप गलत प्रकार के रूप में डालने का प्रयास करते हैं तो बग को ट्रैक करना कठिन होता है, इसे परंपरागत कास्टिंग पर हमेशा as का उपयोग करने का कारण नहीं माना जाना चाहिए। नियमित कास्टिंग पहले से ही काफी सस्ते ऑपरेशन है।

+0

टाइप 'System.DBNull' को 'int' में परिवर्तित नहीं किया जा सकता है?'एक संदर्भ रूपांतरण के माध्यम से, मुक्केबाजी रूपांतरण, अनबॉक्सिंग रूपांतरण, रैपिंग रूपांतरण, या शून्य प्रकार रूपांतरण। –

+0

क्या आपने उपरोक्त कोड को आजमाया है? यह काम करेगा। आपने जो कहा वह बिल्कुल सही है और यही कारण है कि मेरा उत्तर काम करता है: http://msdn.microsoft.com/en-us/library/cscsdfbt.aspx –

+0

मेरी गलती। मैं '(ऑब्जेक्ट) DBNull के बजाय' DBNull.Value int? 'का परीक्षण कर रहा था। Int के रूप में वैल्यू? '। –

12

क्या आप reader.Get* methods का उपयोग नहीं करना चाहते हैं? केवल कष्टप्रद बात यह है कि वे स्तंभ संख्या ले ताकि आप GetOrdinal()

using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SingleRow)) 
{ 
    reader.Read(); 

    this.field_a = reader.GetString(reader.GetOrdinal("field_a")); 
    this.field_a = reader.GetDouble(reader.GetOrdinal("field_b")); 
    //etc 
} 
+1

मैंने हमेशा 'reader.GetX' विधियों से परहेज किया क्योंकि आपको उन्हें कॉलम का नंबर पास करना होगा, और क्योंकि यह "खराब" है (क्या होगा - उदाहरण के लिए - अंतर्निहित संग्रहीत प्रक्रिया कुछ अतिरिक्त कॉलम प्राप्त करती है? भले ही आपको परवाह न हो वे कोड को पेंच करेंगे।) और मुझे 'पाठक.गेटऑर्डिनल' विधि से अवगत नहीं था, मैंने उन्हें कभी भी नहीं माना। – Albireo

+0

मुझे लगता है कि आप शायद एक विस्तार विधि के माध्यम से अधिभार की एक श्रृंखला जोड़ सकते हैं जो आपको कॉलम नाम में पाठक को पास करने की अनुमति देगा और विभिन्न प्रकारों को –

+0

पर वापस ले जाएगा, मुझे नहीं पता कि यह ओपी की मदद से कितना है। ओपी पहले से ही अपने कोड में 'पाठक ["field_a"] 'का उपयोग करता है। यह वास्तविक प्रकार के लिए कास्टिंग का मामला है। 'GetOrdinal' वैसे भी स्ट्रिंग लुकअप को सामान्य प्राप्त करने के लिए करता है, यदि आप यही से बचने की कोशिश कर रहे हैं। यदि आप GetOrdinal का उपयोग कर रहे हैं तो आपको इसे कैश करना होगा: http://stackoverflow.com/questions/1079366/why-use-the-getordinal-method-of-the-sqldatareader – nawfal

3

आप विस्तार तरीकों का एक सेट कर सकते हैं, प्रति डेटा प्रकार एक जोड़ी:

public static int? GetNullableInt32(this IDataRecord dr, string fieldName) 
    { 
     return GetNullableInt32(dr, dr.GetOrdinal(fieldName)); 
    } 

    public static int? GetNullableInt32(this IDataRecord dr, int ordinal) 
    { 
     return dr.IsDBNull(ordinal) ? null : (int?)dr.GetInt32(ordinal); 
    } 

इसमें कुछ समय लागू करने के लिए कठिन हो जाता है, लेकिन यह काफी कुशल है। System.Data.DataSetExtensions में।डीएलएल, माइक्रोसॉफ्ट ने Field<T> method के साथ डेटासेट्स के लिए एक ही समस्या हल की, जो आम तौर पर कई डेटा प्रकारों को संभालती है, और डीबीएनयूएल को एक नालीबल में बदल सकती है।

एक प्रयोग के रूप में, मैंने एक बार डेटा रीडर के लिए एक समकक्ष विधि लागू की, लेकिन मैं वास्तविक प्रकार रूपांतरणों को कुशलतापूर्वक करने के लिए डेटासेट एक्सटेंशन (अनबॉक्स) से एक आंतरिक वर्ग उधार लेने के लिए परावर्तक का उपयोग करके समाप्त हुआ। मैं उधार वर्ग को वितरित करने की वैधता के बारे में निश्चित नहीं हूं, इसलिए मुझे शायद कोड साझा नहीं करना चाहिए, लेकिन खुद को देखना बहुत आसान है।

6

इस तरह मैंने पहले भी यह साथ पेश किया है:

public Nullable<T> GetNullableField<T>(this SqlDataReader reader, Int32 ordinal) where T : struct 
    { 
     var item = reader[ordinal]; 

     if (item == null) 
     { 
      return null; 
     } 

     if (item == DBNull.Value) 
     { 
      return null; 
     } 

     try 
     { 
      return (T)item; 
     } 
     catch (InvalidCastException ice) 
     { 
      throw new InvalidCastException("Data type of Database field does not match the IndexEntry type.", ice); 
     } 
    } 

उपयोग:

int? myInt = reader.GetNullableField<int>(reader.GetOrdinal("myIntField")); 
2

सामान्य hanlding कोड यहां पोस्ट अच्छा है, लेकिन जब से सवाल शीर्षक शब्द शामिल 'कुशलतापूर्वक' मैं अपनी कम जेनेरिक पोस्ट करूंगा लेकिन (मुझे उम्मीद है) अधिक कुशल जवाब।

मेरा सुझाव है कि आप उन XXXXX विधियों का उपयोग करें जो दूसरों ने उल्लेख किया है। स्तंभ संख्या समस्या यह है कि के बारे में बातचीत बिहॉप के साथ सौदा करने के लिए, मैं इस तरह एक enum का उपयोग करें,:

enum ReaderFields { Id, Name, PhoneNumber, ... } 
int id = sqlDataReader.getInt32((int)readerFields.Id) 

यह थोड़ा अतिरिक्त टाइपिंग है, लेकिन उसके बाद आप प्रत्येक स्तंभ के लिए सूचकांक को खोजने के लिए GetOrdinal कॉल करने के लिए की जरूरत नहीं है । और, कॉलम नामों के बारे में चिंता करने की बजाय, आप कॉलम की स्थिति के बारे में चिंता करते हैं।

नल स्तंभों के साथ सौदा करने के लिए, आप DBNull के लिए जाँच करने के लिए है, और शायद एक डिफ़ॉल्ट मान प्रदान की जरूरत है:

string phoneNumber; 
if (Convert.IsDBNull(sqlDataReader[(int)readerFields.PhoneNumber]) { 
    phoneNumber = string.Empty; 
} 
else { 
    phoneNumber = sqlDataReader.getString((int)readerFields.PhoneNumber); 
} 
+0

यह समस्या को हल नहीं कर रहा है जो मुझे 'reader.GetX' विधियों से दूर रख रहा था: यदि अंतर्निहित रिकॉर्डसेट लेआउट बदलता है, खासकर अगर पुराने लोगों के बीच अतिरिक्त कॉलम लौटाते हैं, तो कोड खराब हो जाएगा, भले ही आप नहीं पड़े नए कॉलम के बारे में परवाह करें, क्योंकि उनके क्रमिक संख्या में परिवर्तन होता है, और आपका enum नहीं करता है। – Albireo

+0

यदि आपका रिकॉर्डसेट बदलता है, तो इससे कोई फर्क नहीं पड़ता कि आप किस योजना का उपयोग करते हैं। दान के स्वीकृत उत्तर में, यदि फ़ील्ड नाम बदलते हैं, तो कोड टूटा हुआ है। दोबारा, मैंने इस जवाब को पोस्ट करने का कारण यह था क्योंकि आपने दक्षता का उल्लेख किया था। नामों से फ़ील्ड तक पहुंचने से परिणामों में सभी फ़ील्ड के माध्यम से आंतरिक रूप से 'फॉर' लूप का उपयोग होता है जब तक मिलान वाला कोई नहीं मिलता है। यह प्रत्येक पंक्ति में प्रत्येक फ़ील्ड एक्सेस के लिए किया जाता है। तो, कहें, 20 फ़ील्ड और 50 रिकॉर्ड लौटे - आपके पास 1,000 'लूप' हैं। इसके अलावा 1,000 प्रकार के कास्ट और 1,000 नालीबल प्रकार (जो मानक प्रकारों से थोड़ा बड़ा होते हैं)। – Ray

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