Skip to content

Commit 0b0e8d1

Browse files
committed
feat(relationships): implement relational attrs
1 parent 07ff717 commit 0b0e8d1

File tree

13 files changed

+61
-84
lines changed

13 files changed

+61
-84
lines changed

src/JsonApiDotNetCore/Builders/DocumentBuilder.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -124,25 +124,25 @@ private void _addRelationships(DocumentData data, ContextEntity contextEntity, I
124124
{
125125
Links = new Links
126126
{
127-
Self = linkBuilder.GetSelfRelationLink(contextEntity.EntityName, entity.Id.ToString(), r.RelationshipName),
128-
Related = linkBuilder.GetRelatedRelationLink(contextEntity.EntityName, entity.Id.ToString(), r.RelationshipName)
127+
Self = linkBuilder.GetSelfRelationLink(contextEntity.EntityName, entity.Id.ToString(), r.InternalRelationshipName),
128+
Related = linkBuilder.GetRelatedRelationLink(contextEntity.EntityName, entity.Id.ToString(), r.InternalRelationshipName)
129129
}
130130
};
131131

132-
if (_relationshipIsIncluded(r.RelationshipName))
132+
if (_relationshipIsIncluded(r.InternalRelationshipName))
133133
{
134134
var navigationEntity = _jsonApiContext.ContextGraph
135-
.GetRelationship(entity, r.RelationshipName);
135+
.GetRelationship(entity, r.InternalRelationshipName);
136136

137137
if(navigationEntity == null)
138138
relationshipData.SingleData = null;
139139
else if (navigationEntity is IEnumerable)
140-
relationshipData.ManyData = _getRelationships((IEnumerable<object>)navigationEntity, r.RelationshipName);
140+
relationshipData.ManyData = _getRelationships((IEnumerable<object>)navigationEntity, r.InternalRelationshipName);
141141
else
142-
relationshipData.SingleData = _getRelationship(navigationEntity, r.RelationshipName);
142+
relationshipData.SingleData = _getRelationship(navigationEntity, r.InternalRelationshipName);
143143
}
144144

145-
data.Relationships.Add(r.RelationshipName.Dasherize(), relationshipData);
145+
data.Relationships.Add(r.InternalRelationshipName.Dasherize(), relationshipData);
146146
});
147147
}
148148

