2010-10-14 15 views
6

मेरे पास एक जटिल LINQ क्वेरी है (LINQ 2 EF का उपयोग करके) जो डुप्लिकेट परिणाम लौटा सकती है और मैं डुप्लिकेट से बचने के लिए .Distinct() विधि का उपयोग कर रहा हूं। यहाँ कंकाल है:एक्सएमएल फ़ील्ड को अनदेखा करते समय LINQ का चयन करें

var subQuery1 = // one query... 
var subQuery2 = // another query... 
var result = subQuery1.Distinct().Union(subQuery2.Distinct()).ToArray(); 

उप प्रश्नों में से प्रत्येक एक और टेबल के साथ एक आम उपयोगकर्ता तालिका में शामिल होने और एक 'जहां' क्वेरी निष्पादित, परिणाम बाद में .Union(...) में मिलाया जाता है। यह ठीक काम किया जब तक तालिका एक XML स्तंभ है, जो इस अपवाद में परिणाम है शामिल करने के लिए संशोधित किया गया था:

XML डेटा प्रकार अलग रूप में चयन नहीं किया जा सकता है क्योंकि यह तुलनीय

इस मामले मैं में नहीं है अगर एक्सएमएल कॉलम परिणामों के बराबर है तो परवाह नहीं है। असल में मुझे केवल यह आश्वासन दिया जाना चाहिए कि प्राथमिक कुंजी UserId परिणाम में अलग है।

क्या Distinct() का उपयोग करने का कोई तरीका है लेकिन एक्सएमएल कॉलम को अनदेखा करें या यह सुनिश्चित करने के लिए एक आसान तरीका है कि मैं परिणाम से रिकॉर्ड UserId के साथ एक कुशल तरीके से हटा देता हूं? आदर्श रूप से यह डेटाबेस से डुप्लिकेट रिकॉर्ड पुनर्प्राप्त नहीं करेगा और डुप्लिकेट को हटाने के लिए पोस्ट-प्रोसेसिंग की आवश्यकता नहीं होगी।

अद्यतन: मुझे पता चला गया है तो Linq2Objects के बाद से comparer के किसी भी प्रकार की कोई जरूरत एक्सएमएल अलग चयन मुद्दा नहीं है नहीं है कि अगर मैं अपने प्रश्नों को क्रमानुसार समय से आगे arrays के लिए। उदाहरण के लिए मैं यह कर सकता:

var subQuery1 = // one query... 
var subQuery2 = // another query... 
var result = 
    subQuery1.Distinct().ToArray().Union( 
     subQuery2.Distinct().ToArray()) 
    .ToArray(); 

तो क्या मैं वास्तव में के लिए देख रहा हूँ एक तरह से मध्यवर्ती प्रश्नों serializing से बचने और एक Linq2Entities सीधे कि फोन डुप्लिकेट UserId रों के साथ रिकॉर्ड लाने नहीं होगा करना है। अब तक सभी उत्तरों के लिए धन्यवाद।

+1

सटीक समस्या का उत्तर नहीं है, लेकिन सामान्य रूप से यदि आप कुछ संक्षिप्तकरण के साथ 'विशिष्ट' चाहते हैं, तो सीधे 'संघ' का उपयोग करें। 'यूनियन', 'एक्सेप्ट', 'इंटरसेक्ट' इत्यादि जैसे ऑपरेशन सेट करें वैसे भी डुप्लिकेट हटा दें। तो आपके मामले में, बस: 'subQuery1.Union (subQuery2)। ToArray()' – nawfal

उत्तर

1

इस विस्तार विधि उस में डुप्लिकेट के प्रत्येक सेट से केवल पहला आइटम के साथ आइटम की एक सूची प्रदान करना चाहिए ...

public static IEnumerable<Tsource> RemoveDuplicates<Tkey, Tsource>(this IEnumerable<Tsource> source, Func<Tsource, Tkey> keySelector) 
{ 
    var hashset = new HashSet<Tkey>(); 
    foreach (var item in source) 
    { 
     var key = keySelector(item); 
     if (hashset.Add(key)) 
      yield return item; 
    } 
} 

यह इस list.RemoveDuplicates(x => x.UserID) प्रकार की सूची पर इस्तेमाल किया जाएगा। यदि एक ही उपयोगकर्ता आईडी के साथ सूची में दो रिकॉर्ड थे, तो यह केवल पहले

+0

अच्छा, बेहतर इसे 'विशिष्ट' या 'DistinctBy' कहते हैं? 'निकालें' ध्वनि कार्यात्मक नहीं बल्कि गैर-दुष्प्रभाव मुक्त है। – nawfal

3

आपके XML प्रकार वाले ऑब्जेक्ट के लिए IEqualityComparer<T> कार्यान्वयन लिखें और इसे Distinct पर पास कर देगा। Equals विधि में आप समानता अर्थशास्त्र को लागू कर सकते हैं, हालांकि आप चाहें।

यह एक आसान टी -4 कोड पीढ़ी टेम्पलेट मैं अपने आप को अपनी टीम के डोमेन मॉडल के लिए IEqualityComparer<T> कार्यान्वयन पैदा करने के लिए लिखा है:

<#@ template language="C#v3.5" debug="True" #> 
<#@ output extension=".generated.cs" #> 
<# 
    var modelNames = new string[] { 
     "ClassName1", 
     "ClassName2", 
     "ClassName3", 
    }; 

    var namespaceName = "MyNamespace"; 
#> 
using System; 
using System.Collections.Generic; 

