Resource Repositories
If you want to use a data access technology other than Entity Framework Core, you can create an implementation of IResourceRepository<TResource, TId>.
If you only need minor changes you can override the methods defined in EntityFrameworkCoreRepository<TResource, TId>.
// Program.cs
builder.Services.AddScoped<IResourceRepository<Article, long>, ArticleRepository>();
builder.Services.AddScoped<IResourceReadRepository<Article, long>, ArticleRepository>();
builder.Services.AddScoped<IResourceWriteRepository<Article, long>, ArticleRepository>();
In v4.0 we introduced an extension method that you can use to register a resource repository on all of its JsonApiDotNetCore interfaces. This is helpful when you implement (a subset of) the resource interfaces and want to register them all in one go.
// Program.cs
builder.Services.AddResourceRepository<ArticleRepository>();
Tip
If you're using auto-discovery, then resource services, repositories and resource definitions will be automatically registered for you.
A sample implementation that performs authorization might look like this.
All of the methods in EntityFrameworkCoreRepository will use the GetAll() method to get the DbSet<TResource>, so this is a good method to apply filters such as user or tenant authorization.
public class ArticleRepository : EntityFrameworkCoreRepository<Article, long>
{
private readonly IAuthenticationService _authenticationService;
public ArticleRepository(IAuthenticationService authenticationService,
ITargetedFields targetedFields, IDbContextResolver dbContextResolver,
IResourceGraph resourceGraph, IResourceFactory resourceFactory,
IEnumerable<IQueryConstraintProvider> constraintProviders,
ILoggerFactory loggerFactory,
IResourceDefinitionAccessor resourceDefinitionAccessor)
: base(targetedFields, dbContextResolver, resourceGraph, resourceFactory,
constraintProviders, loggerFactory, resourceDefinitionAccessor)
{
_authenticationService = authenticationService;
}
public override IQueryable<Article> GetAll(CancellationToken cancellationToken)
{
return base.GetAll(cancellationToken)
.Where(article => article.UserId == _authenticationService.UserId);
}
}
Multiple DbContexts
If you need to use multiple Entity Framework Core DbContexts, first create a repository for each context and inject its typed resolver.
This example shows a single DbContextARepository for all entities that are members of DbContextA.
public class DbContextARepository<TResource, TId>
: EntityFrameworkCoreRepository<TResource, TId>
where TResource : class, IIdentifiable<TId>
{
public DbContextARepository(ITargetedFields targetedFields,
DbContextResolver<DbContextA> dbContextResolver,
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
IResourceGraph resourceGraph, IResourceFactory resourceFactory,
IEnumerable<IQueryConstraintProvider> constraintProviders,
ILoggerFactory loggerFactory, IResourceDefinitionAccessor resourceDefinitionAccessor)
: base(targetedFields, dbContextResolver, resourceGraph, resourceFactory,
constraintProviders, loggerFactory, resourceDefinitionAccessor)
{
}
}
Then register the added types and use the non-generic overload of AddJsonApi to add their resources to the graph.
// Program.cs
builder.Services.AddDbContext<DbContextA>(options => options.UseSqlite("Data Source=A.db"));
builder.Services.AddDbContext<DbContextB>(options => options.UseSqlite("Data Source=B.db"));
builder.Services.AddJsonApi(dbContextTypes: new[] { typeof(DbContextA), typeof(DbContextB) });
builder.Services.AddScoped<IResourceRepository<ResourceA>, DbContextARepository<ResourceA>>();
builder.Services.AddScoped<IResourceRepository<ResourceB>, DbContextBRepository<ResourceB>>();