Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit b073a0b

Browse files
Merge pull request #378 from json-api-dotnet/feat/#39
[WIP] Feat/#39: Deeply Nested Inclusions
2 parents c1bf919 + b0e4fa1 commit b073a0b

File tree

9 files changed

+835
-43
lines changed

9 files changed

+835
-43
lines changed

‎src/JsonApiDotNetCore/Builders/DocumentBuilder.cs

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -192,24 +192,62 @@ private RelationshipData GetRelationshipData(RelationshipAttribute attr, Context
192192
return relationshipData;
193193
}
194194

195-
private List<DocumentData> GetIncludedEntities(List<DocumentData> included, ContextEntity contextEntity, IIdentifiable entity)
195+
private List<DocumentData> GetIncludedEntities(List<DocumentData> included, ContextEntity rootContextEntity, IIdentifiable rootResource)
196196
{
197-
contextEntity.Relationships.ForEach(r =>
197+
if(_jsonApiContext.IncludedRelationships!=null)
198198
{
199-
if (!RelationshipIsIncluded(r.PublicRelationshipName)) return;
199+
foreach(var relationshipName in _jsonApiContext.IncludedRelationships)
200+
{
201+
var relationshipChain = relationshipName.Split('.');
200202

201-
var navigationEntity = _jsonApiContext.ContextGraph.GetRelationship(entity, r.InternalRelationshipName);
203+
var contextEntity = rootContextEntity;
204+
var entity = rootResource;
205+
included = IncludeRelationshipChain(included, rootContextEntity, rootResource, relationshipChain, 0);
206+
}
207+
}
202208

203-
if (navigationEntity is IEnumerable hasManyNavigationEntity)
204-
foreach (IIdentifiable includedEntity in hasManyNavigationEntity)
205-
included = AddIncludedEntity(included, includedEntity);
206-
else
207-
included = AddIncludedEntity(included, (IIdentifiable)navigationEntity);
208-
});
209+
return included;
210+
}
209211

212+
private List<DocumentData> IncludeRelationshipChain(
213+
List<DocumentData> included, ContextEntity parentEntity, IIdentifiable parentResource, string[] relationshipChain, int relationshipChainIndex)
214+
{
215+
var requestedRelationship = relationshipChain[relationshipChainIndex];
216+
var relationship = parentEntity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == requestedRelationship);
217+
var navigationEntity = _jsonApiContext.ContextGraph.GetRelationship(parentResource, relationship.InternalRelationshipName);
218+
if (navigationEntity is IEnumerable hasManyNavigationEntity)
219+
{
220+
foreach (IIdentifiable includedEntity in hasManyNavigationEntity)
221+
{
222+
included = AddIncludedEntity(included, includedEntity);
223+
included = IncludeSingleResourceRelationships(included, includedEntity, relationship, relationshipChain, relationshipChainIndex);
224+
}
225+
}
226+
else
227+
{
228+
included = AddIncludedEntity(included, (IIdentifiable)navigationEntity);
229+
included = IncludeSingleResourceRelationships(included, (IIdentifiable)navigationEntity, relationship, relationshipChain, relationshipChainIndex);
230+
}
231+
232+
return included;
233+
}
234+
235+
private List<DocumentData> IncludeSingleResourceRelationships(
236+
List<DocumentData> included, IIdentifiable navigationEntity, RelationshipAttribute relationship, string[] relationshipChain, int relationshipChainIndex)
237+
{
238+
if(relationshipChainIndex < relationshipChain.Length)
239+
{
240+
var nextContextEntity = _jsonApiContext.ContextGraph.GetContextEntity(relationship.Type);
241+
var resource = (IIdentifiable)navigationEntity;
242+
// recursive call
243+
if(relationshipChainIndex < relationshipChain.Length - 1)
244+
included = IncludeRelationshipChain(included, nextContextEntity, resource, relationshipChain, relationshipChainIndex + 1);
245+
}
246+
210247
return included;
211248
}
212249

250+
213251
private List<DocumentData> AddIncludedEntity(List<DocumentData> entities, IIdentifiable entity)
214252
{
215253
var includedEntity = GetIncludedEntity(entity);
@@ -245,12 +283,6 @@ private DocumentData GetIncludedEntity(IIdentifiable entity)
245283
return data;
246284
}
247285

