Skip to content

Commit 2a3cdc4

Browse files
committed
fix(CreateOpProcessor): issues around service location
also cleans up some styling
1 parent a66534b commit 2a3cdc4

15 files changed

+126
-96
lines changed

src/JsonApiDotNetCore/Builders/ContextGraphBuilder.cs

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,22 @@ public IContextGraphBuilder AddResource<TResource, TId>(string pluralizedTypeNam
3535
{
3636
var entityType = typeof(TResource);
3737

38-
VerifyEntityIsNotAlreadyDefined(entityType);
38+
AssertEntityIsNotAlreadyDefined(entityType);
3939

40-
_entities.Add(new ContextEntity
41-
{
42-
EntityName = pluralizedTypeName,
43-
EntityType = entityType,
44-
IdentityType = typeof(TId),
45-
Attributes = GetAttributes(entityType),
46-
Relationships = GetRelationships(entityType)
47-
});
40+
_entities.Add(GetEntity(pluralizedTypeName, entityType, typeof(TId)));
4841

4942
return this;
5043
}
5144

45+
private ContextEntity GetEntity(string pluralizedTypeName, Type entityType, Type idType) => new ContextEntity
46+
{
47+
EntityName = pluralizedTypeName,
48+
EntityType = entityType,
49+
IdentityType = idType,
50+
Attributes = GetAttributes(entityType),
51+
Relationships = GetRelationships(entityType)
52+
};
53+
5254
private Link GetLinkFlags(Type entityType)
5355
{
5456
var attribute = (LinksAttribute)entityType.GetTypeInfo().GetCustomAttribute(typeof(LinksAttribute));
@@ -116,15 +118,9 @@ public IContextGraphBuilder AddDbContext<T>() where T : DbContext
116118
{
117119
var entityType = dbSetType.GetGenericArguments()[0];
118120

119-
VerifyEntityIsNotAlreadyDefined(entityType);
121+
AssertEntityIsNotAlreadyDefined(entityType);
120122

121-
_entities.Add(new ContextEntity
122-
{
123-
EntityName = GetResourceName(property),
124-
EntityType = entityType,
125-
Attributes = GetAttributes(entityType),
126-
Relationships = GetRelationships(entityType)
127-
});
123+
_entities.Add(GetEntity(GetResourceName(property), entityType, GetIdType(entityType)));
128124
}
129125
}
130126

@@ -140,7 +136,19 @@ private string GetResourceName(PropertyInfo property)
140136
return ((ResourceAttribute)resourceAttribute).ResourceName;
141137
}
142138

143-
private void VerifyEntityIsNotAlreadyDefined(Type entityType)
139+
private Type GetIdType(Type resourceType)
140+
{
141+
var interfaces = resourceType.GetInterfaces();
142+
foreach (var type in interfaces)
143+
{
144+
if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(IIdentifiable<>))
145+
return type.GetGenericArguments()[0];
146+
}
147+
148+
throw new ArgumentException("Type does not implement 'IIdentifiable<TId>'", nameof(resourceType));
149+
}
150+
151+
private void AssertEntityIsNotAlreadyDefined(Type entityType)
144152
{
145153
if (_entities.Any(e => e.EntityType == entityType))
146154
throw new InvalidOperationException($"Cannot add entity type {entityType} to context graph, there is already an entity of that type configured.");

src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public virtual async Task<TEntity> UpdateAsync(TId id, TEntity entity)
137137

138138
public async Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds)
139139
{
140-
var genericProcessor = _genericProcessorFactory.GetProcessor<IGenericProcessor>(relationship.Type);
140+
var genericProcessor = _genericProcessorFactory.GetProcessor<IGenericProcessor>(typeof(GenericProcessor<>), relationship.Type);
141141
await genericProcessor.UpdateRelationshipsAsync(parent, relationship, relationshipIds);
142142
}
143143

