Skip to content

Commit 5919517

Browse files
authored
Merge pull request #115 from Research-Institute/fix/#113
Fix/#113
2 parents fadd676 + e121f2c commit 5919517

12 files changed

+299
-11
lines changed

docs/EntityRepositories.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,14 @@ A sample implementation that performs data authorization might look like:
2424
public class MyAuthorizedEntityRepository : DefaultEntityRepository<MyEntity>
2525
{
2626
private readonly ILogger _logger;
27-
private readonly AppDbContext _context;
2827
private readonly IAuthenticationService _authenticationService;
2928

30-
public MyAuthorizedEntityRepository(AppDbContext context,
29+
public MyAuthorizedEntityRepository(
3130
ILoggerFactory loggerFactory,
3231
IJsonApiContext jsonApiContext,
3332
IAuthenticationService authenticationService)
34-
: base(context, loggerFactory, jsonApiContext)
35-
{
36-
_context = context;
33+
: base(loggerFactory, jsonApiContext)
34+
{
3735
_logger = loggerFactory.CreateLogger<MyEntityRepository>();
3836
_authenticationService = authenticationService;
3937
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using JsonApiDotNetCore.Extensions;
3+
using Microsoft.EntityFrameworkCore;
4+
5+
namespace JsonApiDotNetCore.Data
6+
{
7+
public class DbContextResolver : IDbContextResolver
8+
{
9+
private readonly DbContext _context;
10+
11+
public DbContextResolver(DbContext context)
12+
{
13+
_context = context;
14+
}
15+
16+
public DbContext GetContext() => _context;
17+
18+
public DbSet<TEntity> GetDbSet<TEntity>() where TEntity : class
19+
=> _context.GetDbSet<TEntity>();
20+
}
21+
}

src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.Linq;
34
using System.Threading.Tasks;
@@ -17,12 +18,19 @@ public class DefaultEntityRepository<TEntity>
1718
IEntityRepository<TEntity>
1819
where TEntity : class, IIdentifiable<int>
1920
{
21+
[Obsolete("DbContext is no longer directly injected into the ctor. Use JsonApiContext.GetDbContextResolver() instead")]
2022
public DefaultEntityRepository(
2123
DbContext context,
2224
ILoggerFactory loggerFactory,
2325
IJsonApiContext jsonApiContext)
2426
: base(context, loggerFactory, jsonApiContext)
2527
{ }
28+
29+
public DefaultEntityRepository(
30+
ILoggerFactory loggerFactory,
31+
IJsonApiContext jsonApiContext)
32+
: base(loggerFactory, jsonApiContext)
33+
{ }
2634
}
2735

2836
public class DefaultEntityRepository<TEntity, TId>
@@ -35,6 +43,7 @@ public class DefaultEntityRepository<TEntity, TId>
3543
private readonly IJsonApiContext _jsonApiContext;
3644
private readonly IGenericProcessorFactory _genericProcessorFactory;
3745

46+
[Obsolete("DbContext is no longer directly injected into the ctor. Use JsonApiContext.GetDbContextResolver() instead")]
3847
public DefaultEntityRepository(
3948
DbContext context,
4049
ILoggerFactory loggerFactory,
@@ -47,6 +56,18 @@ public DefaultEntityRepository(
4756
_genericProcessorFactory = _jsonApiContext.GenericProcessorFactory;
4857
}
4958

59+
public DefaultEntityRepository(
60+
ILoggerFactory loggerFactory,
61+
IJsonApiContext jsonApiContext)
62+
{
63+
var contextResolver = jsonApiContext.GetDbContextResolver();
64+
_context = contextResolver.GetContext();
65+
_dbSet = contextResolver.GetDbSet<TEntity>();
66+
_jsonApiContext = jsonApiContext;
67+
_logger = loggerFactory.CreateLogger<DefaultEntityRepository<TEntity, TId>>();
68+
_genericProcessorFactory = _jsonApiContext.GenericProcessorFactory;
69+
}
70+
5071
public virtual IQueryable<TEntity> Get()
5172
{
5273
if(_jsonApiContext.QuerySet?.Fields != null && _jsonApiContext.QuerySet.Fields.Any())
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using Microsoft.EntityFrameworkCore;
2+
3+
namespace JsonApiDotNetCore.Data
4+
{
5+
public interface IDbContextResolver
6+
{
7+
DbContext GetContext();
8+
DbSet<TEntity> GetDbSet<TEntity>()
9+
where TEntity : class;
10+
}
11+
}

src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public static void AddJsonApiInternals(
8989
services.AddSingleton<DbContextOptions>(new DbContextOptionsBuilder().Options);
9090
}
9191

92+
services.AddScoped<IDbContextResolver, DbContextResolver>();
9293
services.AddScoped(typeof(IEntityRepository<>), typeof(DefaultEntityRepository<>));
9394
services.AddScoped(typeof(IEntityRepository<,>), typeof(DefaultEntityRepository<,>));
9495
services.AddScoped(typeof(IResourceService<>), typeof(EntityResourceService<>));

src/JsonApiDotNetCore/Models/AttrAttribute.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ public AttrAttribute(string publicName)
1111
PublicAttributeName = publicName;
1212
}
1313

14+
public AttrAttribute(string publicName, string internalName)
15+
{
16+
PublicAttributeName = publicName;
17+
InternalAttributeName = internalName;
18+
}
19+
1420
public string PublicAttributeName { get; set; }
1521
public string InternalAttributeName { get; set; }
1622

src/JsonApiDotNetCore/Services/IJsonApiContext.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Collections.Generic;
22
using JsonApiDotNetCore.Builders;
33
using JsonApiDotNetCore.Configuration;
4+
using JsonApiDotNetCore.Data;
45
using JsonApiDotNetCore.Internal;
56
using JsonApiDotNetCore.Internal.Generics;
67
using JsonApiDotNetCore.Internal.Query;
@@ -24,5 +25,6 @@ public interface IJsonApiContext
2425
IGenericProcessorFactory GenericProcessorFactory { get; set; }
2526
Dictionary<AttrAttribute, object> AttributesToUpdate { get; set; }
2627
Dictionary<RelationshipAttribute, object> RelationshipsToUpdate { get; set; }
28+
IDbContextResolver GetDbContextResolver();
2729
}
2830
}

src/JsonApiDotNetCore/Services/JsonApiContext.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
using System;
12
using System.Collections.Generic;
23
using System.Linq;
34
using JsonApiDotNetCore.Builders;
45
using JsonApiDotNetCore.Configuration;
6+
using JsonApiDotNetCore.Data;
57
using JsonApiDotNetCore.Internal;
68
using JsonApiDotNetCore.Internal.Generics;
79
using JsonApiDotNetCore.Internal.Query;
@@ -13,13 +15,17 @@ namespace JsonApiDotNetCore.Services
1315
public class JsonApiContext : IJsonApiContext
1416
{
1517
private readonly IHttpContextAccessor _httpContextAccessor;
18+
private readonly IDbContextResolver _contextResolver;
19+
1620
public JsonApiContext(
21+
IDbContextResolver contextResolver,
1722
IContextGraph contextGraph,
1823
IHttpContextAccessor httpContextAccessor,
1924
JsonApiOptions options,
2025
IMetaBuilder metaBuilder,
2126
IGenericProcessorFactory genericProcessorFactory)
2227
{
28+
_contextResolver = contextResolver;
2329
ContextGraph = contextGraph;
2430
_httpContextAccessor = httpContextAccessor;
2531
Options = options;
@@ -61,6 +67,8 @@ public IJsonApiContext ApplyContext<T>()
6167
return this;
6268
}
6369

70+
public IDbContextResolver GetDbContextResolver() => _contextResolver;
71+
6472
private PageManager GetPageManager()
6573
{
6674
if (Options.DefaultPageSize == 0 && (QuerySet == null || QuerySet.PageQuery.PageSize == 0))
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using JsonApiDotNetCore.Controllers;
4+
using JsonApiDotNetCore.Internal;
5+
using Microsoft.AspNetCore.Mvc;
6+
using Xunit;
7+
using Moq;
8+
using Microsoft.EntityFrameworkCore;
9+
using JsonApiDotNetCoreExample.Models;
10+
using JsonApiDotNetCore.Extensions;
11+
using JsonApiDotNetCore.Data;
12+
using JsonApiDotNetCore.Models;
13+
using Microsoft.Extensions.Logging;
14+
using JsonApiDotNetCore.Services;
15+
using System.Threading.Tasks;
16+
17+
namespace UnitTests.Data
18+
{
19+
public class DefaultEntityRepository_Tests : JsonApiControllerMixin
20+
{
21+
private readonly Mock<IJsonApiContext> _jsonApiContextMock;
22+
private readonly Mock<ILoggerFactory> _loggFactoryMock;
23+
private readonly Mock<DbSet<TodoItem>> _dbSetMock;
24+
private readonly Mock<DbContext> _contextMock;
25+
private readonly Mock<IDbContextResolver> _contextResolverMock;
26+
private readonly TodoItem _todoItem;
27+
private Dictionary<AttrAttribute, object> _attrsToUpdate = new Dictionary<AttrAttribute, object>();
28+
private Dictionary<RelationshipAttribute, object> _relationshipsToUpdate = new Dictionary<RelationshipAttribute, object>();
29+
30+
public DefaultEntityRepository_Tests()
31+
{
32+
_todoItem = new TodoItem
33+
{
34+
Id = 1,
35+
Description = Guid.NewGuid().ToString(),
36+
Ordinal = 10
37+
};
38+
_jsonApiContextMock = new Mock<IJsonApiContext>();
39+
_loggFactoryMock = new Mock<ILoggerFactory>();
40+
_dbSetMock = DbSetMock.Create<TodoItem>(new[] { _todoItem });
41+
_contextMock = new Mock<DbContext>();
42+
_contextResolverMock = new Mock<IDbContextResolver>();
43+
}
44+
45+
[Fact]
46+
public async Task UpdateAsync_Updates_Attributes_In_AttributesToUpdate()
47+
{
48+
// arrange
49+
var todoItemUpdates = new TodoItem
50+
{
51+
Id = _todoItem.Id,
52+
Description = Guid.NewGuid().ToString()
53+
};
54+
55+
_attrsToUpdate = new Dictionary<AttrAttribute, object>
56+
{
57+
{
58+
new AttrAttribute("description", "Description"),
59+
todoItemUpdates.Description
60+
}
61+
};
62+
63+
var repository = GetRepository();
64+
65+
// act
66+
var updatedItem = await repository.UpdateAsync(_todoItem.Id, todoItemUpdates);
67+
68+
// assert
69+
Assert.NotNull(updatedItem);
70+
Assert.Equal(_todoItem.Ordinal, updatedItem.Ordinal);
71+
Assert.Equal(todoItemUpdates.Description, updatedItem.Description);
72+
}
73+
74+
private DefaultEntityRepository<TodoItem> GetRepository()
75+
{
76+
_contextResolverMock
77+
.Setup(m => m.GetContext())
78+
.Returns(_contextMock.Object);
79+
80+
_contextResolverMock
81+
.Setup(m => m.GetDbSet<TodoItem>())
82+
.Returns(_dbSetMock.Object);
83+
84+
_jsonApiContextMock
85+
.Setup(m => m.AttributesToUpdate)
86+
.Returns(_attrsToUpdate);
87+
88+
_jsonApiContextMock
89+
.Setup(m => m.RelationshipsToUpdate)
90+
.Returns(_relationshipsToUpdate);
91+
92+
_jsonApiContextMock
93+
.Setup(m => m.GetDbContextResolver())
94+
.Returns(_contextResolverMock.Object);
95+
96+
return new DefaultEntityRepository<TodoItem>(
97+
_loggFactoryMock.Object,
98+
_jsonApiContextMock.Object);
99+
}
100+
}
101+
}

0 commit comments

Comments
 (0)