Skip to content

Commit 5f3e04b

Browse files
committed
refactor: AttachHasOnePointers
1 parent c4ebf4b commit 5f3e04b

File tree

2 files changed

+38
-19
lines changed

2 files changed

+38
-19
lines changed

src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public DefaultEntityRepository(
8282
public virtual IQueryable<TEntity> Get()
8383
=> _dbSet;
8484

85+
/// <inheritdoc />
8586
public virtual IQueryable<TEntity> Select(IQueryable<TEntity> entities, List<string> fields)
8687
{
8788
if (fields?.Count > 0)
@@ -228,16 +229,16 @@ private void AttachHasMany(TEntity entity, HasManyAttribute relationship, IList
228229
var relatedList = (IList)entity.GetType().GetProperty(relationship.EntityPropertyName)?.GetValue(entity);
229230
foreach (var related in relatedList)
230231
{
231-
if (_context.EntityIsTracked(related as IIdentifiable) == false)
232+
if (_context.EntityIsTracked((IIdentifiable)related) == false)
232233
_context.Entry(related).State = EntityState.Unchanged;
233234
}
234235
}
235236
else
236237
{
237238
foreach (var pointer in pointers)
238239
{
239-
if (_context.EntityIsTracked(pointer as IIdentifiable) == false)
240-
_context.Entry(pointer).State = EntityState.Unchanged;
240+
if (_context.EntityIsTracked((IIdentifiable)pointer) == false)
241+
_context.Entry(pointer).State = EntityState.Unchanged;
241242
}
242243
}
243244
}
@@ -266,31 +267,49 @@ private void AttachHasManyThrough(TEntity entity, HasManyThroughAttribute hasMan
266267

267268
/// <summary>
268269
/// This is used to allow creation of HasOne relationships when the
269-
/// independent side of the relationship already exists.
270270
/// </summary>
271271
private void AttachHasOnePointers(TEntity entity)
272272
{
273-
var relationships = _jsonApiContext.HasOneRelationshipPointers.Get();
273+
var relationships = _jsonApiContext
274+
.HasOneRelationshipPointers
275+
.Get();
276+
274277
foreach (var relationship in relationships)
275278
{
276-
if (relationship.Key.GetType() != typeof(HasOneAttribute))
277-
continue;
279+
var pointer = GetValueFromRelationship(entity, relationship);
280+
if (pointer == null) return;
281+
var trackedEntity = _context.GetTrackedEntity(pointer);
278282

279-
var hasOne = (HasOneAttribute)relationship.Key;
280-
if (hasOne.EntityPropertyName != null)
283+
// useful article: https://stackoverflow.com/questions/30987806/dbset-attachentity-vs-dbcontext-entryentity-state-entitystate-modified
284+
if (trackedEntity == null)
281285
{
282-
var relatedEntity = entity.GetType().GetProperty(hasOne.EntityPropertyName)?.GetValue(entity);
283-
if (relatedEntity != null && _context.Entry(relatedEntity).State == EntityState.Detached && _context.EntityIsTracked((IIdentifiable)relatedEntity) == false)
284-
_context.Entry(relatedEntity).State = EntityState.Unchanged;
285-
}
286-
else
286+
/// the relationship pointer is new to EF Core, but we are sure
287+
/// it exists in the database (json:api spec), so we attach it.
288+
_context.Entry(pointer).State = EntityState.Unchanged;
289+
} else
287290
{
288-
if (_context.Entry(relationship.Value).State == EntityState.Detached && _context.EntityIsTracked(relationship.Value) == false)
289-
_context.Entry(relationship.Value).State = EntityState.Unchanged;
291+
/// there already was an instance of this type and ID tracked
292+
/// by EF Core. Reattaching is not allowed, and from now on we
293+
/// will use the already attached one instead. (This entry might
294+
/// contain updated fields as a result of Business logic.
295+
relationships[relationship.Key] = trackedEntity;
290296
}
297+
291298
}
292299
}
293300

301+
IIdentifiable GetValueFromRelationship(TEntity principalEntity, KeyValuePair<RelationshipAttribute, IIdentifiable> relationship )
302+
{
303+
HasOneAttribute hasOne = (HasOneAttribute)relationship.Key;
304+
if (hasOne.EntityPropertyName != null)
305+
{
306+
var relatedEntity = principalEntity.GetType().GetProperty(hasOne.EntityPropertyName)?.GetValue(principalEntity);
307+
return (IIdentifiable)relatedEntity;
308+
}
309+
return relationship.Value;
310+
}
311+
312+
294313
/// <inheritdoc />
295314
public virtual async Task<TEntity> UpdateAsync(TId id, TEntity entity)
296315
{

src/JsonApiDotNetCore/Request/HasOneRelationshipPointers.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,18 @@ namespace JsonApiDotNetCore.Request
2828
/// </summary>
2929
public class HasOneRelationshipPointers
3030
{
31-
private Dictionary<RelationshipAttribute, IIdentifiable> _hasOneRelationships = new Dictionary<RelationshipAttribute, IIdentifiable>();
31+
private readonly Dictionary<HasOneAttribute, IIdentifiable> _hasOneRelationships = new Dictionary<HasOneAttribute, IIdentifiable>();
3232

3333
/// <summary>
3434
/// Add the relationship to the list of relationships that should be
3535
/// set in the repository layer.
3636
/// </summary>
37-
public void Add(RelationshipAttribute relationship, IIdentifiable entity)
37+
public void Add(HasOneAttribute relationship, IIdentifiable entity)
3838
=> _hasOneRelationships[relationship] = entity;
3939

4040
/// <summary>
4141
/// Get all the models that should be associated
4242
/// </summary>
43-
public Dictionary<RelationshipAttribute, IIdentifiable> Get() => _hasOneRelationships;
43+
public Dictionary<HasOneAttribute, IIdentifiable> Get() => _hasOneRelationships;
4444
}
4545
}

0 commit comments

Comments
 (0)