namespace <#= namespaceName #> 
{ 
<# 
    for (int i = 0; i < modelNames.Length; ++i) 
    { 
     string modelName = modelNames[i]; 
     string eqcmpClassName = modelName + "ByIDEqualityComparer"; 
#> 
    #region <#= eqcmpClassName #> 

    /// <summary> 
    /// Use this EqualityComparer class to determine uniqueness among <#= modelName #> instances 
    /// by using only checking the ID property. 
    /// </summary> 
    [System.Diagnostics.DebuggerNonUserCode] 
    public sealed partial class <#= eqcmpClassName #> : IEqualityComparer<<#= modelName #>> 
    { 
     public bool Equals(<#= modelName #> x, <#= modelName #> y) 
     { 
      if ((x == null) && (y == null)) return true; 
      if ((x == null) || (y == null)) return false; 

      return x.ID.Equals(y.ID); 
     } 

     public int GetHashCode(<#= modelName #> obj) 
     { 
      if (obj == null) return 0; 

      return obj.ID.GetHashCode(); 
     } 
    } 

    #endregion 
<# 
     if (i < modelNames.Length - 1) WriteLine(String.Empty); 
    } // for (int i = 0; i < modelNames.Length; ++i) 
#> 
} 

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

+0

टी 4 टेम्पलेट सुनिश्चित करना आसान है, लेकिन IEqualityComparer का उपयोग करने के लिए मुझे डुप्लिकेट को हटाए जाने से पहले मेरे प्रश्नों को सरणी में दोनों क्वेरी (जैसे linq2entities तुलनाकर्ता का समर्थन नहीं करता) को पहले सेरियलाइज़ करना होगा। फिर भी, यह कुछ ऐसा है जो समय के लिए काम करता है, thanx! – TJB

+0

@ टीजेबी: आह। मुझे LINQ-to-entities के साथ कोई अनुभव नहीं है। मैं त्वरित रूप से अलग-अलग आईडी के लिए इन-मेमोरी संग्रह पर LINQ-to-items के लिए 'IEqualityComparer ' का उपयोग करता हूं। सेवा का होकर ख़ुशी है! टी 4 टेम्पलेट नियम :) –

2

के रूप में जेम्स ड्यूनी कहा, आप एक IEqualityComparer

एक त्वरित नकली अप का उपयोग करने के कुछ इस तरह होगा चाहते हैं। आपको "ऑब्जेक्ट टाइप" को प्रतिस्थापित करने की आवश्यकता होगी, जो भी आपके subQuery1 और subQuery2 में है। कृपया ध्यान दें कि यह अपरीक्षित है:

List<ObjectType> listQueries = new List<ObjectType>(); 

ObjectTypeEqualityComparer objectTypeComparer = new ObjectTypeEqualityComparer(); 

listQueries.AddRange(subQuery1);// your first query 
listQueries.AddRange(subQuery2); // your second query 
ObjectType[] result = listQueries.Distinct(objectTypeComparer).ToArray(); 


class ObjectTypeEqualityComparer : IEqualityComparer<ObjectType> 
{ 
    public bool Equals(ObjectType obj1, ObjectType obj2) 
    { 
     return obj1.UserId == obj2.UserId ? true : false; 
    } 

    public int GetHashCode(ObjectType obj) 
    { 
     return obj.UserId.GetHashCode(); 
    } 

} 
+0

पिगबैक जवाब, आह? :) मैं एक उदाहरण शामिल करने के लिए वापस जाने और अपना उत्तर अपडेट करने पर विचार कर रहा था, लेकिन आपका भी पर्याप्त है। –

+0

हाहा, मेरी रक्षा में, मैं आपका जवाब देखने से पहले इसे लिखने वाला था :) –

1

आप morelinq के DistinctBy इस्तेमाल कर सकते हैं। मुझे संदेह है (लेकिन सत्यापित नहीं किया गया है) कि यह, साथ ही IEqualityComparer और RemoveDuplicates उत्तर, SQL सर्वर से डुप्लिकेट रिकॉर्ड्स पुनर्प्राप्त करेगा और फिर क्लाइंट पर डुप्लीकेट हटा देगा। अगर कोई सर्वर-साइड समाधान प्रदान करता है, तो मैं उनके उत्तर को स्वीकार करने की सिफारिश करता हूं।

+0

ठीक है, मैं ऐसा कुछ ढूंढ रहा हूं जो पोस्ट-प्रोसेसिंग के बजाय एसक्यूएल में 'विशिष्ट' प्राप्त कर सके – TJB

0

नोट: मैं Linq2SQL (Linq2Entities नहीं) का उपयोग कर रहा हूं - लेकिन शायद दोनों के लिए काम करता है।

यदि आप हमेशा चाहते हैं कि एक्सएमएल प्रत्येक क्वेरी के लिए वापस नहीं आती है तो आप एक्सएमएल कॉलम को डीबीएमएल फ़ाइल में 'देरी लोड' के रूप में सेट कर सकते हैं।

मैंने AddressBook एक्सएमएल कॉलम को Customer तालिका में जोड़ा जो अचानक मेरी सभी खोजों को तोड़ दिया। एक बार जब मैंने कॉलम को DelayLoad=true पर स्विच कर दिया तो सबकुछ फिर से काम करता था (क्योंकि इसमें DISTINCT में उस कॉलम को शामिल नहीं किया गया था)।

इस डेटा के आधार पर इस समाधान (आलसी लोड कॉलम बनाना) या तो आपके सिस्टम को काफी तेज़ या धीमा कर सकता है - इसलिए सावधान रहें!

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