2015-01-05 12 views
8

जब आप कोई कस्टम एकीकृत फ़ंक्शन बनाने आप enumeration format निर्दिष्ट करने की जरूरत है:आईबीरीरीरियललाइज इंटरफेस विधियों के लिए क्या उपयोग किया जाता है?

स्वरूप गणन SqlUserDefinedTypeAttribute और SqlUserDefinedAggregateAttribute द्वारा किया जाता है उपयोगकर्ता-निर्धारित प्रकार (UDT) या कुल का क्रमबद्धता प्रारूप इंगित करने के लिए।

और जब UserDefined प्रारूप प्रयोग किया जाता है, IBinarySerialize Interface को लागू करने और अपने read और write तरीकों ओवरराइड करने के लिए अपनी कक्षा की जरूरत है।

मेरा प्रश्न यह है कि इन तरीकों को वास्तव में क्या करना है?

examples पर देखकर, मुझे लगता है कि वे एकत्रीकरण परिणाम पढ़ने/लिखने में सक्षम होना चाहिए?

उदाहरण के लिए, मैं एक एसक्यूएल सीएलआर फ़ंक्शन बनाने की कोशिश कर रहा हूं जो अलग-अलग संख्याओं को जोड़ता है। टी-एसक्यूएल में मेरे पास 1 से 255 विशिष्ट संख्याएं हो सकती हैं (TINYINT मान)। मुझे उनसे एक स्ट्रिंग बनाने की आवश्यकता है (डेलीमीटर का उपयोग करके), लेकिन संख्याओं को भी क्रमबद्ध करना। समारोह काम करने के लिए लगता है, लेकिन मैं वास्तव में यकीन है कि मैं तरीकों को ओवरराइड अपेक्षा के अनुरूप नहीं हूँ:

[Serializable] 
[ 
    Microsoft.SqlServer.Server.SqlUserDefinedAggregate 
    (
     Microsoft.SqlServer.Server.Format.UserDefined, 
     IsInvariantToNulls = true, 
     IsInvariantToDuplicates = true, 
     IsInvariantToOrder = false, 
     MaxByteSize = 1024 
    ) 
] 
public class ConcatenateAnswersPos : Microsoft.SqlServer.Server.IBinarySerialize 
{ 
    private List<byte> intermediateResult; 

    public void Init() 
    { 
     intermediateResult = new List<byte>(); 
    } 

    public void Accumulate(SqlByte value) 
    { 
     intermediateResult.Add((byte)value); 
    } 

    public void Merge(ConcatenateAnswersPos other) 
    { 
     intermediateResult.AddRange(other.intermediateResult); 
    } 

    public SqlString Terminate() 
    { 
     if (intermediateResult != null) 
     { 
      intermediateResult.Sort(); 
      return new SqlString(string.Join(";", intermediateResult)); 
     } 
     else 
     { 
      return new SqlString(""); 
     } 

    } 

    public void Read(BinaryReader r) 
    { 
     if (r == null) throw new ArgumentNullException("r"); 

     intermediateResult = new List<byte>(); 
     string[] answers = r.ReadString().Split(';'); 

     foreach (string answer in answers) 
     { 
      intermediateResult.Add(Convert.ToByte(answer)); 
     } 
    } 

    public void Write(BinaryWriter w) 
    { 
     if (w == null) throw new ArgumentNullException("w"); 
     intermediateResult.Sort(); 
     w.Write(string.Join(";", intermediateResult)); 
    } 
} 
+1

'लिखित' * का उपयोग करके जो कुछ भी आपने रखा है उसे पूरा करना 'रीड' द्वारा पढ़ा जा सकता है (सामान्य रूप से पाठक एक अंतर्निहित धारा पर बैठेगा जो केवल आपके लिए नहीं है), लेकिन * क्या * वास्तव में आप वास्तव में इस इंटरफ़ेस के क्लाइंट (सामान्य SQL सर्वर कोड में) के लिए अपारदर्शी है। –

+1

मैं 'लिखें' विधि से 'सॉर्ट' लेता हूं। इससे पहले कि डेटा वास्तव में कॉलर्स के लिए * दृश्यमान * होता है, इसे 'रीड' के लिए कॉल द्वारा फिर से संसाधित किया जा रहा है और फिर (संभवतः कुछ 'मर्ज' के बाद) 'प्राप्त करने' के लिए 'टर्मिनेट' करने के लिए एक कॉल होगा वास्तविक परिणाम। –

उत्तर

6

उपयोगकर्ता-परिभाषित कुल (यूडीए) का कोई विशेष उदाहरण क्वेरी के पूरे जीवनकाल में मौजूद होने की गारंटी नहीं है। इसे एक असाधारण प्रतिनिधित्व करने की जरूरत है। जब आप केवल मूल्य प्रकारों का उपयोग करते हैं (जैसा कि प्रश्न में "गणना प्रारूप" लिंक में उल्लिखित है), प्रदत्त Read और Write विधियों को समझते हैं कि यूडीए को क्रमबद्ध और deserialize कैसे करें, इस मामले में आप Format.Native का उपयोग करेंगे। लेकिन जब आप संदर्भ प्रकार (स्ट्रिंग, संग्रह, कस्टम प्रकार इत्यादि) का उपयोग करना शुरू करते हैं, तो यह परिभाषित करने के लिए आप पर निर्भर करता है कि उन मानों को क्रमबद्ध और deserialized कैसे प्राप्त किया जाता है, इस मामले में आपको Format.UserDefined का उपयोग करने की आवश्यकता है और Read और Write विधियों को ओवरराइड करें ताकि आप उन परिचालनों के नियंत्रण में हो सकता है।

