2012-02-21 6 views
11

समतुल्य है, मैं अपने डेटाबेस और मेरे सी # कोड के बीच एक कैशिंग परत लागू कर रहा हूं। विचार क्वेरी के पैरामीटर के आधार पर कुछ डीबी प्रश्नों के परिणामों को कैश करना है। डेटाबेस डिफ़ॉल्ट collation का उपयोग कर रहा है - या तो SQL_Latin1_General_CP1_CI_AS या Latin1_General_CI_AS, जो मुझे विश्वास है कि कुछ संक्षिप्त googling समानता के बराबर हैं, सॉर्टिंग के लिए बस अलग है।क्या .NET StringComparer SQL के लैटिन 1_General_CI_AS

मुझे एक .NET स्ट्रिंग कॉम्पैयर की आवश्यकता है जो कम से कम समानता परीक्षण और हैशकोड पीढ़ी के लिए समान व्यवहार दे सकता है, क्योंकि डेटाबेस का संयोजन उपयोग कर रहा है। लक्ष्य यह निर्धारित करने के लिए कि कोई विशेष स्ट्रिंग कुंजी पहले से ही कैश में है या नहीं, यह निर्धारित करने के लिए C# कोड में .NET शब्दकोश में StringComparer का उपयोग करने में सक्षम होना है।

वास्तव में एक सरल उदाहरण:

var comparer = StringComparer.??? // What goes here? 

private static Dictionary<string, MyObject> cache = 
    new Dictionary<string, MyObject>(comparer); 

public static MyObject GetObject(string key) { 
    if (cache.ContainsKey(key)) { 
     return cache[key].Clone(); 
    } else { 
     // invoke SQL "select * from mytable where mykey = @mykey" 
     // with parameter @mykey set to key 
     MyObject result = // object constructed from the sql result 
     cache[key] = result; 
     return result.Clone(); 
    } 
} 
public static void SaveObject(string key, MyObject obj) { 
    // invoke SQL "update mytable set ... where mykey = @mykey" etc 
    cache[key] = obj.Clone(); 
} 

कारण यह महत्वपूर्ण है कि StringComparer से मेल खाता है डेटाबेस का मिलान कि दोनों झूठे सकारात्मक और मिथ्या नकारात्मक कोड के लिए बुरा प्रभाव होता है।

यदि स्ट्रिंगकॉम्पियर कहता है कि दो कुंजी ए और बी बराबर हैं, जब डेटाबेस का मानना ​​है कि वे अलग हैं, तो डेटाबेस में दो पंक्तियों के साथ दो पंक्तियां हो सकती हैं, लेकिन कैश दूसरे को कभी वापस लौटाएगा उत्तराधिकार में ए और बी के लिए पूछा गया - क्योंकि बी के लिए प्राप्त करने से कैश को गलत तरीके से हिट कर दिया जाएगा और ए

समस्या को और अधिक सूक्ष्म बना दिया जाएगा यदि स्ट्रिंगकॉम्पियर कहता है कि डेटाबेस और ए डेटाबेस बी अलग हैं मानते हैं कि वे बराबर हैं, लेकिन कम समस्याग्रस्त नहीं हैं। दोनों कुंजी के लिए GetObject कॉल ठीक रहेगा, और उसी डेटाबेस पंक्ति से संबंधित ऑब्जेक्ट लौटाएंगे। लेकिन फिर कुंजी ए के साथ SaveObject को कॉल करना कैश को गलत छोड़ देगा; कुंजी बी के लिए अभी भी कैश प्रविष्टि होगी जिसमें पुराना डेटा है। बाद में GetObject (बी) पुरानी जानकारी देगी।

तो मेरे कोड को सही तरीके से काम करने के लिए मुझे समानता परीक्षण और हैशकोड पीढ़ी के लिए डेटाबेस व्यवहार से मेल खाने के लिए स्ट्रिंग कॉम्पैयर की आवश्यकता है। अब तक मेरे गुगल ने इस तथ्य के बारे में बहुत सारी जानकारी प्राप्त की है कि एसक्यूएल कॉलेशन और .NET तुलना बिल्कुल समान नहीं हैं, लेकिन मतभेदों के बारे में कोई जानकारी नहीं है, भले ही वे सॉर्टिंग में केवल अंतर तक ही सीमित हों या फिर यह खोजना संभव हो एक स्ट्रिंग कॉम्पैयर जो विशिष्ट एसक्यूएल संयोजन के बराबर है यदि सामान्य प्रयोजन समाधान की आवश्यकता नहीं है।

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

उत्तर

6

