Skip to content

Commit 6ce8874

Browse files
committed
refactor(data): use entity repository to access data
1 parent e2ec868 commit 6ce8874

File tree

10 files changed

+286
-81
lines changed

10 files changed

+286
-81
lines changed

src/JsonApiDotNetCore/Controllers/JsonApiController.cs

Lines changed: 31 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
using System.Linq;
2-
using JsonApiDotNetCore.Extensions;
2+
using System.Threading.Tasks;
3+
using JsonApiDotNetCore.Data;
34
using JsonApiDotNetCore.Models;
45
using JsonApiDotNetCore.Services;
56
using Microsoft.AspNetCore.Mvc;
6-
using Microsoft.EntityFrameworkCore;
7+
using Microsoft.AspNetCore.Routing;
78
using Microsoft.Extensions.Logging;
89
using Newtonsoft.Json;
910

@@ -12,58 +13,56 @@ namespace JsonApiDotNetCore.Controllers
1213
public class JsonApiController<T> : JsonApiController<T, int> where T : class, IIdentifiable<int>
1314
{
1415
public JsonApiController(
15-
ILoggerFactory loggerFactory,
16-
DbContext context,
17-
IJsonApiContext jsonApiContext)
18-
: base(loggerFactory, context, jsonApiContext)
16+
IJsonApiContext jsonApiContext,
17+
IEntityRepository<T, int> entityRepository,
18+
ILoggerFactory loggerFactory)
19+
: base(jsonApiContext, entityRepository, loggerFactory)
1920
{ }
2021
}
2122

2223
public class JsonApiController<T, TId> : Controller where T : class, IIdentifiable<TId>
2324
{
24-
private readonly DbContext _context;
25-
private readonly DbSet<T> _dbSet;
25+
private readonly IEntityRepository<T, TId> _entities;
2626
private readonly IJsonApiContext _jsonApiContext;
2727
private readonly ILogger _logger;
2828

2929
public JsonApiController(
30-
ILoggerFactory loggerFactory,
31-
DbContext context,
32-
IJsonApiContext jsonApiContext)
30+
IJsonApiContext jsonApiContext,
31+
IEntityRepository<T, TId> entityRepository,
32+
ILoggerFactory loggerFactory)
3333
{
34-
_context = context;
35-
_dbSet = context.GetDbSet<T>();
3634
_jsonApiContext = jsonApiContext;
35+
_entities = entityRepository;
3736

3837
_logger = loggerFactory.CreateLogger<JsonApiController<T, TId>>();
3938
_logger.LogTrace($@"JsonApiController activated with ContextGraph:
4039
{JsonConvert.SerializeObject(jsonApiContext.ContextGraph)}");
4140
}
4241

4342
public JsonApiController(
44-
DbContext context,
45-
IJsonApiContext jsonApiContext)
43+
IJsonApiContext jsonApiContext,
44+
IEntityRepository<T, TId> entityRepository)
4645
{
47-
_context = context;
48-
_dbSet = context.GetDbSet<T>();
4946
_jsonApiContext = jsonApiContext;
47+
_entities = entityRepository;
5048
}
5149

5250
[HttpGet]
5351
public virtual IActionResult Get()
5452
{
5553
ApplyContext();
5654

57-
var entities = _dbSet.ToList();
55+
var entities = _entities.Get().ToList();
56+
5857
return Ok(entities);
5958
}
6059

6160
[HttpGet("{id}")]
62-
public virtual IActionResult Get(TId id)
61+
public virtual async Task<IActionResult> GetAsync(TId id)
6362
{
6463
ApplyContext();
6564

66-
var entity = _dbSet.FirstOrDefault(e => e.Id.Equals(id));
65+
var entity = await _entities.GetAsync(id);
6766

6867
if (entity == null)
6968
return NotFound();
@@ -72,7 +71,7 @@ public virtual IActionResult Get(TId id)
7271
}
7372

7473
[HttpGet("{id}/{relationshipName}")]
75-
public virtual IActionResult GetRelationship(TId id, string relationshipName)
74+
public virtual async Task<IActionResult> GetRelationshipAsync(TId id, string relationshipName)
7675
{
7776
ApplyContext();
7877

@@ -81,9 +80,7 @@ public virtual IActionResult GetRelationship(TId id, string relationshipName)
8180
if (relationshipName == null)
8281
return NotFound();
8382

84-
var entity = _dbSet
85-
.Include(relationshipName)
86-
.FirstOrDefault(e => e.Id.Equals(id));
83+
var entity = await _entities.GetAndIncludeAsync(id, relationshipName);
8784

8885
if (entity == null)
8986
return NotFound();
@@ -100,41 +97,29 @@ public virtual IActionResult GetRelationship(TId id, string relationshipName)
10097
}
10198