मूल्यों को क्रमबद्ध करने की आवश्यकता है जो यूडीए के नए उदाहरण को सटीक पर लाने के लिए आवश्यक है, यह बताता है कि यह धारावाहिक होने से पहले था। इसका मतलब है: इनिट() विधि चलने पर निर्भर न करें (यह प्रति समूह एक बार चलता है!) या परिवर्तनीय प्रारंभकर्ता (वे एक बार प्रतिस्थापन के बाद दौड़ते हैं, और यूडीए को फिर से बनाने के बिना कई समूहों के लिए फिर से उपयोग किया जा सकता है!)। तो आपको सभी मूल मूल्यों को क्रमबद्ध करने की आवश्यकता है, भले ही वे सीधे अंतिम आउटपुट से संबंधित न हों।


जिसके अनुसार, आप, कम से कम, अनुकूलन @ Damien_The_Unbeliever के जवाब में बताया गया है क्या करना चाहिए:

  • Write विधि में तरह मत करो। आप इसे पहले से ही Terminate विधि (उपयुक्त स्थान) में कर रहे हैं, इसलिए यह दो बार करने के लिए बेकार है, बहुत अक्षम नहीं है।

  • स्टोर संग्रह की गिनती और उसके बाद अलग-अलग तत्वों

कि परे:

  • जब आप का कहना है कि अपने UDA "विशिष्ट संख्या concatenates" यदि आप वास्तव में मतलब था "अलग "तो आपको यह देखने के लिए प्रत्येक नंबर की जांच करनी होगी कि यह सूची में है या नहीं। मुझे संदेह है कि यह आपकी इच्छा है क्योंकि आपके पास IsInvariantToDuplicates सत्य पर सेट है। आप दोनों Accumulate विधि में ऐसा होगा:

    if (!intermediateResult.Contains(value.Value)) 
    { 
        intermediateResult.Add(value.Value); 
    } 
    

    और Merge विधि में (कहा जाता है जब समानांतरवाद शामिल है):

    foreach (byte _NewValue in other.intermediateResult) 
    { 
        if (!intermediateResult.Contains(_NewValue)) 
        { 
        intermediateResult.Add(_NewValue); 
        } 
    }  
    

    कृपया ध्यान दें कि मैं अपने कलाकारों बदल गया है - में - (byte)valueValue संपत्ति का उपयोग करने में विधि। SqlTypes (उदा। SqlByte, SqlString, SqlInt32, आदि) में Value संपत्ति है जो आपके द्वारा अपेक्षित .NET प्रकार लौटाती है।इसका मतलब यह है कि पर ToString() पर कॉल करने की आवश्यकता नहीं है क्योंकि बहुत से लोग ऐसा करते हैं।

  • मैं सतर्क साथ 1024 यह चिंता का MaxByteSize आंशिक रूप से लागू करने @ दिया डेमियन के सुझावों को कम किया जा होगा कि बचत "165; 207" एक स्ट्रिंग (वर्तमान पद्धति) में तकनीकी रूप से 14 बाइट्स (7 वर्ण है * 2 बाइट प्रति चार) जबकि गिनती और व्यक्तिगत बाइट्स को सहेजना केवल 6 बाइट्स है (count + 2 व्यक्तिगत बाइट्स को स्टोर करने के लिए Int32 के लिए 4 बाइट्स)। और यह असमानता सिर्फ 2 मानों को संग्रहित करने के लिए है। 200 भंडारण? Yeesh!

  • आपके पास IsNullIfEmpty संपत्ति निर्दिष्ट नहीं है। आपको यह निर्दिष्ट करने की आवश्यकता है, विशेष रूप से यह मानते हुए कि Terminate विधि एक खाली स्ट्रिंग लौट रही है यदि आंतरिक संग्रह null है। आपको IsNullIfEmpty = false जोड़ना चाहिए क्योंकि आप NULL वापस नहीं करना चाहते हैं अगर इसे कभी नहीं कहा जाता है।

  • में अतिरिक्त तर्क null संग्रह को संभालने के लिए संभवतः आवश्यक नहीं है। संग्रह Init और Read विधियों में प्रारंभ किया गया है, इसलिए मुझे यकीन नहीं है कि Terminate के समय तक यह null कैसे हो सकता है।


आप Format.UserDefined के साथ एक उपयोगकर्ता निर्धारित सकल बनाने का एक उदाहरण है, तो Getting The Most Out of SQL Server 2005 UDTs and UDAs पर एक नज़र डालें चाहते हैं (नि: शुल्क पंजीकरण आवश्यक)। मैंने लिखा है कि एसक्यूएल सर्वर 2008 से पहले बाहर निकलने के लिए 8000 से अधिक बाइट्स को क्रमबद्ध करने की इजाजत दी गई है, ताकि आप धारावाहिक करने के लिए डेटा को संपीड़ित करने के पहलुओं (पल के लिए) अनदेखा कर सकें।