@@ -152,9 +152,9 @@ private List<DocumentData> _getIncludedEntities(ContextEntity contextEntity, IId
152152

153153
contextEntity.Relationships.ForEach(r =>
154154
{
155-
if (!_relationshipIsIncluded(r.RelationshipName)) return;
155+
if (!_relationshipIsIncluded(r.InternalRelationshipName)) return;
156156

157-
var navigationEntity = _jsonApiContext.ContextGraph.GetRelationship(entity, r.RelationshipName);
157+
var navigationEntity = _jsonApiContext.ContextGraph.GetRelationship(entity, r.InternalRelationshipName);
158158

159159
if (navigationEntity is IEnumerable)
160160
foreach (var includedEntity in (IEnumerable)navigationEntity)

src/JsonApiDotNetCore/Controllers/JsonApiController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ public virtual async Task<IActionResult> PatchRelationshipsAsync(TId id, string
183183
var relationship = _jsonApiContext.ContextGraph
184184
.GetContextEntity(typeof(T))
185185
.Relationships
186-
.FirstOrDefault(r => r.RelationshipName == relationshipName);
186+
.FirstOrDefault(r => r.InternalRelationshipName == relationshipName);
187187

188188
var relationshipIds = relationships.Select(r=>r.Id);
189189

src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ public virtual async Task<TEntity> UpdateAsync(TId id, TEntity entity)
108108
return oldEntity;
109109
}
110110

111-
public async Task UpdateRelationshipsAsync(object parent, Relationship relationship, IEnumerable<string> relationshipIds)
111+
public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds)
112112
{
113-
var genericProcessor = GenericProcessorFactory.GetProcessor(relationship.BaseType, _context);
113+
var genericProcessor = GenericProcessorFactory.GetProcessor(relationship.Type, _context);
114114
await genericProcessor.UpdateRelationshipsAsync(parent, relationship, relationshipIds);
115115
}
116116

@@ -131,7 +131,7 @@ public virtual async Task<bool> DeleteAsync(TId id)
131131
public virtual IQueryable<TEntity> Include(IQueryable<TEntity> entities, string relationshipName)
132132
{
133133
var entity = _jsonApiContext.RequestEntity;
134-
if(entity.Relationships.Any(r => r.RelationshipName == relationshipName))
134+
if(entity.Relationships.Any(r => r.InternalRelationshipName == relationshipName))
135135
return entities.Include(relationshipName);
136136

137137
throw new JsonApiException("400", "Invalid relationship",

src/JsonApiDotNetCore/Data/IEntityRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public interface IEntityRepository<TEntity, in TId>
3434

3535
Task<TEntity> UpdateAsync(TId id, TEntity entity);
3636

37-
Task UpdateRelationshipsAsync(object parent, Relationship relationship, IEnumerable<string> relationshipIds);
37+
Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds);
3838

3939
Task<bool> DeleteAsync(TId id);
4040
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System;
2-
using JsonApiDotNetCore.Models;
32
using System.Collections.Generic;
3+
using JsonApiDotNetCore.Models;
44

55
namespace JsonApiDotNetCore.Internal
66
{
@@ -9,6 +9,6 @@ public class ContextEntity
99
public string EntityName { get; set; }
1010
public Type EntityType { get; set; }
1111
public List<AttrAttribute> Attributes { get; set; }
12-
public List<Relationship> Relationships { get; set; }
12+
public List<RelationshipAttribute> Relationships { get; set; }
1313
}
1414
}

src/JsonApiDotNetCore/Internal/ContextGraph.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ public string GetRelationshipName<TParent>(string relationshipName)
4646
e.EntityType == entityType)
4747
.Relationships
4848
.FirstOrDefault(r =>
49-
r.RelationshipName.ToLower() == relationshipName.ToLower())
50-
?.RelationshipName;
49+
r.InternalRelationshipName.ToLower() == relationshipName.ToLower())
50+
?.InternalRelationshipName;
5151
}
5252
}
5353
}

src/JsonApiDotNetCore/Internal/ContextGraphBuilder.cs

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ public class ContextGraphBuilder<T> where T : DbContext
1515
public ContextGraph<T> Build()
1616
{
1717
_getFirstLevelEntities();
18-
_loadRelationships();
1918

2019
var graph = new ContextGraph<T>
2120
{
@@ -42,7 +41,8 @@ private void _getFirstLevelEntities()
4241
entities.Add(new ContextEntity {
4342
EntityName = property.Name,
4443
EntityType = entityType,
45-
Attributes = _getAttributes(entityType)
44+
Attributes = _getAttributes(entityType),
45+
Relationships = _getRelationships(entityType)
4646
});
4747
}
4848
}
@@ -66,38 +66,29 @@ private List<AttrAttribute> _getAttributes(Type entityType)
6666
return attributes;
6767
}
6868

69-
private void _loadRelationships()
70-
{
71-
_entities.ForEach(entity => {
72-
73-
var relationships = new List<Relationship>();
74-
var properties = entity.EntityType.GetProperties();
75-
76-
foreach(var entityProperty in properties)
77-
{
78-
var propertyType = entityProperty.PropertyType;
79-
80-
if(_isValidEntity(propertyType)
81-
|| (propertyType.GetTypeInfo().IsGenericType && _isValidEntity(propertyType.GetGenericArguments()[0])))
82-
relationships.Add(_getRelationshipFromPropertyInfo(entityProperty));
83-
}
84-
85-
entity.Relationships = relationships;
86-
});
87-
}
88-
89-
private bool _isValidEntity(Type type)
69+
private List<RelationshipAttribute> _getRelationships(Type entityType)
9070
{
91-
var validEntityRelationshipTypes = _entities.Select(e => e.EntityType);
92-
return validEntityRelationshipTypes.Contains(type);
71+
var attributes = new List<RelationshipAttribute>();
72+
73+
var properties = entityType.GetProperties();
74+
75+
foreach(var prop in properties)
76+
{
77+
var attribute = (RelationshipAttribute)prop.GetCustomAttribute(typeof(RelationshipAttribute));
78+
if(attribute == null) continue;
79+
attribute.InternalRelationshipName = prop.Name;
80+
attribute.Type = _getRelationshipType(attribute, prop);
81+
attributes.Add(attribute);
82+
}
83+
return attributes;
9384
}
9485