src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using JsonApiDotNetCore.Serialization;
1111
using JsonApiDotNetCore.Services;
1212
using JsonApiDotNetCore.Services.Operations;
13+
using JsonApiDotNetCore.Services.Operations.Processors;
1314
using Microsoft.AspNetCore.Http;
1415
using Microsoft.AspNetCore.Mvc;
1516
using Microsoft.EntityFrameworkCore;
@@ -99,6 +100,8 @@ public static void AddJsonApiInternals(
99100
services.AddScoped<IDbContextResolver, DbContextResolver>();
100101
services.AddScoped(typeof(IEntityRepository<>), typeof(DefaultEntityRepository<>));
101102
services.AddScoped(typeof(IEntityRepository<,>), typeof(DefaultEntityRepository<,>));
103+
services.AddScoped(typeof(ICreateService<>), typeof(EntityResourceService<>));
104+
services.AddScoped(typeof(ICreateService<,>), typeof(EntityResourceService<,>));
102105
services.AddScoped(typeof(IResourceService<>), typeof(EntityResourceService<>));
103106
services.AddScoped(typeof(IResourceService<,>), typeof(EntityResourceService<,>));
104107
services.AddSingleton<JsonApiOptions>(jsonApiOptions);
@@ -115,6 +118,7 @@ public static void AddJsonApiInternals(
115118
services.AddScoped<IJsonApiOperationsReader, JsonApiOperationsReader>();
116119
services.AddScoped<IGenericProcessorFactory, GenericProcessorFactory>();
117120
services.AddScoped(typeof(GenericProcessor<>));
121+
services.AddScoped(typeof(GenericProcessor<,>));
118122
services.AddScoped<IQueryAccessor, QueryAccessor>();
119123
services.AddScoped<IQueryParser, QueryParser>();
120124
services.AddScoped<IControllerContext, Services.ControllerContext>();
@@ -123,6 +127,8 @@ public static void AddJsonApiInternals(
123127
private static void AddOperationServices(IServiceCollection services)
124128
{
125129
services.AddScoped<IOperationsProcessor, OperationsProcessor>();
130+
services.AddScoped(typeof(ICreateOpProcessor<>), typeof(CreateOpProcessor<>));
131+
services.AddScoped(typeof(ICreateOpProcessor<,>), typeof(CreateOpProcessor<,>));
126132
services.AddSingleton<IOperationProcessorResolver, OperationProcessorResolver>();
127133
services.AddSingleton<IGenericProcessorFactory, GenericProcessorFactory>();
128134
}

src/JsonApiDotNetCore/Internal/Generics/GenericProcessor.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,18 @@
77

88
namespace JsonApiDotNetCore.Internal.Generics
99
{
10-
public class GenericProcessor<T> : IGenericProcessor where T : class, IIdentifiable
10+
public interface IGenericProcessor
11+
{
12+
Task UpdateRelationshipsAsync(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds);
13+
void SetRelationships(object parent, RelationshipAttribute relationship, IEnumerable<string> relationshipIds);
14+
}
15+
16+
public class GenericProcessor<T> : GenericProcessor<T, int> where T : class, IIdentifiable<int>
17+
{
18+
public GenericProcessor(DbContext context) : base(context) { }
19+
}
20+
21+
public class GenericProcessor<T, TId> : IGenericProcessor where T : class, IIdentifiable<TId>
1122
{
1223
private readonly DbContext _context;
1324
public GenericProcessor(DbContext context)

src/JsonApiDotNetCore/Internal/Generics/GenericProcessorFactory.cs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,30 @@
22

33
namespace JsonApiDotNetCore.Internal.Generics
44
{
5+
/// <summary>
6+
/// Used to generate a generic operations processor when the types
7+
/// are not known until runtime. The typical use case would be for
8+
/// accessing relationship data or resolving operations processors.
9+
/// </summary>
10+
public interface IGenericProcessorFactory
11+
{
12+
/// <summary>
13+
/// Constructs the generic type and locates the service, then casts to TInterface
14+
/// </summary>
15+
/// <example>
16+
/// GetProcessor&lt;IGenericProcessor&gt;(typeof(GenericProcessor&lt;&gt;), typeof(TResource));
17+
/// </example>
18+
TInterface GetProcessor<TInterface>(Type openGenericType, Type resourceType);
19+
20+
/// <summary>
21+
/// Constructs the generic type and locates the service, then casts to TInterface
22+
/// </summary>
23+
/// <example>
24+
/// GetProcessor&lt;IGenericProcessor&gt;(typeof(GenericProcessor&lt;,&gt;), typeof(TResource), typeof(TId));
25+
/// </example>
26+
TInterface GetProcessor<TInterface>(Type openGenericType, Type resourceType, Type keyType);
27+
}
28+
529
public class GenericProcessorFactory : IGenericProcessorFactory
630
{
731
private readonly IServiceProvider _serviceProvider;
@@ -11,10 +35,17 @@ public GenericProcessorFactory(IServiceProvider serviceProvider)
1135
_serviceProvider = serviceProvider;
1236
}
1337

14-
public TInterface GetProcessor<TInterface>(Type[] types)
38+
public TInterface GetProcessor<TInterface>(Type openGenericType, Type resourceType)
39+
=> _getProcessor<TInterface>(openGenericType, resourceType);
40+
41+
public TInterface GetProcessor<TInterface>(Type openGenericType, Type resourceType, Type keyType)
42+
=> _getProcessor<TInterface>(openGenericType, resourceType, keyType);
43+
44+
private TInterface _getProcessor<TInterface>(Type openGenericType, params Type[] types)
1545
{
16-
var processorType = typeof(GenericProcessor<>).MakeGenericType(types);
17-
return (TInterface)_serviceProvider.GetService(processorType);
46+
var concreteType = openGenericType.MakeGenericType(types);
47+
48+
return (TInterface)_serviceProvider.GetService(concreteType);
1849
}
1950
}
2051
}

src/JsonApiDotNetCore/Internal/Generics/IGenericProcessor.cs

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

src/JsonApiDotNetCore/Internal/Generics/IGenericProcessorFactory.cs

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

src/JsonApiDotNetCore/Models/DocumentBase.cs

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,13 @@ namespace JsonApiDotNetCore.Models
55
{
66
public class DocumentBase
77
{
8-
[JsonProperty("links")]
8+
[JsonProperty("links", NullValueHandling = NullValueHandling.Ignore)]
99
public RootLinks Links { get; set; }
1010

11-
[JsonProperty("included")]
11+
[JsonProperty("included", NullValueHandling = NullValueHandling.Ignore)]
1212
public List<DocumentData> Included { get; set; }
1313

14-
[JsonProperty("meta")]
14+
[JsonProperty("meta", NullValueHandling = NullValueHandling.Ignore)]
1515
public Dictionary<string, object> Meta { get; set; }
16-
17-
// http://www.newtonsoft.com/json/help/html/ConditionalProperties.htm
18-
public bool ShouldSerializeIncluded()
19-
{
20-
return (Included != null);
21-
}
22-
23-
public bool ShouldSerializeMeta()
24-
{
25-
return (Meta != null);
26-
}
27-
28-
public bool ShouldSerializeLinks()
29-
{
30-
return (Links != null);
31-
}
3216
}
3317
}

src/JsonApiDotNetCore/Models/DocumentData.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class DocumentData
1414
[JsonProperty("attributes")]
1515
public Dictionary<string, object> Attributes { get; set; }
1616

17-
[JsonProperty("relationships")]
17+
[JsonProperty("relationships", NullValueHandling = NullValueHandling.Ignore)]
1818
public Dictionary<string, RelationshipData> Relationships { get; set; }
1919
}
2020
}

src/JsonApiDotNetCore/Models/Operations/Operation.cs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
using System.Collections.Generic;
22
using Newtonsoft.Json;
3+
using Newtonsoft.Json.Converters;
34
using Newtonsoft.Json.Linq;
45

56
namespace JsonApiDotNetCore.Models.Operations
67
{
78
public class Operation : DocumentBase
89
{
9-
[JsonProperty("op")]
10+
[JsonProperty("op"), JsonConverter(typeof(StringEnumConverter))]
1011
public OperationCode Op { get; set; }
1112

12-
[JsonProperty("ref")]
13+
[JsonProperty("ref", NullValueHandling = NullValueHandling.Ignore)]
1314
public ResourceReference Ref { get; set; }
1415

15-
[JsonProperty("params")]
16+
[JsonProperty("params", NullValueHandling = NullValueHandling.Ignore)]
1617
public Params Params { get; set; }
1718

1819
[JsonProperty("data")]
@@ -28,32 +29,41 @@ public object Data
2829

2930
private void SetData(object data)
3031
{
31-
if (data is JArray jArray) {
32+
if (data is JArray jArray)
33+
{
3234
DataIsList = true;
3335
DataList = jArray.ToObject<List<DocumentData>>();
3436
}
35-
else if (data is List<DocumentData> dataList) {
37+
else if (data is List<DocumentData> dataList)
38+
{
3639
DataIsList = true;
3740
DataList = dataList;
3841
}
39-
else if (data is JObject jObject) {
42+
else if (data is JObject jObject)
43+
{
4044
DataObject = jObject.ToObject<DocumentData>();
4145
}
42-
else if (data is DocumentData dataObject) {
46+
else if (data is DocumentData dataObject)
47+
{
4348
DataObject = dataObject;
4449
}
4550
}
4651

52+
[JsonIgnore]
4753
public bool DataIsList { get; private set; }
54+
55+
[JsonIgnore]
4856
public List<DocumentData> DataList { get; private set; }
57+
58+
[JsonIgnore]
4959
public DocumentData DataObject { get; private set; }
5060

5161
public string GetResourceTypeName()
5262
{
53-
if(Ref != null)
63+
if (Ref != null)
5464
return Ref.Type?.ToString();
5565

56-
if(DataIsList)
66+
if (DataIsList)
5767
return DataList[0].Type?.ToString();
5868

5969
return DataObject.Type?.ToString();

0 commit comments

Comments
 (0)