@@ -147,14 +147,15 @@ public virtual async Task<TEntity> CreateAsync(TEntity entity)
147147 foreach ( var relationshipAttr in _jsonApiContext . RelationshipsToUpdate ? . Keys )
148148 {
149149 var trackedRelationshipValue = GetTrackedRelationshipValue ( relationshipAttr , entity , out bool wasAlreadyTracked ) ;
150- // LoadInverseRelationships(trackedRelationshipValue, relationshipAttribute)
150+ LoadInverseRelationships ( trackedRelationshipValue , relationshipAttr ) ;
151151 if ( wasAlreadyTracked )
152152 {
153153 /// We only need to reassign the relationship value to the to-be-added
154154 /// entity when we're using a different instance (because this different one
155155 /// was already tracked) than the one assigned to the to-be-created entity.
156156 AssignRelationshipValue ( entity , trackedRelationshipValue , relationshipAttr ) ;
157- } else if ( relationshipAttr is HasManyThroughAttribute throughAttr )
157+ }
158+ else if ( relationshipAttr is HasManyThroughAttribute throughAttr )
158159 {
159160 /// even if we don't have to reassign anything because of already tracked
160161 /// entities, we still need to assign the "through" entities in the case of many-to-many.
@@ -167,21 +168,41 @@ public virtual async Task<TEntity> CreateAsync(TEntity entity)
167168 return entity ;
168169 }
169170
171+ /// <summary>
172+ /// Loads the inverse relationships to prevent foreign key constraints from being violated
173+ /// to support implicit removes, see https://github.com/json-api-dotnet/JsonApiDotNetCore/issues/502.
174+ /// </summary>
175+ private void LoadInverseRelationships ( object trackedRelationshipValue , RelationshipAttribute relationshipAttr )
176+ {
177+ if ( relationshipAttr . InverseNavigation == null ) return ;
178+ if ( relationshipAttr is HasOneAttribute hasOneAttr )
179+ {
180+ _context . Entry ( ( IIdentifiable ) trackedRelationshipValue ) . Reference ( hasOneAttr . InverseNavigation ) . Load ( ) ;
181+ }
182+ else if ( relationshipAttr is HasManyAttribute hasManyAttr )
183+ {
184+ foreach ( IIdentifiable relationshipValue in ( IList ) trackedRelationshipValue )
185+ {
186+ _context . Entry ( ( IIdentifiable ) trackedRelationshipValue ) . Reference ( hasManyAttr . InverseNavigation ) . Load ( ) ;
187+ }
188+ }
189+ }
190+
170191
171192 /// <inheritdoc />
172193 public void DetachRelationshipPointers ( TEntity entity )
173194 {
174195
175196 foreach ( var relationshipAttr in _jsonApiContext . RelationshipsToUpdate . Keys )
176197 {
177- if ( relationshipAttr is HasOneAttribute hasOneAttr )
198+ if ( relationshipAttr is HasOneAttribute hasOneAttr )
178199 {
179200 var relationshipValue = GetEntityResourceSeparationValue ( entity , hasOneAttr ) ?? ( IIdentifiable ) hasOneAttr . GetValue ( entity ) ;
180201 if ( relationshipValue == null ) continue ;
181202 _context . Entry ( relationshipValue ) . State = EntityState . Detached ;
182203
183204 }
184- else
205+ else
185206 {
186207 IEnumerable < IIdentifiable > relationshipValueList = ( IEnumerable < IIdentifiable > ) relationshipAttr . GetValue ( entity ) ;
187208 /// This adds support for resource-entity separation in the case of one-to-many.
@@ -220,7 +241,7 @@ public virtual async Task<TEntity> UpdateAsync(TId id, TEntity updatedEntity)
220241 {
221242 LoadCurrentRelationships ( oldEntity , relationshipAttr ) ;
222243 var trackedRelationshipValue = GetTrackedRelationshipValue ( relationshipAttr , updatedEntity , out bool wasAlreadyTracked ) ;
223- // LoadInverseRelationships(trackedRelationshipValue, relationshipAttribute)
244+ LoadInverseRelationships ( trackedRelationshipValue , relationshipAttr ) ;
224245 AssignRelationshipValue ( oldEntity , trackedRelationshipValue , relationshipAttr ) ;
225246 }
226247 await _context . SaveChangesAsync ( ) ;
@@ -237,21 +258,21 @@ public virtual async Task<TEntity> UpdateAsync(TId id, TEntity updatedEntity)
237258 private object GetTrackedRelationshipValue ( RelationshipAttribute relationshipAttr , TEntity entity , out bool wasAlreadyAttached )
238259 {
239260 wasAlreadyAttached = false ;
240- if ( relationshipAttr is HasOneAttribute hasOneAttribute )
261+ if ( relationshipAttr is HasOneAttribute hasOneAttr )
241262 {
242263 /// This adds support for resource-entity separation in the case of one-to-one.
243- var relationshipValue = GetEntityResourceSeparationValue ( entity , hasOneAttribute ) ?? ( IIdentifiable ) hasOneAttribute . GetValue ( entity ) ;
244- if ( relationshipValue == null )
245- return null ;
246- return GetTrackedHasOneRelationshipValue ( relationshipValue , hasOneAttribute , ref wasAlreadyAttached ) ;
264+ var relationshipValue = GetEntityResourceSeparationValue ( entity , hasOneAttr ) ?? ( IIdentifiable ) hasOneAttr . GetValue ( entity ) ;
265+ if ( relationshipValue == null )
266+ return null ;
267+ return GetTrackedHasOneRelationshipValue ( relationshipValue , hasOneAttr , ref wasAlreadyAttached ) ;
247268 }
248269 else
249270 {
250271 IEnumerable < IIdentifiable > relationshipValueList = ( IEnumerable < IIdentifiable > ) relationshipAttr . GetValue ( entity ) ;
251272 /// This adds support for resource-entity separation in the case of one-to-many.
252273 /// todo: currently there is no support for many to many relations.
253- if ( relationshipAttr is HasManyAttribute hasMany )
254- relationshipValueList = GetEntityResourceSeparationValue ( entity , hasMany ) ?? relationshipValueList ;
274+ if ( relationshipAttr is HasManyAttribute hasMany )
275+ relationshipValueList = GetEntityResourceSeparationValue ( entity , hasMany ) ?? relationshipValueList ;
255276 if ( relationshipValueList == null ) return null ;
256277 return GetTrackedManyRelationshipValue ( relationshipValueList , relationshipAttr , ref wasAlreadyAttached ) ;
257278 }
@@ -279,7 +300,7 @@ private IList GetTrackedManyRelationshipValue(IEnumerable<IIdentifiable> relatio
279300 }
280301
281302 // helper method used in GetTrackedRelationshipValue. See comments there.
282- private IIdentifiable GetTrackedHasOneRelationshipValue ( IIdentifiable relationshipValue , HasOneAttribute hasOneAttribute , ref bool wasAlreadyAttached )
303+ private IIdentifiable GetTrackedHasOneRelationshipValue ( IIdentifiable relationshipValue , HasOneAttribute hasOneAttr , ref bool wasAlreadyAttached )
283304 {
284305 var tracked = AttachOrGetTracked ( relationshipValue ) ;
285306 if ( tracked != null ) wasAlreadyAttached = true ;
0 commit comments