@@ -49,6 +49,73 @@ public static partial class AutoMapper
4949 /// </summary>
5050 internal static class InternalHelpers
5151 {
52+ /// <summary>
53+ /// Combine several hashcodes into a single new one. This implementation was grabbed from http://stackoverflow.com/a/34229665 where it is introduced
54+ /// as MS implementation of GetHashCode() for strings.
55+ /// </summary>
56+ /// <param name="hashCodes">Hascodes to be combined.</param>
57+ /// <returns>A new Hascode value combining those passed as parameters.</returns>
58+ private static int CombineHashCodes ( params int [ ] hashCodes )
59+ {
60+ int hash1 = ( 5381 << 16 ) + 5381 ;
61+ int hash2 = hash1 ;
62+
63+ int i = 0 ;
64+ foreach ( var hashCode in hashCodes )
65+ {
66+ if ( i % 2 == 0 )
67+ hash1 = ( ( hash1 << 5 ) + hash1 + ( hash1 >> 27 ) ) ^ hashCode ;
68+ else
69+ hash2 = ( ( hash2 << 5 ) + hash2 + ( hash2 >> 27 ) ) ^ hashCode ;
70+
71+ ++ i ;
72+ }
73+
74+ return hash1 + ( hash2 * 1566083941 ) ;
75+ }
76+
77+ /// <summary>
78+ /// Defines the key for caching instances. Overrides Equality as to get unicity for a given set of identifiers values
79+ /// for a given type.
80+ /// </summary>
81+ public struct InstanceKey : IEquatable < InstanceKey >
82+ {
83+ public bool Equals ( InstanceKey other ) {
84+ return Equals ( Type , other . Type )
85+ && Equals ( ParentInstance , other . ParentInstance )
86+ && StructuralComparisons . StructuralEqualityComparer . Equals ( IdentifierValues , other . IdentifierValues ) ;
87+ }
88+
89+ public override bool Equals ( object obj )
90+ {
91+ if ( ReferenceEquals ( null , obj ) ) return false ;
92+ return obj is InstanceKey && Equals ( ( InstanceKey ) obj ) ;
93+ }
94+
95+ public override int GetHashCode ( )
96+ {
97+ unchecked
98+ {
99+ return CombineHashCodes ( Type ? . GetHashCode ( ) ?? 0 , StructuralComparisons . StructuralEqualityComparer . GetHashCode ( IdentifierValues ) , ParentInstance ? . GetHashCode ( ) ?? 0 ) ;
100+ }
101+ }
102+
103+ public static bool operator == ( InstanceKey left , InstanceKey right ) { return left . Equals ( right ) ; }
104+
105+ public static bool operator != ( InstanceKey left , InstanceKey right ) { return ! left . Equals ( right ) ; }
106+
107+ public InstanceKey ( Type type , object [ ] identifierValues , object parentInstance )
108+ {
109+ Type = type ;
110+ IdentifierValues = identifierValues ;
111+ ParentInstance = parentInstance ;
112+ }
113+
114+ public Type Type { get ; }
115+ public object [ ] IdentifierValues { get ; }
116+ public object ParentInstance { get ; }
117+ }
118+
52119 /// <summary>
53120 /// Gets the identifiers for the given type. Returns NULL if not found.
54121 /// Results are cached for subsequent use and performance.
@@ -338,6 +405,28 @@ private static object GetMemberValue(object member, object obj)
338405 return value ;
339406 }
340407
408+ /// <summary>
409+ /// Computes a key for storing and identifying an instance in the cache.
410+ /// </summary>
411+ /// <param name="type">Type of instance to get</param>
412+ /// <param name="properties">List of properties and values</param>
413+ /// <param name="parentInstance">Parent instance. Can be NULL if this is the root instance.</param>
414+ /// <returns>
415+ /// InstanceKey that will be unique for given set of identifiers values for the type. If the type isn't associated with any
416+ /// identifier, the return value is made unique by generating a Guid.
417+ /// ASSUMES GetIdentifiers(type) ALWAYS RETURN IDENTIFIERS IN THE SAME ORDER FOR A GIVEN TYPE.
418+ /// This is certainly the case as long as GetIdentifiers caches its result for a given type (which it does by 2016-11-25).
419+ /// </returns>
420+ private static InstanceKey GetCacheKey ( Type type , IDictionary < string , object > properties , object parentInstance )
421+ {
422+ var identifierValues = GetIdentifiers ( type ) ? . Select ( id => properties [ id ] ) . DefaultIfEmpty ( Guid . NewGuid ( ) ) . ToArray ( )
423+ ?? new object [ ] { Guid . NewGuid ( ) } ;
424+
425+ var key = new InstanceKey ( type , identifierValues , parentInstance ) ;
426+ return key ;
427+ }
428+
429+
341430 /// <summary>
342431 /// Gets a new or existing instance depending on whether an instance with the same identifiers already existing
343432 /// in the instance cache.
@@ -349,7 +438,7 @@ private static object GetMemberValue(object member, object obj)
349438 /// Tuple of bool, object, int where bool represents whether this is a newly created instance,
350439 /// object being an instance of the requested type and int being the instance's identifier hash.
351440 /// </returns>
352- internal static Tuple < bool , object , Tuple < int , int , object > > GetInstance ( Type type , IDictionary < string , object > properties , object parentInstance = null )
441+ internal static Tuple < bool , object , InstanceKey > GetInstance ( Type type , IDictionary < string , object > properties , object parentInstance = null )
353442 {
354443 var key = GetCacheKey ( type , properties , parentInstance ) ;
355444
@@ -368,47 +457,6 @@ internal static Tuple<bool, object, Tuple<int, int, object>> GetInstance(Type ty
368457 return Tuple . Create ( isNewlyCreatedInstance , instance , key ) ;
369458 }
370459
371- private static Tuple < int , int , object > GetCacheKey ( Type type , IDictionary < string , object > properties , object parentInstance )
372- {
373- var identifierHash = GetIdentifierHash ( type , properties ) ;
374-
375- var key = Tuple . Create ( identifierHash , type . GetHashCode ( ) , parentInstance ) ;
376- return key ;
377- }
378-
379- private static int GetIdentifierHash ( Type type , IDictionary < string , object > properties )
380- {
381- var identifiers = GetIdentifiers ( type ) ;
382-
383- var identifierHash = 0 ;
384-
385- if ( identifiers != null )
386- {
387- foreach ( var identifier in identifiers )
388- {
389- if ( properties . ContainsKey ( identifier ) )
390- {
391- var identifierValue = properties [ identifier ] ;
392- if ( identifierValue != null )
393- {
394- // Unchecked to avoid arithmetic overflow
395- unchecked
396- {
397- // Include identifier hashcode to avoid collisions between e.g. multiple int IDs
398- identifierHash += identifierValue . GetHashCode ( ) + identifier . GetHashCode ( ) ;
399- }
400- }
401- }
402- }
403- }
404- else
405- {
406- // If the type has no identifiers we must generate a unique hash for it.
407- identifierHash = Guid . NewGuid ( ) . GetHashCode ( ) ;
408- }
409- return identifierHash ;
410- }
411-
412460 /// <summary>
413461 /// Populates the given instance's properties where the IDictionary key property names
414462 /// match the type's property names case insensitively.
0 commit comments