2016-08-23 5 views
9

मुझे एक इकाई ढांचे परियोजना में एक नेविगेशन संपत्ति के साथ कोई समस्या है। ,क्यों आईसीओलेक्शन <>। मेरे ओवरराइड बराबर और IEquatable <> इंटरफ़ेस को अनदेखा करता है?

[DataContract] 
[Table("MobileDeviceInfo")] 
public class MobileDeviceInfo : IEquatable<MobileDeviceInfo> 
{ 
    [DataContract] 
    public enum MobilePlatform 
    { 
     [EnumMember] 
     // ReSharper disable once InconsistentNaming because correct spelling is iOS 
     iOS = 1, 
     [EnumMember] Android = 2, 
     [EnumMember] WindowsPhone = 3, 
     [EnumMember] Blackberry = 4 
    } 

    // constructors omitted ... 

    [DataMember, Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int DeviceInfoId { get; private set; } 

    [DataMember, Required, Index(IsUnique = true), MinLength(DeviceTokenMinLength), MaxLength(DeviceTokenMaxLength)] 
    public string DeviceToken { get; set; } 

    [DataMember, Required, MinLength(DeviceNameMinLength), MaxLength(DeviceNameMaxLength)] 
    public string DeviceName { get; set; } 

    [DataMember, Required] 
    public MobilePlatform Platform { get; set; } 

    // other properties ... 

    [DataMember] 
    public virtual MobileUser MobileUser { get; private set; } 

    /// <summary> 
    ///  The foreign-key to the MobileUser. 
    ///  This is not the VwdId which is stored in MobileUser 
    /// </summary> 
    [DataMember, ForeignKey("MobileUser")] 
    public int UserId { get; set; } 

    public bool Equals(MobileDeviceInfo other) 
    { 
     if (other == null) return false; 
     return DeviceToken == other.DeviceToken; 
    } 

    public override string ToString() 
    { 
     return "Bah"; // implementation omitted 

    public override bool Equals(object obj) 
    { 
     if (ReferenceEquals(this, obj)) return true; 
     MobileDeviceInfo other = obj as MobileDeviceInfo; 
     if (other == null) return false; 
     return Equals(other); 
    } 

    public override int GetHashCode() 
    { 
     // ReSharper disable once NonReadonlyMemberInGetHashCode 
     return DeviceToken.GetHashCode(); 
    } 

    #region constants 
    // irrelevant 
    #endregion 
} 

आप देख सकते हैं:

public virtual ICollection<MobileDeviceInfo> DeviceInfos { get; private set; } 

इस वर्ग MobileDeviceInfo है:

[DataContract] 
[Table("MobileUser")] 
public class MobileUser: IEquatable<MobileUser> 
{ 
    // constructors omitted.... 

    /// <summary> 
    /// The primary-key of MobileUser. 
    /// This is not the VwdId which is stored in a separate column 
    /// </summary> 
    [DataMember, Key, Required, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int UserId { get; set; } 

    [DataMember, Required, Index(IsUnique = true), MinLength(VwdIdMinLength), MaxLength(VwdIdMaxLength)] 
    public string VwdId { get; set; } 

    // other properties omitted ... 

    [DataMember] 
    public virtual ICollection<MobileDeviceInfo> DeviceInfos { get; private set; } 

    public bool Equals(MobileUser other) 
    { 
     return this.UserId == other?.UserId || this.VwdId == other?.VwdId; 
    } 

    public override bool Equals(object obj) 
    { 
     if(object.ReferenceEquals(this, obj))return true; 
     MobileUser other = obj as MobileUser; 
     if (other == null) return false; 
     return this.Equals(other); 
    } 

    public override int GetHashCode() 
    { 
     // ReSharper disable once NonReadonlyMemberInGetHashCode 
     return VwdId.GetHashCode(); 
    } 

    public override string ToString() 
    { 
     return "foo"; // omitted actual implementation 
    } 

    #region constants 
    // irrelevant 
    #endregion 
} 

प्रासंगिक हिस्सा इस नेविगेशन संपत्ति है:

यहाँ वर्ग MobileUser है यह IEquatable<MobileDeviceInfo> लागू करता है औरओवरराइड करता है System.Object सेऔर GetHashCode

मेरे पास निम्न परीक्षण है, मुझे उम्मीद है कि Contains मेरे Equals पर कॉल करेगा लेकिन ऐसा नहीं है। यह बजाय Object.ReferenceEquals उपयोग करने के लिए लगता है, इसलिए मेरे डिवाइस नहीं मिलेगा, क्योंकि यह एक अलग संदर्भ है:

var userRepo = new MobileUserRepository((ILog)null); 
var deviceRepo = new MobileDeviceRepository((ILog)null); 

IReadOnlyList<MobileUser> allUser = userRepo.GetAllMobileUsersWithDevices(); 
MobileUser user = allUser.First(); 

IReadOnlyList<MobileDeviceInfo> allDevices = deviceRepo.GetMobileDeviceInfos(user.VwdId, true); 
MobileDeviceInfo device = allDevices.First(); 
bool contains = user.DeviceInfos.Contains(device); 
bool anyEqual = user.DeviceInfos.Any(x => x.DeviceToken == device.DeviceToken); 
Assert.IsTrue(contains); // no, it's false 

LINQ के Enumerable.Any उम्मीद true रिटर्न के साथ दूसरा दृष्टिकोण।

यदि मैं user.DeviceInfos.Contains(device) का उपयोग नहीं करता लेकिन user.DeviceInfos.ToList().Contains(device) यह List<>.Contains से Equals का उपयोग करने के बाद से अपेक्षा करता है।

ICollection<> के वास्तविक प्रकार एक System.Collections.Generic.HashSet<MobileDeviceInfo> प्रतीत हो रहा है, लेकिन अगर मैं कोड भी एक HashSet<> इसे फिर से अपेक्षा के अनुरूप काम करता है का उपयोग करता है निम्नलिखित का उपयोग करें:

bool contains = new HashSet<MobileDeviceInfo>(user.DeviceInfos).Contains(device); // true 

तो क्यों केवल संदर्भ तुलना की जाती है और अपने कस्टम Equals है अवहेलना करना?

अद्यतन:

और भी अधिक भ्रमित परिणाम है false है, भले ही मैं इसे HashSet<MobileDeviceInfo> लिए डाली:

// still false 
bool contains2 = ((HashSet<MobileDeviceInfo>)user.DeviceInfos).Contains(device); 
// but this is true as already mentioned 
bool contains3 = new HashSet<MobileDeviceInfo>(user.DeviceInfos).Contains(device); 

अद्यतन 2:: इस का कारण यह वास्तव में लगता है हो सकता है कि हैशसेट्स अलग तुलनाकर्ताओं का उपयोग करें।

System.Data.Entity.Infrastructure.ObjectReferenceEqualityComparer 

और मानक HashSet<> एक का उपयोग करता है: इकाई-ढांचा-HashSet एक का उपयोग करता है

GenericEqualityComparer<T> 

यही मुद्दा बताते हैं, हालांकि मुझे समझ नहीं आता क्यों इकाई की रूपरेखा एक कार्यान्वयन कि कस्टम पर ध्यान नहीं देता का उपयोग करता है कुछ परिस्थितियों में Equals कार्यान्वयन। यह एक बुरा जाल है, है ना?


निष्कर्ष: Contains का उपयोग करता है, तो आप क्या comparer उपयोग किया जाएगा पता है या अधिभार कि एक कस्टम comparer लेता है साथ Enumerable.Contains का उपयोग नहीं करते कभी नहीं:, एफई स्रोत से

bool contains = user.DeviceInfos.Contains(device, EqualityComparer<MobileDeviceInfo>.Default); // true 
+0

'आईसीओलेक्शन 'स्वयं कुछ भी नहीं करेगा - यह सिर्फ एक इंटरफ़ेस है। क्या कार्यान्वयन का उपयोग किया जा रहा है? क्या आप ईएफ का उपयोग किये बिना इसे पुन: पेश कर सकते हैं? –

+0

@ जोनस्केट: यह आलसी लोडिंग का उपयोग नहीं कर रहा है, इसका प्रकार वास्तव में एक हैशसेट <मोबाइलडिवाइस इंफो>> है। यही इतना भ्रमित है। अंतिम पैराग्राफ को देखो। कोई एसक्यूएल उत्पन्न नहीं होता है। –

+0

आपके हैश कोड को एक म्यूटेबल प्रॉपर्टी पर आधारित है, तो क्या होता है यदि ईएफ आइटम हैशसेट में जोड़ता है और * फिर * संपत्ति मान सेट करता है? –

उत्तर

8

आप CreateCollectionCreateDelegate पर ठोकर खा सकता है, जिसे नेविगेशन गुणों को जोड़ने के हिस्से के रूप में जाना जाता है।

यह EntityUtil.DetermineCollectionType पर कॉल करता है और HashSet<T> को उस प्रकार के रूप में देता है जो संपत्ति के अनुकूल है।

फिर, HashSet<T> के साथ सशस्त्र, यह DelegateFactory.GetNewExpressionForCollectionType के लिए एक कॉल जो, कोड और विवरण के अनुसार, विशेष मामले के रूप HashSet<T> संभालती है और यह निर्माता में एक ObjectReferenceEqualityComparer गुजरता बनाता है।

तो: HashSet<T> ईएफ आपके लिए बनाता है आपके समानता कार्यान्वयन का उपयोग नहीं कर रहा है, यह इसके बजाय संदर्भ समानता का उपयोग करता है।

+0

धन्यवाद। हालांकि मैं वास्तव में समझ में नहीं आता कि क्यों इकाई ढांचा उस मामले में 'ऑब्जेक्ट रेफरेंस एक्वालिटी कॉम्पियर' का उपयोग करना चाहता है। यह एक बुरा जाल है। –

+2

@TimSchmelter मेरा मानना ​​है कि यह इसका उपयोग कर रहा है क्योंकि यह तेज़ कैश लुकअप रखने के लिए 'ICollection 'चाहता है, यह गारंटी नहीं दे सकता कि डिफ़ॉल्ट तुलनाकर्ता उस फ़ंक्शन के लिए अच्छी तरह से व्यवहार करेगा। आपने सोचा था कि यह वैसे भी एक सूची थी, इसलिए बस 'एन्यूमेरेबल.कॉन्टेन' अधिभार का उपयोग करें [जो एक तुलनाकर्ता में लेता है] (https://msdn.microsoft.com/en-us/library/bb339118 (v = बनाम। 110) .aspx) और 'समानता कॉम्पैयर <मोबाइलडिवाइस इंफो> डीफॉल्ट' में पास करें। –

+1

@ स्कॉटकैम्बरलेन: इसलिए अंगूठे का नियम: यदि आप नहीं जानते कि संग्रह का वास्तविक प्रकार क्या है और यह किस तुलना में उपयोग करता है, तो अन्यथा आप गर्म पानी में प्रवेश करेंगे। –

2

क्यों ICollection <> .Contains मेरी ओवरराइड बराबर है और IEquatable <> इंटरफेस पर ध्यान नहीं देता?

क्योंकि ऐसा करने के लिए इंटरफ़ेस के कार्यान्वयनकर्ताओं की कोई आवश्यकता नहीं है।

ICollection<T>.Contains विधि MSDN documentation कहता है:

ICollection < टी> कोई विशिष्ट मान है कि क्या निर्धारित करता है।

और फिर

टिप्पणियां

क्रियान्वयन कि वे किस तरह की वस्तुओं की समानता का निर्धारण में भिन्न हो सकते हैं; उदाहरण के लिए, सूची < टी> तुलनाकर्ता < टी> डीफॉल्ट का उपयोग करता है, जबकि शब्दकोश < टीके, टीवीएयू> उपयोगकर्ता को चाबियाँ की तुलना करने के लिए उपयोग करने के लिए आईसीओएमपेयर < टी> कार्यान्वयन निर्दिष्ट करने की अनुमति देता है।

साइड नोट: ऐसा लगता है कि वे IEqualityComparer<T> साथ IComparer<T> में गड़बड़ है, लेकिन आप बिंदु :)

निष्कर्ष मिलती है: कभी नहीं का उपयोग होता है अगर आप नहीं जानते कि क्या comparer उपयोग किया जाएगा या अधिभार कि एक कस्टम comparer

लेता है साथ Enumerable.Contains का उपयोग Enumerable.Contains<T>(IEnumerable<T>, T) विधि अधिभार के अनुसार (अर्थात।कस्टम comparer के बिना) documentation:

निर्धारित करता है एक दृश्य डिफ़ॉल्ट समानता comparer का उपयोग करके एक निर्दिष्ट तत्व शामिल है या नहीं।

जो आपके ओवरराइड की तरह लगता है। लेकिन तब आता है निम्नलिखित:

टिप्पणियां
स्रोत के प्रकार को लागू करता है ICollection < टी>, कि कार्यान्वयन में विधि शामिल परिणाम प्राप्त करने के लिए लागू किया जाता है। अन्यथा, यह विधि निर्धारित करती है कि स्रोत में निर्दिष्ट तत्व है या नहीं।

जो प्रारंभिक बयान के साथ संघर्ष करता है।

यह वास्तव में एक गड़बड़ है। मैं बस इतना कह सकता हूं कि मैं उस निष्कर्ष से पूरी तरह से सहमत हूं!

+0

यह ... परेशान है। लगभग अपने कोड के रूप में समझ में नहीं आता :) –

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