1010using JsonApiDotNetCore . Internal . Contracts ;
1111using JsonApiDotNetCore . Models ;
1212using JsonApiDotNetCore . Models . Links ;
13- using Microsoft . EntityFrameworkCore ;
1413using Microsoft . Extensions . Logging ;
1514
1615namespace JsonApiDotNetCore . Builders
1716{
1817 public class ResourceGraphBuilder : IResourceGraphBuilder
1918 {
20- private readonly List < ResourceContext > _entities = new List < ResourceContext > ( ) ;
21- private readonly List < ValidationResult > _validationResults = new List < ValidationResult > ( ) ;
22- private readonly IResourceNameFormatter _resourceNameFormatter = new KebabCaseFormatter ( ) ;
19+ private List < ResourceContext > _resources { get ; set ; } = new List < ResourceContext > ( ) ;
20+ private List < ValidationResult > _validationResults { get ; set ; } = new List < ValidationResult > ( ) ;
21+ private IResourceNameFormatter _formatter { get ; set ; } = new KebabCaseFormatter ( ) ;
2322
2423 public ResourceGraphBuilder ( ) { }
2524
2625 public ResourceGraphBuilder ( IResourceNameFormatter formatter )
2726 {
28- _resourceNameFormatter = formatter ;
27+ _formatter = formatter ;
2928 }
3029
3130 /// <inheritdoc />
3231 public IResourceGraph Build ( )
3332 {
34- _entities . ForEach ( SetResourceLinksOptions ) ;
35- var resourceGraph = new ResourceGraph ( _entities , _validationResults ) ;
33+ _resources . ForEach ( SetResourceLinksOptions ) ;
34+ var resourceGraph = new ResourceGraph ( _resources , _validationResults ) ;
3635 return resourceGraph ;
3736 }
3837
@@ -56,13 +55,19 @@ public IResourceGraphBuilder AddResource<TResource, TId>(string pluralizedTypeNa
5655 => AddResource ( typeof ( TResource ) , typeof ( TId ) , pluralizedTypeName ) ;
5756
5857 /// <inheritdoc />
59- public IResourceGraphBuilder AddResource ( Type entityType , Type idType , string pluralizedTypeName = null )
58+ public IResourceGraphBuilder AddResource ( Type resourceType , Type idType = null , string pluralizedTypeName = null )
6059 {
61- AssertEntityIsNotAlreadyDefined ( entityType ) ;
62-
63- pluralizedTypeName = pluralizedTypeName ?? _resourceNameFormatter . FormatResourceName ( entityType ) ;
64-
65- _entities . Add ( GetEntity ( pluralizedTypeName , entityType , idType ) ) ;
60+ AssertEntityIsNotAlreadyDefined ( resourceType ) ;
61+ if ( resourceType . Implements < IIdentifiable > ( ) )
62+ {
63+ pluralizedTypeName ??= _formatter . FormatResourceName ( resourceType ) ;
64+ idType ??= TypeLocator . GetIdType ( resourceType ) ;
65+ _resources . Add ( GetEntity ( pluralizedTypeName , resourceType , idType ) ) ;
66+ }
67+ else
68+ {
69+ _validationResults . Add ( new ValidationResult ( LogLevel . Warning , $ "{ resourceType } does not implement 'IIdentifiable<>'. ") ) ;
70+ }
6671
6772 return this ;
6873 }
@@ -93,7 +98,7 @@ protected virtual List<AttrAttribute> GetAttributes(Type entityType)
9398 {
9499 var idAttr = new AttrAttribute ( )
95100 {
96- PublicAttributeName = _resourceNameFormatter . FormatPropertyName ( prop ) ,
101+ PublicAttributeName = _formatter . FormatPropertyName ( prop ) ,
97102 PropertyInfo = prop ,
98103 InternalAttributeName = prop . Name
99104 } ;
@@ -105,7 +110,7 @@ protected virtual List<AttrAttribute> GetAttributes(Type entityType)
105110 if ( attribute == null )
106111 continue ;
107112
108- attribute . PublicAttributeName = attribute . PublicAttributeName ?? _resourceNameFormatter . FormatPropertyName ( prop ) ;
113+ attribute . PublicAttributeName = attribute . PublicAttributeName ?? _formatter . FormatPropertyName ( prop ) ;
109114 attribute . InternalAttributeName = prop . Name ;
110115 attribute . PropertyInfo = prop ;
111116
@@ -123,7 +128,7 @@ protected virtual List<RelationshipAttribute> GetRelationships(Type entityType)
123128 var attribute = ( RelationshipAttribute ) prop . GetCustomAttribute ( typeof ( RelationshipAttribute ) ) ;
124129 if ( attribute == null ) continue ;
125130
126- attribute . PublicRelationshipName = attribute . PublicRelationshipName ?? _resourceNameFormatter . FormatPropertyName ( prop ) ;
131+ attribute . PublicRelationshipName = attribute . PublicRelationshipName ?? _formatter . FormatPropertyName ( prop ) ;
127132 attribute . InternalRelationshipName = prop . Name ;
128133 attribute . RightType = GetRelationshipType ( attribute , prop ) ;
129134 attribute . LeftType = entityType ;
@@ -178,63 +183,9 @@ protected virtual Type GetRelationshipType(RelationshipAttribute relation, Prope
178183
179184 private Type GetResourceDefinitionType ( Type entityType ) => typeof ( ResourceDefinition < > ) . MakeGenericType ( entityType ) ;
180185
181- /// <inheritdoc />
182- public IResourceGraphBuilder AddDbContext < T > ( ) where T : DbContext
183- {
184- var contextType = typeof ( T ) ;
185- var contextProperties = contextType . GetProperties ( ) ;
186- foreach ( var property in contextProperties )
187- {
188- var dbSetType = property . PropertyType ;
189- if ( dbSetType . GetTypeInfo ( ) . IsGenericType
190- && dbSetType . GetGenericTypeDefinition ( ) == typeof ( DbSet < > ) )
191- {
192- var entityType = dbSetType . GetGenericArguments ( ) [ 0 ] ;
193- AssertEntityIsNotAlreadyDefined ( entityType ) ;
194- var ( isJsonApiResource , idType ) = GetIdType ( entityType ) ;
195- if ( isJsonApiResource )
196- _entities . Add ( GetEntity ( GetResourceNameFromDbSetProperty ( property , entityType ) , entityType , idType ) ) ;
197- }
198- }
199-
200- return this ;
201- }
202-
203- private string GetResourceNameFromDbSetProperty ( PropertyInfo property , Type resourceType )
204- {
205- // this check is actually duplicated in the DefaultResourceNameFormatter
206- // however, we perform it here so that we allow class attributes to be prioritized over
207- // the DbSet attribute. Eventually, the DbSet attribute should be deprecated.
208- //
209- // check the class definition first
210- // [Resource("models"] public class Model : Identifiable { /* ... */ }
211- if ( resourceType . GetCustomAttribute ( typeof ( ResourceAttribute ) ) is ResourceAttribute classResourceAttribute )
212- return classResourceAttribute . ResourceName ;
213-
214- // check the DbContext member next
215- // [Resource("models")] public DbSet<Model> Models { get; set; }
216- if ( property . GetCustomAttribute ( typeof ( ResourceAttribute ) ) is ResourceAttribute resourceAttribute )
217- return resourceAttribute . ResourceName ;
218-
219- // fallback to the established convention using the DbSet Property.Name
220- // e.g DbSet<FooBar> FooBars { get; set; } => "foo-bars"
221- return _resourceNameFormatter . FormatResourceName ( resourceType ) ;
222- }
223-
224- private ( bool isJsonApiResource , Type idType ) GetIdType ( Type resourceType )
225- {
226- var possible = TypeLocator . GetIdType ( resourceType ) ;
227- if ( possible . isJsonApiResource )
228- return possible ;
229-
230- _validationResults . Add ( new ValidationResult ( LogLevel . Warning , $ "{ resourceType } does not implement 'IIdentifiable<>'. ") ) ;
231-
232- return ( false , null ) ;
233- }
234-
235186 private void AssertEntityIsNotAlreadyDefined ( Type entityType )
236187 {
237- if ( _entities . Any ( e => e . ResourceType == entityType ) )
188+ if ( _resources . Any ( e => e . ResourceType == entityType ) )
238189 throw new InvalidOperationException ( $ "Cannot add entity type { entityType } to context resourceGraph, there is already an entity of that type configured.") ;
239190 }
240191 }
0 commit comments