95-
private Relationship _getRelationshipFromPropertyInfo(PropertyInfo propertyInfo)
86+
private Type _getRelationshipType(RelationshipAttribute relation, PropertyInfo prop)
9687
{
97-
return new Relationship {
98-
Type = propertyInfo.PropertyType,
99-
RelationshipName = propertyInfo.Name
100-
};
88+
if(relation.IsHasMany)
89+
return prop.PropertyType.GetGenericArguments()[0];
90+
else
91+
return prop.PropertyType;
10192
}
10293
}
10394
}

src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ public GenericProcessor(DbContext context)
1717
_context = context;
1818
}
1919

20-
public async Task UpdateRelationshipsAsync(object parent, Relationship relationship, IEnumerable<string> relationshipIds)
20+
public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds)
2121
{
22-
var relationshipType = relationship.BaseType;
22+
var relationshipType = relationship.Type;
2323

2424
// TODO: replace with relationship.IsMany
2525
if(relationship.Type.GetInterfaces().Contains(typeof(IEnumerable)))
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
using System.Collections.Generic;
22
using System.Threading.Tasks;
3+
using JsonApiDotNetCore.Models;
34

45
namespace JsonApiDotNetCore.Internal
56
{
67
public interface IGenericProcessor
78
{
8-
Task UpdateRelationshipsAsync(object parent, Relationship relationship, IEnumerable<string> relationshipIds);
9+
Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds);
910
}
1011
}

src/JsonApiDotNetCore/Internal/Relationship.cs

Lines changed: 0 additions & 30 deletions
This file was deleted.

src/JsonApiDotNetCore/Models/HasManyAttribute.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System;
2+
13
namespace JsonApiDotNetCore.Models
24
{
35
public class HasManyAttribute : RelationshipAttribute

src/JsonApiDotNetCore/Models/RelationshipAttribute.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Reflection;
23

34
namespace JsonApiDotNetCore.Models
45
{
@@ -11,5 +12,17 @@ protected RelationshipAttribute(string publicName)
1112

1213
public string PublicRelationshipName { get; set; }
1314
public string InternalRelationshipName { get; set; }
15+
public Type Type { get; set; }
16+
public bool IsHasMany { get { return this.GetType() == typeof(HasManyAttribute); } }
17+
public bool IsHasOne { get { return this.GetType() == typeof(HasOneAttribute); } }
18+
19+
public void SetValue(object entity, object newValue)
20+
{
21+
var propertyInfo = entity
22+
.GetType()
23+
.GetProperty(InternalRelationshipName);
24+
25+
propertyInfo.SetValue(entity, newValue);
26+
}
1427
}
1528
}

src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,12 @@ private static object _setRelationships(
9898

9999
foreach (var attr in contextEntity.Relationships)
100100
{
101-
var entityProperty = entityProperties.FirstOrDefault(p => p.Name == $"{attr.RelationshipName}Id");
101+
var entityProperty = entityProperties.FirstOrDefault(p => p.Name == $"{attr.InternalRelationshipName}Id");
102102

103103
if (entityProperty == null)
104-
throw new JsonApiException("400", $"{contextEntity.EntityType.Name} does not contain an relationsip named {attr.RelationshipName}");
104+
throw new JsonApiException("400", $"{contextEntity.EntityType.Name} does not contain an relationsip named {attr.InternalRelationshipName}");
105105

106-
var relationshipName = attr.RelationshipName.Dasherize();
106+
var relationshipName = attr.InternalRelationshipName.Dasherize();
107107
RelationshipData relationshipData;
108108
if (relationships.TryGetValue(relationshipName, out relationshipData))
109109
{

0 commit comments

Comments
 (0)