CollationInfo कक्षा पर एक नज़र डालें। यह Microsoft.SqlServer.Management.SqlParser.dll नामक एक असेंबली में स्थित है, हालांकि मुझे पूरा यकीन नहीं है कि यह कहां प्राप्त करें। Collations (नाम) की एक स्थिर सूची और एक स्थिर विधि GetCollationInfo (नाम से) है।

प्रत्येक CollationInfo में Comparer है। यह बिल्कुल StringComparer जैसा नहीं है लेकिन इसकी समान कार्यक्षमता है।

संपादित करें: माइक्रोसॉफ्ट.SqlServer.Management.SqlParser.dll साझा प्रबंधन ऑब्जेक्ट्स (एसएमओ) पैकेज का एक हिस्सा है।यह सुविधा एसक्यूएल सर्वर 2008 R2 यहाँ डाउनलोड किया जा सकता:

http://www.microsoft.com/download/en/details.aspx?id=16978#SMO

संपादित करें:CollationInfoEqualityComparer नाम के एक संपत्ति है जो एक IEqualityComparer<string> है है।

+0

दुर्भाग्यवश आईसीओएमपेयर में हैशकोड प्राप्त करने की क्षमता शामिल नहीं है - मुझे एक आईक्वालिटी कॉम्पैयर की आवश्यकता है जो स्ट्रिंगकॉम्पियर प्रदान करता है। – Stuart

+0

@ स्टुअर्ट - मेरा संपादन देखें, CollactionInfo में IEqualityComparer है। – dana

+0

क्या .NET SQL_Latin1_General_CP1_CI_AS समकक्ष (बस) मूलभूत प्रकार को कॉन्फ़िगर या कार्यान्वित करके प्राप्त करने का कोई तरीका है? एसएमओ मेरी स्थिति के लिए भारी वजन निर्भरता है। –

1

SQL सर्वर का Server.GetStringComparer कुछ उपयोग हो सकता है।

+1

दुर्भाग्यवश आईसीओएमपेयर में शामिल नहीं है हैशकोड प्राप्त करने की क्षमता - मुझे एक IEqualityComparer की आवश्यकता है जो StringComparer प्रदान करता है। – Stuart

9

मुझे हाल ही में एक ही समस्या का सामना करना पड़ा है: मुझे IEqualityComparer<string> की आवश्यकता है जो SQL जैसी शैली में व्यवहार करता है। मैंने CollationInfo और इसकी EqualityComparer की कोशिश की है। अपने डीबी हमेशा है _AS (लहजे संवेदी) तो अपने समाधान काम करेंगे, लेकिन मामले में आप मिलान कि है या WI या जो कुछ भी "असंवेदनशील" और हैशिंग टूट जाएगा बदलते हैं।
क्यों? आप Microsoft.SqlServer.Management.SqlParser.dll डिकंपाइल और अंदर देखो तो आपको पता चल जाएगा कि CollationInfo आंतरिक रूप से उपयोग करता है CultureAwareComparer.GetHashCode और अंत में यह निम्नलिखित है (यह mscorlib.dll के आंतरिक वर्ग है):

public override int GetHashCode(string obj) 
{ 
    if (obj == null) 
    throw new ArgumentNullException("obj"); 
    CompareOptions options = CompareOptions.None; 
    if (this._ignoreCase) 
    options |= CompareOptions.IgnoreCase; 
    return this._compareInfo.GetHashCodeOfString(obj, options); 
} 

जैसा कि आप देख सकते हैं कि यह "एए" और "एए" के लिए एक ही हैशकोड उत्पन्न कर सकता है, लेकिन "äå" और "aa" के लिए नहीं (जो वही हैं, यदि आप बहुसंख्यकों में बहुसंख्यक (एआई) को अनदेखा करते हैं, तो उन्हें चाहिए एक ही हैशकोड है)। मुझे नहीं पता कि .NET API इस तक सीमित क्यों है, लेकिन आपको समझना चाहिए कि समस्या कहां से आ सकती है। विशेषक आप निम्न कर सकते के साथ तार के लिए एक ही hashCode पाने के लिए: create implementationIEqualityComparer<T> की GetHashCode कि कॉल करेंगे लागू करने उचित CompareInfo की वस्तु की GetHashCodeOfString प्रतिबिंब के माध्यम से क्योंकि इस प्रक्रिया में आंतरिक है और सीधे नहीं किया जा सकता। लेकिन यह सीधे कॉल सही CompareOptions साथ वांछित परिणाम उपज जाएगा: इस उदाहरण देखें:

static void Main(string[] args) 
    { 
     const string outputPath = "output.txt"; 
     const string latin1GeneralCiAiKsWs = "Latin1_General_100_CI_AI_KS_WS"; 
     using (FileStream fileStream = File.Open(outputPath, FileMode.Create, FileAccess.Write)) 
     { 
      using (var streamWriter = new StreamWriter(fileStream, Encoding.UTF8)) 
      { 
       string[] strings = { "aa", "AA", "äå", "ÄÅ" }; 
       CompareInfo compareInfo = CultureInfo.GetCultureInfo(1033).CompareInfo; 
       MethodInfo GetHashCodeOfString = compareInfo.GetType() 
        .GetMethod("GetHashCodeOfString", 
        BindingFlags.Instance | BindingFlags.NonPublic, 
        null, 
        new[] { typeof(string), typeof(CompareOptions), typeof(bool), typeof(long) }, 
        null); 

       Func<string, int> correctHackGetHashCode = s => (int)GetHashCodeOfString.Invoke(compareInfo, 
        new object[] { s, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace, false, 0L }); 

       Func<string, int> incorrectCollationInfoGetHashCode = 
        s => CollationInfo.GetCollationInfo(latin1GeneralCiAiKsWs).EqualityComparer.GetHashCode(s); 

       PrintHashCodes(latin1GeneralCiAiKsWs, incorrectCollationInfoGetHashCode, streamWriter, strings); 
       PrintHashCodes("----", correctHackGetHashCode, streamWriter, strings); 
      } 
     } 
     Process.Start(outputPath); 
    } 
    private static void PrintHashCodes(string collation, Func<string, int> getHashCode, TextWriter writer, params string[] strings) 
    { 
     writer.WriteLine(Environment.NewLine + "Used collation: {0}", collation + Environment.NewLine); 
     foreach (string s in strings) 
     { 
      WriteStringHashcode(writer, s, getHashCode(s)); 
     } 
    } 

उत्पादन होता है:

Used collation: Latin1_General_100_CI_AI_KS_WS 
aa, hashcode: 2053722942 
AA, hashcode: 2053722942 
äå, hashcode: -266555795 
ÄÅ, hashcode: -266555795 

Used collation: ---- 
aa, hashcode: 2053722942 
AA, hashcode: 2053722942 
äå, hashcode: 2053722942 
ÄÅ, hashcode: 2053722942 

मैं यह लग रहा है पता हैक की तरह, लेकिन उसके बाद का निरीक्षण करते हुए decompiled नेट कोड मुझे यकीन नहीं है कि सामान्य कार्यक्षमता की आवश्यकता होने पर कोई अन्य विकल्प है या नहीं। तो सुनिश्चित करें कि आप पूरी तरह से सही एपीआई का उपयोग कर जाल में नहीं आ जाएंगे।
अद्यतन:
मैंने CollationInfo का उपयोग करके the gist with potential implementation of "SQL-like comparer" भी बनाया है। इसके अलावा आपके कोड बेस में where to search for "string pitfalls" पर पर्याप्त ध्यान देना चाहिए, इसलिए यदि स्ट्रिंग तुलना, हैशकोड, समानता को "एसक्यूएल collation-like" में बदला जाना चाहिए, तो उन स्थानों को 100% टूटा जाएगा, इसलिए आपको पता लगाना होगा और उन सभी स्थानों का निरीक्षण करें जिन्हें टूटा जा सकता है।
अद्यतन # 2:
GetHashCode() तुलनाऑप्शन का इलाज करने के लिए बेहतर और साफ तरीका है। कक्षा में शामिल SortKey कि CompareOptions साथ ठीक से काम करता है और उसका उपयोग करना

CompareInfo.GetSortKey (yourString, yourCompareOptions) .GetHashCode()

यहाँ link नेट के लिए स्रोत कोड है और प्राप्त किए जा सकें कार्यान्वयन।

+0

+1 यह एक गंभीरता से अच्छी तरह से शोध किया गया जवाब है! काश मैं आपको एक से अधिक बार वोट दे सकता हूं। –

+0

अच्छी तरह से समझाया। – user2250250

+0

कृपया अद्यतन # 2 पर ध्यान दें - GetHashCode() को तुलनात्मक रूप से तुलना करने के लिए सही तरीके से इलाज करने के लिए बॉक्स से बाहर है। यह बहुत साफ तरीका है और किसी भी प्रतिबिंब हैक की आवश्यकता नहीं है। दुर्भाग्यवश, मैंने यह पोस्ट करने के कुछ ही समय बाद ही इसे पाया। –

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