1
1
using System ;
2
+ using System . Collections ;
2
3
using System . Collections . Generic ;
3
4
using System . Linq ;
4
5
using System . Reflection ;
@@ -23,24 +24,36 @@ public interface IContextGraphBuilder
23
24
/// Add a json:api resource
24
25
/// </summary>
25
26
/// <typeparam name="TResource">The resource model type</typeparam>
26
- /// <param name="pluralizedTypeName">The pluralized name that should be exposed by the API</param>
27
- IContextGraphBuilder AddResource < TResource > ( string pluralizedTypeName ) where TResource : class , IIdentifiable < int > ;
27
+ /// <param name="pluralizedTypeName">
28
+ /// The pluralized name that should be exposed by the API.
29
+ /// If nothing is specified, the configured name formatter will be used.
30
+ /// See <see cref="JsonApiOptions.ResourceNameFormatter" />.
31
+ /// </param>
32
+ IContextGraphBuilder AddResource < TResource > ( string pluralizedTypeName = null ) where TResource : class , IIdentifiable < int > ;
28
33
29
34
/// <summary>
30
35
/// Add a json:api resource
31
36
/// </summary>
32
37
/// <typeparam name="TResource">The resource model type</typeparam>
33
38
/// <typeparam name="TId">The resource model identifier type</typeparam>
34
- /// <param name="pluralizedTypeName">The pluralized name that should be exposed by the API</param>
35
- IContextGraphBuilder AddResource < TResource , TId > ( string pluralizedTypeName ) where TResource : class , IIdentifiable < TId > ;
39
+ /// <param name="pluralizedTypeName">
40
+ /// The pluralized name that should be exposed by the API.
41
+ /// If nothing is specified, the configured name formatter will be used.
42
+ /// See <see cref="JsonApiOptions.ResourceNameFormatter" />.
43
+ /// </param>
44
+ IContextGraphBuilder AddResource < TResource , TId > ( string pluralizedTypeName = null ) where TResource : class , IIdentifiable < TId > ;
36
45
37
46
/// <summary>
38
47
/// Add a json:api resource
39
48
/// </summary>
40
49
/// <param name="entityType">The resource model type</param>
41
50
/// <param name="idType">The resource model identifier type</param>
42
- /// <param name="pluralizedTypeName">The pluralized name that should be exposed by the API</param>
43
- IContextGraphBuilder AddResource ( Type entityType , Type idType , string pluralizedTypeName ) ;
51
+ /// <param name="pluralizedTypeName">
52
+ /// The pluralized name that should be exposed by the API.
53
+ /// If nothing is specified, the configured name formatter will be used.
54
+ /// See <see cref="JsonApiOptions.ResourceNameFormatter" />.
55
+ /// </param>
56
+ IContextGraphBuilder AddResource ( Type entityType , Type idType , string pluralizedTypeName = null ) ;
44
57
45
58
/// <summary>
46
59
/// Add all the models that are part of the provided <see cref="DbContext" />
@@ -80,18 +93,20 @@ public IContextGraph Build()
80
93
}
81
94
82
95
/// <inheritdoc />
83
- public IContextGraphBuilder AddResource < TResource > ( string pluralizedTypeName ) where TResource : class , IIdentifiable < int >
96
+ public IContextGraphBuilder AddResource < TResource > ( string pluralizedTypeName = null ) where TResource : class , IIdentifiable < int >
84
97
=> AddResource < TResource , int > ( pluralizedTypeName ) ;
85
98
86
99
/// <inheritdoc />
87
- public IContextGraphBuilder AddResource < TResource , TId > ( string pluralizedTypeName ) where TResource : class , IIdentifiable < TId >
100
+ public IContextGraphBuilder AddResource < TResource , TId > ( string pluralizedTypeName = null ) where TResource : class , IIdentifiable < TId >
88
101
=> AddResource ( typeof ( TResource ) , typeof ( TId ) , pluralizedTypeName ) ;
89
102
90
103
/// <inheritdoc />
91
- public IContextGraphBuilder AddResource ( Type entityType , Type idType , string pluralizedTypeName )
104
+ public IContextGraphBuilder AddResource ( Type entityType , Type idType , string pluralizedTypeName = null )
92
105
{
93
106
AssertEntityIsNotAlreadyDefined ( entityType ) ;
94
107
108
+ pluralizedTypeName = pluralizedTypeName ?? _resourceNameFormatter . FormatResourceName ( entityType ) ;
109
+
95
110
_entities . Add ( GetEntity ( pluralizedTypeName , entityType , idType ) ) ;
96
111
97
112
return this ;
@@ -152,7 +167,47 @@ protected virtual List<RelationshipAttribute> GetRelationships(Type entityType)
152
167
attribute . InternalRelationshipName = prop . Name ;
153
168
attribute . Type = GetRelationshipType ( attribute , prop ) ;
154
169
attributes . Add ( attribute ) ;
170
+
171
+ if ( attribute is HasManyThroughAttribute hasManyThroughAttribute ) {
172
+ var throughProperty = properties . SingleOrDefault ( p => p . Name == hasManyThroughAttribute . InternalThroughName ) ;
173
+ if ( throughProperty == null )
174
+ throw new JsonApiSetupException ( $ "Invalid '{ nameof ( HasManyThroughAttribute ) } ' on type '{ entityType } '. Type does not contain a property named '{ hasManyThroughAttribute . InternalThroughName } '.") ;
175
+
176
+ if ( throughProperty . PropertyType . Implements < IList > ( ) == false )
177
+ throw new JsonApiSetupException ( $ "Invalid '{ nameof ( HasManyThroughAttribute ) } ' on type '{ entityType } .{ throughProperty . Name } '. Property type does not implement IList.") ;
178
+
179
+ // assumption: the property should be a generic collection, e.g. List<ArticleTag>
180
+ if ( throughProperty . PropertyType . IsGenericType == false )
181
+ throw new JsonApiSetupException ( $ "Invalid '{ nameof ( HasManyThroughAttribute ) } ' on type '{ entityType } '. Expected through entity to be a generic type, such as List<{ prop . PropertyType } >.") ;
182
+
183
+ // Article → List<ArticleTag>
184
+ hasManyThroughAttribute . ThroughProperty = throughProperty ;
185
+
186
+ // ArticleTag
187
+ hasManyThroughAttribute . ThroughType = throughProperty . PropertyType . GetGenericArguments ( ) [ 0 ] ;
188
+
189
+ var throughProperties = hasManyThroughAttribute . ThroughType . GetProperties ( ) ;
190
+
191
+ // ArticleTag.Article
192
+ hasManyThroughAttribute . LeftProperty = throughProperties . SingleOrDefault ( x => x . PropertyType == entityType )
193
+ ?? throw new JsonApiSetupException ( $ "{ hasManyThroughAttribute . ThroughType } does not contain a navigation property to type { entityType } ") ;
194
+
195
+ // ArticleTag.ArticleId
196
+ var leftIdPropertyName = JsonApiOptions . RelatedIdMapper . GetRelatedIdPropertyName ( hasManyThroughAttribute . LeftProperty . Name ) ;
197
+ hasManyThroughAttribute . LeftIdProperty = throughProperties . SingleOrDefault ( x => x . Name == leftIdPropertyName )
198
+ ?? throw new JsonApiSetupException ( $ "{ hasManyThroughAttribute . ThroughType } does not contain a relationship id property to type { entityType } with name { leftIdPropertyName } ") ;
199
+
200
+ // Article → ArticleTag.Tag
201
+ hasManyThroughAttribute . RightProperty = throughProperties . SingleOrDefault ( x => x . PropertyType == hasManyThroughAttribute . Type )
202
+ ?? throw new JsonApiSetupException ( $ "{ hasManyThroughAttribute . ThroughType } does not contain a navigation property to type { hasManyThroughAttribute . Type } ") ;
203
+
204
+ // ArticleTag.TagId
205
+ var rightIdPropertyName = JsonApiOptions . RelatedIdMapper . GetRelatedIdPropertyName ( hasManyThroughAttribute . RightProperty . Name ) ;
206
+ hasManyThroughAttribute . RightIdProperty = throughProperties . SingleOrDefault ( x => x . Name == rightIdPropertyName )
207
+ ?? throw new JsonApiSetupException ( $ "{ hasManyThroughAttribute . ThroughType } does not contain a relationship id property to type { hasManyThroughAttribute . Type } with name { rightIdPropertyName } ") ;
208
+ }
155
209
}
210
+
156
211
return attributes ;
157
212
}
158
213
@@ -212,8 +267,9 @@ private string GetResourceNameFromDbSetProperty(PropertyInfo property, Type reso
212
267
if ( property . GetCustomAttribute ( typeof ( ResourceAttribute ) ) is ResourceAttribute resourceAttribute )
213
268
return resourceAttribute . ResourceName ;
214
269
215
- // fallback to dsherized...this should actually check for a custom IResourceNameFormatter
216
- return _resourceNameFormatter . FormatResourceName ( resourceType ) ;
270
+ // fallback to the established convention using the DbSet Property.Name
271
+ // e.g DbSet<FooBar> FooBars { get; set; } => "foo-bars"
272
+ return _resourceNameFormatter . ApplyCasingConvention ( property . Name ) ;
217
273
}
218
274
219
275
private ( bool isJsonApiResource , Type idType ) GetIdType ( Type resourceType )
0 commit comments