10299
[HttpPost]
103-
public virtual IActionResult Post([FromBody] T entity)
100+
public virtual async Task<IActionResult> PostAsync([FromBody] T entity)
104101
{
105102
ApplyContext();
106103

107104
if (entity == null)
108105
return BadRequest();
109106

110-
_dbSet.Add(entity);
111-
_context.SaveChanges();
107+
await _entities.CreateAsync(entity);
112108

113109
return Created(HttpContext.Request.Path, entity);
114110
}
115111

116112
[HttpPatch("{id}")]
117-
public virtual IActionResult Patch(int id, [FromBody] T entity)
113+
public virtual async Task<IActionResult> PatchAsync(TId id, [FromBody] T entity)
118114
{
119115
ApplyContext();
120116

121117
if (entity == null)
122118
return BadRequest();
123119

124-
var oldEntity = _dbSet.FirstOrDefault(e => e.Id.Equals(id));
125-
if (oldEntity == null)
126-
return NotFound();
127-
128-
var requestEntity = _jsonApiContext.RequestEntity;
129-
130-
requestEntity.Attributes.ForEach(attr =>
131-
{
132-
attr.SetValue(oldEntity, attr.GetValue(entity));
133-
});
120+
var updatedEntity = await _entities.UpdateAsync(id, entity);
134121

135-
_context.SaveChanges();
136-
137-
return Ok(oldEntity);
122+
return Ok(updatedEntity);
138123
}
139124

140125
// [HttpPatch("{id}/{relationship}")]
@@ -144,16 +129,14 @@ public virtual IActionResult Patch(int id, [FromBody] T entity)
144129
// }
145130

146131
[HttpDelete("{id}")]
147-
public virtual IActionResult Delete(TId id)
132+
public virtual async Task<IActionResult> DeleteAsync(TId id)
148133
{
149134
ApplyContext();
150135

151-
var entity = _dbSet.FirstOrDefault(e => e.Id.Equals(id));
152-
if (entity == null)
153-
return NotFound();
136+
var wasDeleted = await _entities.DeleteAsync(id);
154137

155-
_dbSet.Remove(entity);
156-
_context.SaveChanges();
138+
if (!wasDeleted)
139+
return NotFound();
157140

158141
return Ok();
159142
}
@@ -166,6 +149,7 @@ public virtual IActionResult Delete(TId id)
166149

