Skip to content

Commit 550e00a

Browse files
committed
feat(context-graph): first pass at refactoring the graph API
also affects the service collection extensions
1 parent e17cad2 commit 550e00a

File tree

6 files changed

+179
-121
lines changed

6 files changed

+179
-121
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Reflection;
4+
using Microsoft.EntityFrameworkCore;
5+
using JsonApiDotNetCore.Internal;
6+
using JsonApiDotNetCore.Models;
7+
8+
namespace JsonApiDotNetCore.Builders
9+
{
10+
public class ContextGraphBuilder : IContextGraphBuilder
11+
{
12+
private List<ContextEntity> Entities;
13+
14+
public ContextGraphBuilder()
15+
{
16+
Entities = new List<ContextEntity>();
17+
}
18+
19+
public IContextGraph Build()
20+
{
21+
var graph = new ContextGraph()
22+
{
23+
Entities = Entities
24+
};
25+
26+
return graph;
27+
}
28+
29+
public void AddResource<TResource>(string pluralizedTypeName) where TResource : class
30+
{
31+
var entityType = typeof(TResource);
32+
Entities.Add(new ContextEntity
33+
{
34+
EntityName = pluralizedTypeName,
35+
EntityType = entityType,
36+
Attributes = GetAttributes(entityType),
37+
Relationships = GetRelationships(entityType)
38+
});
39+
}
40+
41+
protected virtual List<AttrAttribute> GetAttributes(Type entityType)
42+
{
43+
var attributes = new List<AttrAttribute>();
44+
45+
var properties = entityType.GetProperties();
46+
47+
foreach (var prop in properties)
48+
{
49+
var attribute = (AttrAttribute)prop.GetCustomAttribute(typeof(AttrAttribute));
50+
if (attribute == null) continue;
51+
attribute.InternalAttributeName = prop.Name;
52+
attributes.Add(attribute);
53+
}
54+
return attributes;
55+
}
56+
57+
protected virtual List<RelationshipAttribute> GetRelationships(Type entityType)
58+
{
59+
var attributes = new List<RelationshipAttribute>();
60+
61+
var properties = entityType.GetProperties();
62+
63+
foreach (var prop in properties)
64+
{
65+
var attribute = (RelationshipAttribute)prop.GetCustomAttribute(typeof(RelationshipAttribute));
66+
if (attribute == null) continue;
67+
attribute.InternalRelationshipName = prop.Name;
68+
attribute.Type = GetRelationshipType(attribute, prop);
69+
attributes.Add(attribute);
70+
}
71+
return attributes;
72+
}
73+
74+
protected virtual Type GetRelationshipType(RelationshipAttribute relation, PropertyInfo prop)
75+
{
76+
if (relation.IsHasMany)
77+
return prop.PropertyType.GetGenericArguments()[0];
78+
else
79+
return prop.PropertyType;
80+
}
81+
82+
public void AddDbContext<T>() where T : DbContext
83+
{
84+
var contextType = typeof(T);
85+
86+
var entities = new List<ContextEntity>();
87+
88+
var contextProperties = contextType.GetProperties();
89+
90+
foreach (var property in contextProperties)
91+
{
92+
var dbSetType = property.PropertyType;
93+
94+
if (dbSetType.GetTypeInfo().IsGenericType
95+
&& dbSetType.GetGenericTypeDefinition() == typeof(DbSet<>))
96+
{
97+
var entityType = dbSetType.GetGenericArguments()[0];
98+
entities.Add(new ContextEntity
99+
{
100+
EntityName = property.Name,
101+
EntityType = entityType,
102+
Attributes = GetAttributes(entityType),
103+
Relationships = GetRelationships(entityType)
104+
});
105+
}
106+
}
107+
108+
Entities = entities;
109+
}
110+
}
111+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using JsonApiDotNetCore.Internal;
2+
using Microsoft.EntityFrameworkCore;
3+
4+
namespace JsonApiDotNetCore.Builders
5+
{
6+
public interface IContextGraphBuilder
7+
{
8+
IContextGraph Build();
9+
void AddResource<TResource>(string pluralizedTypeName) where TResource : class;
10+
}
11+
}

src/JsonApiDotNetCore/Configuration/JsonApiOptions.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
using System;
2+
using JsonApiDotNetCore.Builders;
3+
using JsonApiDotNetCore.Internal;
4+
using Microsoft.EntityFrameworkCore;
5+
16
namespace JsonApiDotNetCore.Configuration
27
{
38
public class JsonApiOptions
@@ -6,5 +11,31 @@ public class JsonApiOptions
611
public int DefaultPageSize { get; set; }
712
public bool IncludeTotalRecordCount { get; set; }
813
public bool AllowClientGeneratedIds { get; set; }
14+
public IContextGraph ContextGraph { get; set; }
15+
16+
public void BuildContextGraph<TContext>(Action<IContextGraphBuilder> builder)
17+
where TContext : DbContext
18+
{
19+
var contextGraphBuilder = new ContextGraphBuilder();
20+
21+
contextGraphBuilder.AddDbContext<TContext>();
22+
23+
if(builder != null)
24+
builder(contextGraphBuilder);
25+
26+
ContextGraph = contextGraphBuilder.Build();
27+
}
28+
29+
public void BuildContextGraph(Action<IContextGraphBuilder> builder)
30+
{
31+
if(builder == null)
32+
throw new ArgumentException("Cannot build non-EF context graph without a IContextGraphBuilder action", nameof(builder));
33+
34+
var contextGraphBuilder = new ContextGraphBuilder();
35+
36+
builder(contextGraphBuilder);
37+
38+
ContextGraph = contextGraphBuilder.Build();
39+
}
940
}
1041
}

