1313
1414 internal class ForeignKeyConventionHelper
1515 {
16- private static readonly ConcurrentDictionary < PropertyInfo , Result > _foreignKeyCache = new ConcurrentDictionary < PropertyInfo , Result > ( ) ;
16+ private static readonly ConcurrentDictionary < Tuple < Type , Type , string > , Result > _foreignKeyCache = new ConcurrentDictionary < Tuple < Type , Type , string > , Result > ( ) ;
1717
1818 public static Result GetForeignKeyPropertyInfos ( [ NotNull ] PropertyInfo pi )
1919 {
2020 Guard . NotNull ( pi , nameof ( pi ) ) ;
2121
22- if ( ! _foreignKeyCache . TryGetValue ( pi , out var result ) )
22+ var sourceType = pi . DeclaringType ;
23+ var targetType = pi . PropertyType . GetGenericTypeOrDefault ( ) ;
24+
25+ return GetForeignKeyPropertyInfos ( sourceType , targetType , pi . Name ) ;
26+ }
27+
28+ public static Result GetForeignKeyPropertyInfos ( [ NotNull ] Type sourceType , [ NotNull ] Type targetType , [ NotNull ] string navPiName )
29+ {
30+ Guard . NotNull ( sourceType , nameof ( sourceType ) ) ;
31+ Guard . NotNull ( targetType , nameof ( targetType ) ) ;
32+
33+ var key = Tuple . Create ( sourceType , targetType , navPiName ) ;
34+
35+ if ( ! _foreignKeyCache . TryGetValue ( key , out var result ) )
2336 {
24- result = GetForeignKeyPropertyInfosCore ( pi ) ;
25- _foreignKeyCache . TryAdd ( pi , result ) ;
37+ result = GetForeignKeyPropertyInfosCore ( sourceType , targetType , navPiName ) ;
38+ _foreignKeyCache . TryAdd ( key , result ) ;
2639 }
2740
2841 return result ;
2942 }
3043
31- private static Result GetForeignKeyPropertyInfosCore ( PropertyInfo pi )
44+ private static Result GetForeignKeyPropertyInfosCore ( Type sourceType , Type targetType , string navPiName )
3245 {
33- var foreignType = pi . PropertyType . GetGenericTypeOrDefault ( ) ;
34- var declaringType = pi . DeclaringType ;
35-
36- if ( foreignType . IsEnumerable ( ) || declaringType . IsEnumerable ( ) )
46+ if ( sourceType . IsEnumerable ( ) || targetType . IsEnumerable ( ) )
3747 return null ;
3848
3949 bool foundInSource ;
4050
41- if ( TryGetForeignKeyPropertyInfos ( foreignType , declaringType ,
51+ if ( TryGetForeignKeyPropertyInfos (
52+ sourceType , targetType , navPiName ,
53+ searchNavPiInSource : true ,
4254 out var foreignKeyPropertyInfos ,
4355 out var foreignNavPropertyInfo ,
4456 out var adjacentNavPropertyInfo ) )
4557 {
46- foundInSource = false ;
47- adjacentNavPropertyInfo = pi ;
58+ foundInSource = true ;
4859 }
49- else if ( TryGetForeignKeyPropertyInfos ( declaringType , foreignType ,
60+ else if ( TryGetForeignKeyPropertyInfos (
61+ targetType , sourceType , navPiName ,
62+ searchNavPiInSource : false ,
5063 out foreignKeyPropertyInfos ,
5164 out foreignNavPropertyInfo ,
5265 out adjacentNavPropertyInfo ) )
5366 {
54- foundInSource = true ;
67+ foundInSource = false ;
5568 }
5669 else
5770 {
5871 return null ;
5972 }
6073
61- var rightNavPi = pi ;
62- var rightPiType = rightNavPi . PropertyType . GetGenericTypeOrDefault ( ) ;
63- var rightKeysToJoinOn = foundInSource
64- ? PrimaryKeyConventionHelper . GetPrimaryKeyPropertyInfos ( rightPiType )
65- : foreignKeyPropertyInfos ;
66-
67- var leftNavPi = foreignNavPropertyInfo ;
68- var leftPiType = leftNavPi . PropertyType . GetGenericTypeOrDefault ( ) ;
69- var leftKeysToJoinOn = foundInSource
70- ? foreignKeyPropertyInfos
71- : PrimaryKeyConventionHelper . GetPrimaryKeyPropertyInfos ( leftPiType ) ;
74+ // if left is null, its probably because there is no bi-directional nav properties in both types
75+ PropertyInfo [ ] leftKeysToJoinOn = null ;
76+ var leftNavPi = foundInSource ? adjacentNavPropertyInfo : foreignNavPropertyInfo ;
77+ if ( leftNavPi != null )
78+ {
79+ var leftPiType = leftNavPi . PropertyType . GetGenericTypeOrDefault ( ) ;
80+ leftKeysToJoinOn = foundInSource
81+ ? foreignKeyPropertyInfos
82+ : PrimaryKeyConventionHelper . GetPrimaryKeyPropertyInfos ( leftPiType ) ;
83+ }
7284
73- var newLeftNavPi = foundInSource ? adjacentNavPropertyInfo : leftNavPi ;
85+ PropertyInfo [ ] rightKeysToJoinOn = null ;
86+ var rightNavPi = foundInSource ? foreignNavPropertyInfo : adjacentNavPropertyInfo ;
87+ if ( rightNavPi != null )
88+ {
89+ var rightPiType = rightNavPi . PropertyType . GetGenericTypeOrDefault ( ) ;
90+ rightKeysToJoinOn = foundInSource
91+ ? PrimaryKeyConventionHelper . GetPrimaryKeyPropertyInfos ( rightPiType )
92+ : foreignKeyPropertyInfos ;
93+ }
94+ // might be doing a backgward tarversal, which is the only reason why
95+ // left might have something but the right side wont
96+ else if ( leftNavPi != null )
97+ {
98+ rightKeysToJoinOn = foreignKeyPropertyInfos ;
99+ }
74100
75- return new Result ( newLeftNavPi , leftKeysToJoinOn , rightNavPi , rightKeysToJoinOn ) ;
101+ return new Result ( leftNavPi , leftKeysToJoinOn , rightNavPi , rightKeysToJoinOn ) ;
76102 }
77103
78- private static bool TryGetForeignKeyPropertyInfos ( Type source , Type target ,
104+ private static bool TryGetForeignKeyPropertyInfos ( Type sourceType , Type targetType , string navPiName , bool searchNavPiInSource ,
79105 out PropertyInfo [ ] foreignKeyPropertyInfos ,
80106 out PropertyInfo foreignNavPropertyInfo ,
81107 out PropertyInfo adjacentNavPropertyInfo )
82108 {
83- var propsFromSource = source . GetRuntimeProperties ( ) . Where ( ModelConventionHelper . IsColumnMapped ) . ToList ( ) ;
84- var propsFromTarget = target . GetRuntimeProperties ( ) . Where ( ModelConventionHelper . IsColumnMapped ) . ToList ( ) ;
85-
86- var foreignNavPi = propsFromSource . FirstOrDefault ( x => x . PropertyType == target ) ;
87- var adjacentNavPi = propsFromTarget . FirstOrDefault ( x => x . PropertyType == source ) ;
88-
89- var foreignKeyPiList = new List < PropertyInfo > ( ) ;
90-
91109 adjacentNavPropertyInfo = null ;
92110 foreignKeyPropertyInfos = null ;
93111 foreignNavPropertyInfo = null ;
94112
113+ var propsFromSource = sourceType . GetRuntimeProperties ( ) . Where ( ModelConventionHelper . IsColumnMapped ) . ToList ( ) ;
114+ var propsFromTarget = targetType . GetRuntimeProperties ( ) . Where ( ModelConventionHelper . IsColumnMapped ) . ToList ( ) ;
115+
116+ PropertyInfo foreignNavPi = null ;
117+ PropertyInfo adjacentNavPi = null ;
118+
119+ if ( ! string . IsNullOrEmpty ( navPiName ) )
120+ {
121+ if ( searchNavPiInSource )
122+ {
123+ foreignNavPi = propsFromSource . FirstOrDefault ( x => x . Name == navPiName ) ;
124+ adjacentNavPi = propsFromTarget . FirstOrDefault ( x => x . PropertyType == sourceType ) ;
125+ }
126+ else
127+ {
128+ adjacentNavPi = propsFromTarget . FirstOrDefault ( x => x . Name == navPiName ) ;
129+ foreignNavPi = propsFromSource . FirstOrDefault ( x => x . PropertyType == targetType ) ;
130+ }
131+ }
132+
133+ var foreignKeyPiList = new List < PropertyInfo > ( ) ;
134+
95135 if ( foreignNavPi != null )
96136 {
97137 // Gets by checking the annotations in source
@@ -108,7 +148,7 @@ private static bool TryGetForeignKeyPropertyInfos(Type source, Type target,
108148 string . Format (
109149 Resources . ForeignKeyAttributeOnPropertyNotFoundOnDependentType ,
110150 propertyInfosWithForeignKey . Name ,
111- source . FullName ,
151+ sourceType . FullName ,
112152 foreignKeyAttributeName ) ) ;
113153 }
114154 }
@@ -130,7 +170,7 @@ private static bool TryGetForeignKeyPropertyInfos(Type source, Type target,
130170 }
131171
132172 // Try to find by naming convention
133- var primaryKeyPropertyInfos = PrimaryKeyConventionHelper . GetPrimaryKeyPropertyInfos ( target ) ;
173+ var primaryKeyPropertyInfos = PrimaryKeyConventionHelper . GetPrimaryKeyPropertyInfos ( targetType ) ;
134174
135175 if ( ! foreignKeyPiList . Any ( ) && primaryKeyPropertyInfos . Any ( ) )
136176 {
0 commit comments