13
13
14
14
namespace JsonApiDotNetCore . Data
15
15
{
16
+ /// <inheritdoc />
16
17
public class DefaultEntityRepository < TEntity >
17
18
: DefaultEntityRepository < TEntity , int > ,
18
19
IEntityRepository < TEntity >
@@ -26,6 +27,10 @@ public DefaultEntityRepository(
26
27
{ }
27
28
}
28
29
30
+ /// <summary>
31
+ /// Provides a default repository implementation and is responsible for
32
+ /// abstracting any EF Core APIs away from the service layer.
33
+ /// </summary>
29
34
public class DefaultEntityRepository < TEntity , TId >
30
35
: IEntityRepository < TEntity , TId >
31
36
where TEntity : class , IIdentifiable < TId >
@@ -48,7 +53,7 @@ public DefaultEntityRepository(
48
53
_genericProcessorFactory = _jsonApiContext . GenericProcessorFactory ;
49
54
}
50
55
51
- /// </ inheritdoc>
56
+ /// <inheritdoc / >
52
57
public virtual IQueryable < TEntity > Get ( )
53
58
{
54
59
if ( _jsonApiContext . QuerySet ? . Fields != null && _jsonApiContext . QuerySet . Fields . Count > 0 )
@@ -57,41 +62,43 @@ public virtual IQueryable<TEntity> Get()
57
62
return _dbSet ;
58
63
}
59
64
60
- /// </ inheritdoc>
65
+ /// <inheritdoc / >
61
66
public virtual IQueryable < TEntity > Filter ( IQueryable < TEntity > entities , FilterQuery filterQuery )
62
67
{
63
68
return entities . Filter ( _jsonApiContext , filterQuery ) ;
64
69
}
65
70
66
- /// </ inheritdoc>
71
+ /// <inheritdoc / >
67
72
public virtual IQueryable < TEntity > Sort ( IQueryable < TEntity > entities , List < SortQuery > sortQueries )
68
73
{
69
74
return entities . Sort ( sortQueries ) ;
70
75
}
71
76
72
- /// </ inheritdoc>
77
+ /// <inheritdoc / >
73
78
public virtual async Task < TEntity > GetAsync ( TId id )
74
79
{
75
80
return await Get ( ) . SingleOrDefaultAsync ( e => e . Id . Equals ( id ) ) ;
76
81
}
77
82
78
- /// </ inheritdoc>
83
+ /// <inheritdoc / >
79
84
public virtual async Task < TEntity > GetAndIncludeAsync ( TId id , string relationshipName )
80
85
{
81
86
_logger . LogDebug ( $ "[JADN] GetAndIncludeAsync({ id } , { relationshipName } )") ;
82
87
83
- var result = await Include ( Get ( ) , relationshipName ) . SingleOrDefaultAsync ( e => e . Id . Equals ( id ) ) ;
88
+ var includedSet = await IncludeAsync ( Get ( ) , relationshipName ) ;
89
+ var result = await includedSet . SingleOrDefaultAsync ( e => e . Id . Equals ( id ) ) ;
84
90
85
91
return result ;
86
92
}
87
93
88
- /// </ inheritdoc>
94
+ /// <inheritdoc / >
89
95
public virtual async Task < TEntity > CreateAsync ( TEntity entity )
90
96
{
91
97
AttachRelationships ( ) ;
92
98
_dbSet . Add ( entity ) ;
93
99
94
100
await _context . SaveChangesAsync ( ) ;
101
+
95
102
return entity ;
96
103
}
97
104
@@ -129,7 +136,7 @@ private void AttachHasOnePointers()
129
136
_context . Entry ( relationship . Value ) . State = EntityState . Unchanged ;
130
137
}
131
138
132
- /// </ inheritdoc>
139
+ /// <inheritdoc / >
133
140
public virtual async Task < TEntity > UpdateAsync ( TId id , TEntity entity )
134
141
{
135
142
var oldEntity = await GetAsync ( id ) ;
@@ -148,14 +155,14 @@ public virtual async Task<TEntity> UpdateAsync(TId id, TEntity entity)
148
155
return oldEntity ;
149
156
}
150
157
151
- /// </ inheritdoc>
158
+ /// <inheritdoc / >
152
159
public async Task UpdateRelationshipsAsync ( object parent , RelationshipAttribute relationship , IEnumerable < string > relationshipIds )
153
160
{
154
161
var genericProcessor = _genericProcessorFactory . GetProcessor < IGenericProcessor > ( typeof ( GenericProcessor < > ) , relationship . Type ) ;
155
162
await genericProcessor . UpdateRelationshipsAsync ( parent , relationship , relationshipIds ) ;
156
163
}
157
164
158
- /// </ inheritdoc>
165
+ /// <inheritdoc / >
159
166
public virtual async Task < bool > DeleteAsync ( TId id )
160
167
{
161
168
var entity = await GetAsync ( id ) ;
@@ -170,7 +177,8 @@ public virtual async Task<bool> DeleteAsync(TId id)
170
177
return true ;
171
178
}
172
179
173
- /// </ inheritdoc>
180
+ /// <inheritdoc />
181
+ [ Obsolete ( "Use IncludeAsync" ) ]
174
182
public virtual IQueryable < TEntity > Include ( IQueryable < TEntity > entities , string relationshipName )
175
183
{
176
184
var entity = _jsonApiContext . RequestEntity ;
@@ -185,10 +193,57 @@ public virtual IQueryable<TEntity> Include(IQueryable<TEntity> entities, string
185
193
{
186
194
throw new JsonApiException ( 400 , $ "Including the relationship { relationshipName } on { entity . EntityName } is not allowed") ;
187
195
}
196
+
197
+ return entities . Include ( relationship . InternalRelationshipName ) ;
198
+ }
199
+
200
+ /// <inheritdoc />
201
+ public virtual async Task < IQueryable < TEntity > > IncludeAsync ( IQueryable < TEntity > entities , string relationshipName )
202
+ {
203
+ var entity = _jsonApiContext . RequestEntity ;
204
+ var relationship = entity . Relationships . FirstOrDefault ( r => r . PublicRelationshipName == relationshipName ) ;
205
+ if ( relationship == null )
206
+ {
207
+ throw new JsonApiException ( 400 , $ "Invalid relationship { relationshipName } on { entity . EntityName } ",
208
+ $ "{ entity . EntityName } does not have a relationship named { relationshipName } ") ;
209
+ }
210
+
211
+ if ( ! relationship . CanInclude )
212
+ {
213
+ throw new JsonApiException ( 400 , $ "Including the relationship { relationshipName } on { entity . EntityName } is not allowed") ;
214
+ }
215
+
216
+ await ReloadPointerAsync ( relationship ) ;
217
+
188
218
return entities . Include ( relationship . InternalRelationshipName ) ;
189
219
}
190
220
191
- /// </ inheritdoc>
221
+ /// <summary>
222
+ /// Ensure relationships on the provided entity have been fully loaded from the database.
223
+ /// </summary>
224
+ /// <remarks>
225
+ /// The only known case when this should be called is when a POST request is
226
+ /// sent with an ?include query.
227
+ ///
228
+ /// See https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/343
229
+ /// </remarks>
230
+ private async Task ReloadPointerAsync ( RelationshipAttribute relationshipAttr )
231
+ {
232
+ if ( relationshipAttr . IsHasOne && _jsonApiContext . HasOneRelationshipPointers . Get ( ) . TryGetValue ( relationshipAttr , out var pointer ) )
233
+ {
234
+ await _context . Entry ( pointer ) . ReloadAsync ( ) ;
235
+ }
236
+
237
+ if ( relationshipAttr . IsHasMany && _jsonApiContext . HasManyRelationshipPointers . Get ( ) . TryGetValue ( relationshipAttr , out var pointers ) )
238
+ {
239
+ foreach ( var hasManyPointer in pointers )
240
+ {
241
+ await _context . Entry ( hasManyPointer ) . ReloadAsync ( ) ;
242
+ }
243
+ }
244
+ }
245
+
246
+ /// <inheritdoc />
192
247
public virtual async Task < IEnumerable < TEntity > > PageAsync ( IQueryable < TEntity > entities , int pageSize , int pageNumber )
193
248
{
194
249
if ( pageNumber >= 0 )
@@ -209,23 +264,23 @@ public virtual async Task<IEnumerable<TEntity>> PageAsync(IQueryable<TEntity> en
209
264
. ToListAsync ( ) ;
210
265
}
211
266
212
- /// </ inheritdoc>
267
+ /// <inheritdoc / >
213
268
public async Task < int > CountAsync ( IQueryable < TEntity > entities )
214
269
{
215
270
return ( entities is IAsyncEnumerable < TEntity > )
216
271
? await entities . CountAsync ( )
217
272
: entities . Count ( ) ;
218
273
}
219
274
220
- /// </ inheritdoc>
275
+ /// <inheritdoc / >
221
276
public async Task < TEntity > FirstOrDefaultAsync ( IQueryable < TEntity > entities )
222
277
{
223
278
return ( entities is IAsyncEnumerable < TEntity > )
224
279
? await entities . FirstOrDefaultAsync ( )
225
280
: entities . FirstOrDefault ( ) ;
226
281
}
227
282
228
- /// </ inheritdoc>
283
+ /// <inheritdoc / >
229
284
public async Task < IReadOnlyList < TEntity > > ToListAsync ( IQueryable < TEntity > entities )
230
285
{
231
286
return ( entities is IAsyncEnumerable < TEntity > )
0 commit comments