@@ -147,14 +147,15 @@ public virtual async Task<TEntity> CreateAsync(TEntity entity)
147
147
foreach ( var relationshipAttr in _jsonApiContext . RelationshipsToUpdate ? . Keys )
148
148
{
149
149
var trackedRelationshipValue = GetTrackedRelationshipValue ( relationshipAttr , entity , out bool wasAlreadyTracked ) ;
150
- // LoadInverseRelationships(trackedRelationshipValue, relationshipAttribute)
150
+ LoadInverseRelationships ( trackedRelationshipValue , relationshipAttr ) ;
151
151
if ( wasAlreadyTracked )
152
152
{
153
153
/// We only need to reassign the relationship value to the to-be-added
154
154
/// entity when we're using a different instance (because this different one
155
155
/// was already tracked) than the one assigned to the to-be-created entity.
156
156
AssignRelationshipValue ( entity , trackedRelationshipValue , relationshipAttr ) ;
157
- } else if ( relationshipAttr is HasManyThroughAttribute throughAttr )
157
+ }
158
+ else if ( relationshipAttr is HasManyThroughAttribute throughAttr )
158
159
{
159
160
/// even if we don't have to reassign anything because of already tracked
160
161
/// 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)
167
168
return entity ;
168
169
}
169
170
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
+
170
191
171
192
/// <inheritdoc />
172
193
public void DetachRelationshipPointers ( TEntity entity )
173
194
{
174
195
175
196
foreach ( var relationshipAttr in _jsonApiContext . RelationshipsToUpdate . Keys )
176
197
{
177
- if ( relationshipAttr is HasOneAttribute hasOneAttr )
198
+ if ( relationshipAttr is HasOneAttribute hasOneAttr )
178
199
{
179
200
var relationshipValue = GetEntityResourceSeparationValue ( entity , hasOneAttr ) ?? ( IIdentifiable ) hasOneAttr . GetValue ( entity ) ;
180
201
if ( relationshipValue == null ) continue ;
181
202
_context . Entry ( relationshipValue ) . State = EntityState . Detached ;
182
203
183
204
}
184
- else
205
+ else
185
206
{
186
207
IEnumerable < IIdentifiable > relationshipValueList = ( IEnumerable < IIdentifiable > ) relationshipAttr . GetValue ( entity ) ;
187
208
/// 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)
220
241
{
221
242
LoadCurrentRelationships ( oldEntity , relationshipAttr ) ;
222
243
var trackedRelationshipValue = GetTrackedRelationshipValue ( relationshipAttr , updatedEntity , out bool wasAlreadyTracked ) ;
223
- // LoadInverseRelationships(trackedRelationshipValue, relationshipAttribute)
244
+ LoadInverseRelationships ( trackedRelationshipValue , relationshipAttr ) ;
224
245
AssignRelationshipValue ( oldEntity , trackedRelationshipValue , relationshipAttr ) ;
225
246
}
226
247
await _context . SaveChangesAsync ( ) ;
@@ -237,21 +258,21 @@ public virtual async Task<TEntity> UpdateAsync(TId id, TEntity updatedEntity)
237
258
private object GetTrackedRelationshipValue ( RelationshipAttribute relationshipAttr , TEntity entity , out bool wasAlreadyAttached )
238
259
{
239
260
wasAlreadyAttached = false ;
240
- if ( relationshipAttr is HasOneAttribute hasOneAttribute )
261
+ if ( relationshipAttr is HasOneAttribute hasOneAttr )
241
262
{
242
263
/// 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 ) ;
247
268
}
248
269
else
249
270
{
250
271
IEnumerable < IIdentifiable > relationshipValueList = ( IEnumerable < IIdentifiable > ) relationshipAttr . GetValue ( entity ) ;
251
272
/// This adds support for resource-entity separation in the case of one-to-many.
252
273
/// 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 ;
255
276
if ( relationshipValueList == null ) return null ;
256
277
return GetTrackedManyRelationshipValue ( relationshipValueList , relationshipAttr , ref wasAlreadyAttached ) ;
257
278
}
@@ -279,7 +300,7 @@ private IList GetTrackedManyRelationshipValue(IEnumerable<IIdentifiable> relatio
279
300
}
280
301
281
302
// 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 )
283
304
{
284
305
var tracked = AttachOrGetTracked ( relationshipValue ) ;
285
306
if ( tracked != null ) wasAlreadyAttached = true ;
0 commit comments