इसके अलावा, यदि आप सामान्य रूप से SQLCLR के बारे में अधिक जानना चाहते हैं, तो मैं SQL सर्वर सेंट्रल: Stairway to SQLCLR (पहले लिंक किए गए आलेख के समान साइट) के लिए एक श्रृंखला लिख ​​रहा हूं।

+1

मैंने पहले ही आपके लेख पढ़ और वोट कर दिए हैं और मैं बाकी श्रृंखला की प्रतीक्षा कर रहा हूं। तो, 'मूल्य' संपत्ति का उपयोग तेजी से है? उदाहरण के लिए, हमेशा '(बाइट) मान' के बजाय' value.Value' का उपयोग करने के लिए। अन्य युक्तियों के लिए भी धन्यवाद। अगले लेख की प्रतीक्षा कर रहा है :-) – gotqn

+0

@gotqn धन्यवाद :)। सुनिश्चित नहीं है कि 'Value' का उपयोग करना आवश्यक है _faster_, लेकिन निश्चित रूप से अधिक संगत और भरोसेमंद। –

3

आपका कुल धारावाहिक और पंक्तियों यह पर चल रही है प्रसंस्करण के माध्यम से आधे रास्ते है, जबकि इसे नष्ट कर दिया जा सकता है। डेटाबेस इंजन तब एक नया उदाहरण बना सकता है और इसे छोड़कर वापस जाने के लिए deserialize कर सकते हैं।

जैसे Write विधि को कुल मिलाकर राज्य की स्थिति को स्टोर करने में सक्षम होने की आवश्यकता होती है जब केवल कुछ रिकॉर्ड Accumulate पर पास किए जाते हैं। Read विधि Accumulate या Merge पर अधिक कॉल के लिए कुल बैक अप तैयार करने में सक्षम होने की आवश्यकता है।

जैसा कि मैं कहूंगा कि आपने इन्हें सही तरीके से कार्यान्वित किया है।

4

मैं कहूंगा कि आप अपनी विधियों में अधिक काम कर रहे हैं। आपको बस इतना करना है कि Write विधि में पर्याप्त लिखें ताकि आपकी Read विधि आपके आंतरिक स्थिति का पुनर्निर्माण कर सके। के बाद से अपने आंतरिक स्थिति सिर्फ एक List<byte> है, वहाँ तारों के रूप में सब कुछ का इलाज करने की कोई जरूरत:

public void Read(BinaryReader r) 
{ 
    if (r == null) throw new ArgumentNullException("r"); 

    var count= r.ReadInt32(); 

    intermediateResult = new List<byte>(count); 
    for (int i=0;i<count;i++) 
    { 
     intermediateResult.Add(r.ReadByte()); 
    } 
} 

public void Write(BinaryWriter w) 
{ 
    if (w == null) throw new ArgumentNullException("w"); 
    w.Write(intermediateResult.Count); 
    foreach(byte b in intermediateResult) 
    { 
     w.Write(b); 
    } 
} 

और जैसा कि मैं टिप्पणी में सुझाव दिया, मैं भी Write विधि से Sort हटा दिया है के बाद से वहाँ हमेशा हो जाएगा आपके द्वारा बनाए गए डेटा से पहले Terminate में अंतिम Sort कॉल को आपके कुल उपभोक्ताओं को पास कर दिया गया है।


हम डेटा में Count पहले स्टोर इतना है कि हम जानते हैं कि कितनी बार Read विधि में ReadByte कॉल करने के लिए। यह एक (संभवतः व्यर्थ) अनुकूलन की अनुमति देता है जिसे हम List<byte> कन्स्ट्रक्टर को बता सकते हैं कि इसके लिए कितनी वस्तुओं की आवश्यकता है।

+1

संग्रह को बेहतर तरीके से स्टोर करने के तरीके के बारे में उत्कृष्ट पकड़/सिफारिश। मैंने इस जवाब को मेरा श्रेय दिया। +1 –

1

IBianarySerialize विधियां आपकी ऑब्जेक्ट को सहेजने और डिस्क पर लिखे जाने की आवश्यकता होने पर इसे बहाल करने के लिए हैं।

जैसे, Write विधि (डेटा) अपनी वर्तमान स्थिति में वस्तु को पुन: करने के लिए आवश्यक सब कुछ को बचाने चाहिए और Read विधि क्या लिखें द्वारा उत्पादन था लेने के लिए और वस्तु की राज्य स्थापित करना चाहिए (ताकि वह मूल से मेल खाता है)।

अन्य उत्तरों इस प्रक्रिया के साथ मुद्दों को हल करने में बहुत अच्छे लगते हैं, हालांकि मैं जितना संभव हो सके छोटे और तेज़ तरीके से इन विधियों का उपयोग करके पढ़ने/लिखने वाले डेटा को रखने की सलाह देता हूं।

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