src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,14 @@ public static void AddJsonApi<TContext>(this IServiceCollection services)
1919
where TContext : DbContext
2020
{
2121
var mvcBuilder = services.AddMvc();
22-
AddInternals<TContext>(services, new JsonApiOptions(), mvcBuilder);
22+
AddJsonApi<TContext>(services, (opt) => {}, mvcBuilder);
2323
}
2424

2525
public static void AddJsonApi<TContext>(this IServiceCollection services, Action<JsonApiOptions> options)
2626
where TContext : DbContext
2727
{
28-
var config = new JsonApiOptions();
29-
30-
options(config);
31-
3228
var mvcBuilder = services.AddMvc();
33-
AddInternals<TContext>(services, config, mvcBuilder);
29+
AddJsonApi<TContext>(services, options, mvcBuilder);
3430
}
3531

3632
public static void AddJsonApi<TContext>(this IServiceCollection services,
@@ -41,40 +37,44 @@ public static void AddJsonApi<TContext>(this IServiceCollection services,
4137

4238
options(config);
4339

44-
AddInternals<TContext>(services, config, mvcBuilder);
45-
}
40+
if(config.ContextGraph == null)
41+
config.BuildContextGraph<TContext>(null);
42+
43+
services.AddScoped(typeof(DbContext), typeof(TContext));
4644

47-
private static void AddInternals<TContext>(IServiceCollection services,
48-
JsonApiOptions jsonApiOptions,
49-
IMvcBuilder mvcBuilder) where TContext : DbContext
50-
{
51-
services.AddJsonApiInternals<TContext>(jsonApiOptions);
5245
mvcBuilder
5346
.AddMvcOptions(opt => {
5447
opt.Filters.Add(typeof(JsonApiExceptionFilter));
55-
opt.SerializeAsJsonApi(jsonApiOptions);
48+
opt.SerializeAsJsonApi(config);
5649
});
50+
51+
AddJsonApiInternals(services, config);
5752
}
5853

59-
public static void AddJsonApiInternals<TContext>(this IServiceCollection services, JsonApiOptions jsonApiOptions)
60-
where TContext : DbContext
54+
public static void AddJsonApi(this IServiceCollection services,
55+
Action<JsonApiOptions> options,
56+
IMvcBuilder mvcBuilder)
6157
{
62-
var contextGraphBuilder = new ContextGraphBuilder<TContext>();
63-
var contextGraph = contextGraphBuilder.Build();
58+
var config = new JsonApiOptions();
59+
60+
options(config);
6461

65-
services.AddScoped(typeof(DbContext), typeof(TContext));
66-
services.AddScoped(typeof(IResourceService<>), typeof(EntityResourceService<>));
67-
services.AddScoped(typeof(IResourceService<,>), typeof(EntityResourceService<,>));
62+
AddJsonApiInternals(services, config);
63+
}
64+
65+
public static void AddJsonApiInternals(
66+
this IServiceCollection services,
67+
JsonApiOptions jsonApiOptions)
68+
{
6869
services.AddScoped(typeof(IEntityRepository<>), typeof(DefaultEntityRepository<>));
6970
services.AddScoped(typeof(IEntityRepository<,>), typeof(DefaultEntityRepository<,>));
70-
71+
services.AddScoped(typeof(IResourceService<>), typeof(EntityResourceService<>));
72+
services.AddScoped(typeof(IResourceService<,>), typeof(EntityResourceService<,>));
7173
services.AddSingleton<JsonApiOptions>(jsonApiOptions);
72-
services.AddSingleton<IContextGraph>(contextGraph);
74+
services.AddSingleton<IContextGraph>(jsonApiOptions.ContextGraph);
7375
services.AddScoped<IJsonApiContext,JsonApiContext>();
7476
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
75-
7677
services.AddScoped<JsonApiRouteHandler>();
77-
7878
services.AddScoped<IMetaBuilder, MetaBuilder>();
7979
services.AddScoped<IDocumentBuilder, DocumentBuilder>();
8080
services.AddScoped<IJsonApiSerializer, JsonApiSerializer>();

src/JsonApiDotNetCore/Internal/ContextGraph.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
using System.Reflection;
22
using System.Collections.Generic;
33
using System.Linq;
4-
using Microsoft.EntityFrameworkCore;
54
using System;
65

76
namespace JsonApiDotNetCore.Internal
87
{
9-
public class ContextGraph<T> : IContextGraph where T : DbContext
8+
public class ContextGraph : IContextGraph
109
{
1110
public List<ContextEntity> Entities { get; set; }
1211

src/JsonApiDotNetCore/Internal/ContextGraphBuilder.cs

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

0 commit comments

Comments
 (0)