167150
private void ApplyContext()
168151
{
152+
var routeData = HttpContext.GetRouteData();
169153
_jsonApiContext.RequestEntity = _jsonApiContext.ContextGraph.GetContextEntity(typeof(T));
170154
_jsonApiContext.ApplyContext(HttpContext);
171155
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
using System.Linq;
2+
using System.Threading.Tasks;
3+
using JsonApiDotNetCore.Extensions;
4+
using JsonApiDotNetCore.Models;
5+
using JsonApiDotNetCore.Services;
6+
using Microsoft.EntityFrameworkCore;
7+
using Microsoft.Extensions.Logging;
8+
9+
namespace JsonApiDotNetCore.Data
10+
{
11+
public class DefaultEntityRepository<TEntity, TId>
12+
: IEntityRepository<TEntity, TId>
13+
where TEntity : class, IIdentifiable<TId>
14+
{
15+
private readonly DbContext _context;
16+
private readonly DbSet<TEntity> _dbSet;
17+
private readonly ILogger _logger;
18+
private readonly IJsonApiContext _jsonApiContext;
19+
20+
public DefaultEntityRepository(
21+
DbContext context,
22+
ILoggerFactory loggerFactory,
23+
IJsonApiContext jsonApiContext)
24+
{
25+
_context = context;
26+
_dbSet = context.GetDbSet<TEntity>();
27+
_jsonApiContext = jsonApiContext;
28+
_logger = loggerFactory.CreateLogger<DefaultEntityRepository<TEntity, TId>>();
29+
}
30+
31+
public IQueryable<TEntity> Get()
32+
{
33+
return _dbSet;
34+
}
35+
36+
public async Task<TEntity> GetAsync(TId id)
37+
{
38+
return await _dbSet.FirstOrDefaultAsync(e => e.Id.Equals(id));
39+
}
40+
41+
public async Task<TEntity> GetAndIncludeAsync(TId id, string relationshipName)
42+
{
43+
return await _dbSet
44+
.Include(relationshipName)
45+
.FirstOrDefaultAsync(e => e.Id.Equals(id));
46+
}
47+
48+
public async Task<TEntity> CreateAsync(TEntity entity)
49+
{
50+
_dbSet.Add(entity);
51+
await _context.SaveChangesAsync();
52+
return entity;
53+
}
54+
55+
public async Task<TEntity> UpdateAsync(TId id, TEntity entity)
56+
{
57+
var oldEntity = await GetAsync(id);
58+
59+
if (oldEntity == null)
60+
return null;
61+
62+
_jsonApiContext.RequestEntity.Attributes.ForEach(attr =>
63+
{
64+
attr.SetValue(oldEntity, attr.GetValue(entity));
65+
});
66+
67+
await _context.SaveChangesAsync();
68+
69+
return entity;
70+
}
71+
72+
public async Task<bool> DeleteAsync(TId id)
73+
{
74+
var entity = await GetAsync(id);
75+
76+
if (entity == null)
77+
return false;
78+
79+
_dbSet.Remove(entity);
80+
81+
await _context.SaveChangesAsync();
82+
83+
return true;
84+
}
85+
}
86+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.Linq;
2+
using System.Threading.Tasks;
3+
using JsonApiDotNetCore.Models;
4+
5+
namespace JsonApiDotNetCore.Data
6+
{
7+
public interface IEntityRepository<TEntity, in TId>
8+
where TEntity : class, IIdentifiable<TId>
9+
{
10+
IQueryable<TEntity> Get();
11+
12+
Task<TEntity> GetAsync(TId id);
13+
14+
Task<TEntity> GetAndIncludeAsync(TId id, string relationshipName);
15+
16+
Task<TEntity> CreateAsync(TEntity entity);
17+
18+
Task<TEntity> UpdateAsync(TId id, TEntity entity);
19+
20+
Task<bool> DeleteAsync(TId id);
21+
}
22+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using JsonApiDotNetCore.Internal;
3+
using Microsoft.AspNetCore.Builder;
4+
using Microsoft.AspNetCore.Mvc.Infrastructure;
5+
using Microsoft.AspNetCore.Mvc.Internal;
6+
using Microsoft.AspNetCore.Routing;
7+
using Microsoft.Extensions.DependencyInjection;
8+
9+
namespace JsonApiDotNetCore.Routing
10+
{
11+
public static class IApplicationBuilderExtensions
12+
{
13+
public static IApplicationBuilder UseJsonApi(this IApplicationBuilder app)
14+
{
15+
if (app == null)
16+
throw new ArgumentNullException(nameof(app));
17+
18+
// Verify if AddMvc was done before calling UseMvc
19+
// We use the MvcMarkerService to make sure if all the services were added.
20+
if (app.ApplicationServices.GetService(typeof(MvcMarkerService)) == null)
21+
throw new InvalidOperationException();
22+
23+
var middlewarePipelineBuilder = app.ApplicationServices.GetRequiredService<MiddlewareFilterBuilder>();
24+
middlewarePipelineBuilder.ApplicationBuilder = app.New();
25+
26+
var routes = new RouteBuilder(app)
27+
{
28+
DefaultHandler = app.ApplicationServices.GetRequiredService<JsonApiRouteHandler>(),
29+
};
30+
31+
routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices));
32+
33+
return app.UseRouter(routes.Build());
34+
}
35+
}
36+
}

src/JsonApiDotNetCore/Extensions/ServiceProviderExtensions.cs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using JsonApiDotNetCore.Data;
12
using JsonApiDotNetCore.Formatters;
23
using JsonApiDotNetCore.Internal;
34
using JsonApiDotNetCore.Services;
@@ -9,26 +10,34 @@ namespace JsonApiDotNetCore.Extensions
910
{
1011
public static class ServiceProviderExtensions
1112
{
12-
public static void AddJsonApi<T>(this IServiceCollection services) where T : DbContext
13+
public static void AddJsonApi<TContext>(this IServiceCollection services)
14+
where TContext : DbContext
1315
{
14-
services.AddJsonApiInternals<T>();
16+
services.AddJsonApiInternals<TContext>();
1517
services.AddMvc()
1618
.AddMvcOptions(options => options.SerializeAsJsonApi());
1719
}
1820

19-
public static void AddJsonApiInternals<T>(this IServiceCollection services) where T : DbContext
21+
public static void AddJsonApiInternals<TContext>(this IServiceCollection services)
22+
where TContext : DbContext
2023
{
21-
var contextGraphBuilder = new ContextGraphBuilder<T>();
24+
var contextGraphBuilder = new ContextGraphBuilder<TContext>();
2225
var contextGraph = contextGraphBuilder.Build();
2326

27+
services.AddScoped(typeof(DbContext), typeof(TContext));
28+
29+
services.AddScoped(typeof(IEntityRepository<,>), typeof(DefaultEntityRepository<,>));
30+
// services.AddScoped(typeof(IEntityRepository<,>), typeof(DefaultEntityRepository<,>));
31+
2432
services.AddSingleton<IContextGraph>(contextGraph);
2533
services.AddSingleton<IJsonApiContext, JsonApiContext>();
34+
services.AddScoped<JsonApiRouteHandler>();
2635
}
2736

2837
public static void SerializeAsJsonApi(this MvcOptions options)
2938
{
3039
options.InputFormatters.Insert(0, new JsonApiInputFormatter());
31-
40+
3241
options.OutputFormatters.Insert(0, new JsonApiOutputFormatter());
3342
}
3443
}

0 commit comments

Comments
 (0)