3
3
using System . Linq ;
4
4
using System . Reflection ;
5
5
using JsonApiDotNetCore . Extensions ;
6
+ using JsonApiDotNetCore . Graph ;
6
7
using JsonApiDotNetCore . Internal ;
7
8
using JsonApiDotNetCore . Models ;
8
9
using Microsoft . EntityFrameworkCore ;
@@ -32,13 +33,27 @@ public interface IContextGraphBuilder
32
33
/// <param name="pluralizedTypeName">The pluralized name that should be exposed by the API</param>
33
34
IContextGraphBuilder AddResource < TResource , TId > ( string pluralizedTypeName ) where TResource : class , IIdentifiable < TId > ;
34
35
36
+ /// <summary>
37
+ /// Add a json:api resource
38
+ /// </summary>
39
+ /// <param name="entityType">The resource model type</param>
40
+ /// <param name="idType">The resource model identifier type</param>
41
+ /// <param name="pluralizedTypeName">The pluralized name that should be exposed by the API</param>
42
+ IContextGraphBuilder AddResource ( Type entityType , Type idType , string pluralizedTypeName ) ;
43
+
35
44
/// <summary>
36
45
/// Add all the models that are part of the provided <see cref="DbContext" />
37
46
/// that also implement <see cref="IIdentifiable"/>
38
47
/// </summary>
39
48
/// <typeparam name="T">The <see cref="DbContext"/> implementation type.</typeparam>
40
49
IContextGraphBuilder AddDbContext < T > ( ) where T : DbContext ;
41
50
51
+ /// <summary>
52
+ /// Specify the <see cref="IResourceNameFormatter"/> used to format resource names.
53
+ /// </summary>
54
+ /// <param name="resourceNameFormatter">Formatter used to define exposed resource names by convention.</param>
55
+ IContextGraphBuilder UseNameFormatter ( IResourceNameFormatter resourceNameFormatter ) ;
56
+
42
57
/// <summary>
43
58
/// Which links to include. Defaults to <see cref="Link.All"/>.
44
59
/// </summary>
@@ -51,6 +66,8 @@ public class ContextGraphBuilder : IContextGraphBuilder
51
66
private List < ValidationResult > _validationResults = new List < ValidationResult > ( ) ;
52
67
53
68
private bool _usesDbContext ;
69
+ private IResourceNameFormatter _resourceNameFormatter = new DefaultResourceNameFormatter ( ) ;
70
+
54
71
public Link DocumentLinks { get ; set ; } = Link . All ;
55
72
56
73
public IContextGraph Build ( )
@@ -62,16 +79,20 @@ public IContextGraph Build()
62
79
return graph ;
63
80
}
64
81
82
+ /// <inheritdoc />
65
83
public IContextGraphBuilder AddResource < TResource > ( string pluralizedTypeName ) where TResource : class , IIdentifiable < int >
66
84
=> AddResource < TResource , int > ( pluralizedTypeName ) ;
67
85
86
+ /// <inheritdoc />
68
87
public IContextGraphBuilder AddResource < TResource , TId > ( string pluralizedTypeName ) where TResource : class , IIdentifiable < TId >
69
- {
70
- var entityType = typeof ( TResource ) ;
88
+ => AddResource ( typeof ( TResource ) , typeof ( TId ) , pluralizedTypeName ) ;
71
89
90
+ /// <inheritdoc />
91
+ public IContextGraphBuilder AddResource ( Type entityType , Type idType , string pluralizedTypeName )
92
+ {
72
93
AssertEntityIsNotAlreadyDefined ( entityType ) ;
73
94
74
- _entities . Add ( GetEntity ( pluralizedTypeName , entityType , typeof ( TId ) ) ) ;
95
+ _entities . Add ( GetEntity ( pluralizedTypeName , entityType , idType ) ) ;
75
96
76
97
return this ;
77
98
}
@@ -142,6 +163,7 @@ protected virtual Type GetRelationshipType(RelationshipAttribute relation, Prope
142
163
143
164
private Type GetResourceDefinitionType ( Type entityType ) => typeof ( ResourceDefinition < > ) . MakeGenericType ( entityType ) ;
144
165
166
+ /// <inheritdoc />
145
167
public IContextGraphBuilder AddDbContext < T > ( ) where T : DbContext
146
168
{
147
169
_usesDbContext = true ;
@@ -164,30 +186,38 @@ public IContextGraphBuilder AddDbContext<T>() where T : DbContext
164
186
var ( isJsonApiResource , idType ) = GetIdType ( entityType ) ;
165
187
166
188
if ( isJsonApiResource )
167
- _entities . Add ( GetEntity ( GetResourceName ( property ) , entityType , idType ) ) ;
189
+ _entities . Add ( GetEntity ( GetResourceNameFromDbSetProperty ( property , entityType ) , entityType , idType ) ) ;
168
190
}
169
191
}
170
192
171
193
return this ;
172
194
}
173
195
174
- private string GetResourceName ( PropertyInfo property )
196
+ private string GetResourceNameFromDbSetProperty ( PropertyInfo property , Type resourceType )
175
197
{
176
- var resourceAttribute = property . GetCustomAttribute ( typeof ( ResourceAttribute ) ) ;
177
- if ( resourceAttribute == null )
178
- return property . Name . Dasherize ( ) ;
179
-
180
- return ( ( ResourceAttribute ) resourceAttribute ) . ResourceName ;
198
+ // this check is actually duplicated in the DefaultResourceNameFormatter
199
+ // however, we perform it here so that we allow class attributes to be prioritized over
200
+ // the DbSet attribute. Eventually, the DbSet attribute should be deprecated.
201
+ //
202
+ // check the class definition first
203
+ // [Resource("models"] public class Model : Identifiable { /* ... */ }
204
+ if ( resourceType . GetCustomAttribute ( typeof ( ResourceAttribute ) ) is ResourceAttribute classResourceAttribute )
205
+ return classResourceAttribute . ResourceName ;
206
+
207
+ // check the DbContext member next
208
+ // [Resource("models")] public DbSet<Model> Models { get; set; }
209
+ if ( property . GetCustomAttribute ( typeof ( ResourceAttribute ) ) is ResourceAttribute resourceAttribute )
210
+ return resourceAttribute . ResourceName ;
211
+
212
+ // fallback to dsherized...this should actually check for a custom IResourceNameFormatter
213
+ return _resourceNameFormatter . FormatResourceName ( resourceType ) ;
181
214
}
182
215
183
216
private ( bool isJsonApiResource , Type idType ) GetIdType ( Type resourceType )
184
217
{
185
- var interfaces = resourceType . GetInterfaces ( ) ;
186
- foreach ( var type in interfaces )
187
- {
188
- if ( type . GetTypeInfo ( ) . IsGenericType && type . GetGenericTypeDefinition ( ) == typeof ( IIdentifiable < > ) )
189
- return ( true , type . GetGenericArguments ( ) [ 0 ] ) ;
190
- }
218
+ var possible = TypeLocator . GetIdType ( resourceType ) ;
219
+ if ( possible . isJsonApiResource )
220
+ return possible ;
191
221
192
222
_validationResults . Add ( new ValidationResult ( LogLevel . Warning , $ "{ resourceType } does not implement 'IIdentifiable<>'. ") ) ;
193
223
@@ -199,5 +229,12 @@ private void AssertEntityIsNotAlreadyDefined(Type entityType)
199
229
if ( _entities . Any ( e => e . EntityType == entityType ) )
200
230
throw new InvalidOperationException ( $ "Cannot add entity type { entityType } to context graph, there is already an entity of that type configured.") ;
201
231
}
232
+
233
+ /// <inheritdoc />
234
+ public IContextGraphBuilder UseNameFormatter ( IResourceNameFormatter resourceNameFormatter )
235
+ {
236
+ _resourceNameFormatter = resourceNameFormatter ;
237
+ return this ;
238
+ }
202
239
}
203
240
}
0 commit comments