1313
1414namespace JsonApiDotNetCore . Data
1515{
16+ /// <inheritdoc />
1617 public class DefaultEntityRepository < TEntity >
1718 : DefaultEntityRepository < TEntity , int > ,
1819 IEntityRepository < TEntity >
@@ -26,8 +27,13 @@ public DefaultEntityRepository(
2627 { }
2728 }
2829
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>
2934 public class DefaultEntityRepository < TEntity , TId >
30- : IEntityRepository < TEntity , TId >
35+ : IEntityRepository < TEntity , TId > ,
36+ IEntityFrameworkRepository < TEntity >
3137 where TEntity : class , IIdentifiable < TId >
3238 {
3339 private readonly DbContext _context ;
@@ -48,7 +54,7 @@ public DefaultEntityRepository(
4854 _genericProcessorFactory = _jsonApiContext . GenericProcessorFactory ;
4955 }
5056
51- /// </ inheritdoc>
57+ /// <inheritdoc / >
5258 public virtual IQueryable < TEntity > Get ( )
5359 {
5460 if ( _jsonApiContext . QuerySet ? . Fields != null && _jsonApiContext . QuerySet . Fields . Count > 0 )
@@ -57,41 +63,43 @@ public virtual IQueryable<TEntity> Get()
5763 return _dbSet ;
5864 }
5965
60- /// </ inheritdoc>
66+ /// <inheritdoc / >
6167 public virtual IQueryable < TEntity > Filter ( IQueryable < TEntity > entities , FilterQuery filterQuery )
6268 {
6369 return entities . Filter ( _jsonApiContext , filterQuery ) ;
6470 }
6571
66- /// </ inheritdoc>
72+ /// <inheritdoc / >
6773 public virtual IQueryable < TEntity > Sort ( IQueryable < TEntity > entities , List < SortQuery > sortQueries )
6874 {
6975 return entities . Sort ( sortQueries ) ;
7076 }
7177
72- /// </ inheritdoc>
78+ /// <inheritdoc / >
7379 public virtual async Task < TEntity > GetAsync ( TId id )
7480 {
7581 return await Get ( ) . SingleOrDefaultAsync ( e => e . Id . Equals ( id ) ) ;
7682 }
7783
78- /// </ inheritdoc>
84+ /// <inheritdoc / >
7985 public virtual async Task < TEntity > GetAndIncludeAsync ( TId id , string relationshipName )
8086 {
8187 _logger . LogDebug ( $ "[JADN] GetAndIncludeAsync({ id } , { relationshipName } )") ;
8288
83- var result = await Include ( Get ( ) , relationshipName ) . SingleOrDefaultAsync ( e => e . Id . Equals ( id ) ) ;
89+ var includedSet = Include ( Get ( ) , relationshipName ) ;
90+ var result = await includedSet . SingleOrDefaultAsync ( e => e . Id . Equals ( id ) ) ;
8491
8592 return result ;
8693 }
8794
88- /// </ inheritdoc>
95+ /// <inheritdoc / >
8996 public virtual async Task < TEntity > CreateAsync ( TEntity entity )
9097 {
9198 AttachRelationships ( ) ;
9299 _dbSet . Add ( entity ) ;
93100
94101 await _context . SaveChangesAsync ( ) ;
102+
95103 return entity ;
96104 }
97105
@@ -101,6 +109,28 @@ protected virtual void AttachRelationships()
101109 AttachHasOnePointers ( ) ;
102110 }
103111
112+ /// <inheritdoc />
113+ public void DetachRelationshipPointers ( TEntity entity )
114+ {
115+ foreach ( var hasOneRelationship in _jsonApiContext . HasOneRelationshipPointers . Get ( ) )
116+ {
117+ _context . Entry ( hasOneRelationship . Value ) . State = EntityState . Detached ;
118+ }
119+
120+ foreach ( var hasManyRelationship in _jsonApiContext . HasManyRelationshipPointers . Get ( ) )
121+ {
122+ foreach ( var pointer in hasManyRelationship . Value )
123+ {
124+ _context . Entry ( pointer ) . State = EntityState . Detached ;
125+ }
126+
127+ // HACK: detaching has many relationships doesn't appear to be sufficient
128+ // the navigation property actually needs to be nulled out, otherwise
129+ // EF adds duplicate instances to the collection
130+ hasManyRelationship . Key . SetValue ( entity , null ) ;
131+ }
132+ }
133+
104134 /// <summary>
105135 /// This is used to allow creation of HasMany relationships when the
106136 /// dependent side of the relationship already exists.
@@ -129,7 +159,7 @@ private void AttachHasOnePointers()
129159 _context . Entry ( relationship . Value ) . State = EntityState . Unchanged ;
130160 }
131161
132- /// </ inheritdoc>
162+ /// <inheritdoc / >
133163 public virtual async Task < TEntity > UpdateAsync ( TId id , TEntity entity )
134164 {
135165 var oldEntity = await GetAsync ( id ) ;
@@ -148,14 +178,14 @@ public virtual async Task<TEntity> UpdateAsync(TId id, TEntity entity)
148178 return oldEntity ;
149179 }
150180
151- /// </ inheritdoc>
181+ /// <inheritdoc / >
152182 public async Task UpdateRelationshipsAsync ( object parent , RelationshipAttribute relationship , IEnumerable < string > relationshipIds )
153183 {
154184 var genericProcessor = _genericProcessorFactory . GetProcessor < IGenericProcessor > ( typeof ( GenericProcessor < > ) , relationship . Type ) ;
155185 await genericProcessor . UpdateRelationshipsAsync ( parent , relationship , relationshipIds ) ;
156186 }
157187
158- /// </ inheritdoc>
188+ /// <inheritdoc / >
159189 public virtual async Task < bool > DeleteAsync ( TId id )
160190 {
161191 var entity = await GetAsync ( id ) ;
@@ -170,7 +200,7 @@ public virtual async Task<bool> DeleteAsync(TId id)
170200 return true ;
171201 }
172202
173- /// </ inheritdoc>
203+ /// <inheritdoc / >
174204 public virtual IQueryable < TEntity > Include ( IQueryable < TEntity > entities , string relationshipName )
175205 {
176206 var entity = _jsonApiContext . RequestEntity ;
@@ -185,10 +215,11 @@ public virtual IQueryable<TEntity> Include(IQueryable<TEntity> entities, string
185215 {
186216 throw new JsonApiException ( 400 , $ "Including the relationship { relationshipName } on { entity . EntityName } is not allowed") ;
187217 }
218+
188219 return entities . Include ( relationship . InternalRelationshipName ) ;
189220 }
190221
191- /// </ inheritdoc>
222+ /// <inheritdoc / >
192223 public virtual async Task < IEnumerable < TEntity > > PageAsync ( IQueryable < TEntity > entities , int pageSize , int pageNumber )
193224 {
194225 if ( pageNumber >= 0 )
@@ -209,23 +240,23 @@ public virtual async Task<IEnumerable<TEntity>> PageAsync(IQueryable<TEntity> en
209240 . ToListAsync ( ) ;
210241 }
211242
212- /// </ inheritdoc>
243+ /// <inheritdoc / >
213244 public async Task < int > CountAsync ( IQueryable < TEntity > entities )
214245 {
215246 return ( entities is IAsyncEnumerable < TEntity > )
216247 ? await entities . CountAsync ( )
217248 : entities . Count ( ) ;
218249 }
219250
220- /// </ inheritdoc>
251+ /// <inheritdoc / >
221252 public async Task < TEntity > FirstOrDefaultAsync ( IQueryable < TEntity > entities )
222253 {
223254 return ( entities is IAsyncEnumerable < TEntity > )
224255 ? await entities . FirstOrDefaultAsync ( )
225256 : entities . FirstOrDefault ( ) ;
226257 }
227258
228- /// </ inheritdoc>
259+ /// <inheritdoc / >
229260 public async Task < IReadOnlyList < TEntity > > ToListAsync ( IQueryable < TEntity > entities )
230261 {
231262 return ( entities is IAsyncEnumerable < TEntity > )
0 commit comments