248-
private bool RelationshipIsIncluded(string relationshipName)
249-
{
250-
return _jsonApiContext.IncludedRelationships != null &&
251-
_jsonApiContext.IncludedRelationships.Contains(relationshipName);
252-
}
253-
254286
private List<ResourceIdentifierObject> GetRelationships(IEnumerable<object> entities)
255287
{
256288
var objType = entities.GetElementType();

‎src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -203,20 +203,38 @@ public virtual async Task<bool> DeleteAsync(TId id)
203203
/// <inheritdoc />
204204
public virtual IQueryable<TEntity> Include(IQueryable<TEntity> entities, string relationshipName)
205205
{
206+
if(string.IsNullOrWhiteSpace(relationshipName)) throw new JsonApiException(400, "Include parameter must not be empty if provided");
207+
208+
var relationshipChain = relationshipName.Split('.');
209+
210+
// variables mutated in recursive loop
211+
// TODO: make recursive method
212+
string internalRelationshipPath = null;
206213
var entity = _jsonApiContext.RequestEntity;
207-
var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == relationshipName);
208-
if (relationship == null)
214+
for(var i = 0; i < relationshipChain.Length; i++)
209215
{
210-
throw new JsonApiException(400, $"Invalid relationship {relationshipName} on {entity.EntityName}",
211-
$"{entity.EntityName} does not have a relationship named {relationshipName}");
212-
}
216+
var requestedRelationship = relationshipChain[i];
217+
var relationship = entity.Relationships.FirstOrDefault(r => r.PublicRelationshipName == requestedRelationship);
218+
if (relationship == null)
219+
{
220+
throw new JsonApiException(400, $"Invalid relationship {requestedRelationship} on {entity.EntityName}",
221+
$"{entity.EntityName} does not have a relationship named {requestedRelationship}");
222+
}
213223

214-
if (!relationship.CanInclude)
215-
{
216-
throw new JsonApiException(400, $"Including the relationship {relationshipName} on {entity.EntityName} is not allowed");
224+
if (relationship.CanInclude == false)
225+
{
226+
throw new JsonApiException(400, $"Including the relationship {requestedRelationship} on {entity.EntityName} is not allowed");
227+
}
228+
229+
internalRelationshipPath = (internalRelationshipPath == null)
230+
? relationship.InternalRelationshipName
231+
: $"{internalRelationshipPath}.{relationship.InternalRelationshipName}";
232+
233+
if(i < relationshipChain.Length)
234+
entity = _jsonApiContext.ContextGraph.GetContextEntity(relationship.Type);
217235
}
218236

219-
return entities.Include(relationship.InternalRelationshipName);
237+
return entities.Include(internalRelationshipPath);
220238
}
221239

222240
/// <inheritdoc />

‎src/JsonApiDotNetCore/Models/RelationshipAttribute.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ public bool TryGetHasMany(out HasManyAttribute result)
5454

5555
public abstract void SetValue(object entity, object newValue);
5656

57+
public object GetValue(object entity) => entity
58+
?.GetType()
59+
.GetProperty(InternalRelationshipName)
60+
.GetValue(entity);
61+
5762
public override string ToString()
5863
{
5964
return base.ToString() + ":" + PublicRelationshipName;

‎src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,19 @@ private object SetHasOneRelationship(object entity,
214214
SetHasOneForeignKeyValue(entity, attr, foreignKeyProperty, rio);
215215
SetHasOneNavigationPropertyValue(entity, attr, rio, included);
216216

217+
// recursive call ...
218+
if(included != null)
219+
{
220+
var navigationPropertyValue = attr.GetValue(entity);
221+
var contextGraphEntity = _jsonApiContext.ContextGraph.GetContextEntity(attr.Type);
222+
if(navigationPropertyValue != null && contextGraphEntity != null)
223+
{
224+
var includedResource = included.SingleOrDefault(r => r.Type == rio.Type && r.Id == rio.Id);
225+
if(includedResource != null)
226+
SetRelationships(navigationPropertyValue, contextGraphEntity, includedResource.Relationships, included);
227+
}
228+
}
229+
217230
return entity;
218231
}
219232

‎src/JsonApiDotNetCore/Services/QueryParser.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,6 @@ protected virtual List<SortQuery> ParseSortParameters(string value)
179179

180180
protected virtual List<string> ParseIncludedRelationships(string value)
181181
{
182-
const string NESTED_DELIMITER = ".";
183-
if (value.Contains(NESTED_DELIMITER))
184-
throw new JsonApiException(400, "Deeply nested relationships are not supported");
185-
186182
return value
187183
.Split(QueryConstants.COMMA)
188184
.ToList();

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /