Skip to content

Remove short-hand interfaces for TId is int #1093

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion benchmarks/BenchmarkResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace Benchmarks
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
public sealed class BenchmarkResource : Identifiable
public sealed class BenchmarkResource : Identifiable<int>
{
[Attr(PublicName = BenchmarkResourcePublicNames.NameAttr)]
public string Name { get; set; }
Expand Down
4 changes: 2 additions & 2 deletions benchmarks/Deserialization/DeserializationBenchmarkBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ protected DeserializationBenchmarkBase()
var resourceDefinitionAccessor = new ResourceDefinitionAccessor(resourceGraph, serviceContainer);

serviceContainer.AddService(typeof(IResourceDefinitionAccessor), resourceDefinitionAccessor);
serviceContainer.AddService(typeof(IResourceDefinition<ResourceA>), new JsonApiResourceDefinition<ResourceA>(resourceGraph));
serviceContainer.AddService(typeof(IResourceDefinition<ResourceA, int>), new JsonApiResourceDefinition<ResourceA, int>(resourceGraph));

// ReSharper disable once VirtualMemberCallInConstructor
JsonApiRequest request = CreateJsonApiRequest(resourceGraph);
Expand All @@ -56,7 +56,7 @@ protected DeserializationBenchmarkBase()
protected abstract JsonApiRequest CreateJsonApiRequest(IResourceGraph resourceGraph);

[UsedImplicitly(ImplicitUseTargetFlags.Members)]
public sealed class ResourceA : Identifiable
public sealed class ResourceA : Identifiable<int>
{
[Attr]
public bool Attribute01 { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/Serialization/SerializationBenchmarkBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ protected SerializationBenchmarkBase()
protected abstract IEvaluatedIncludeCache CreateEvaluatedIncludeCache(IResourceGraph resourceGraph);

[UsedImplicitly(ImplicitUseTargetFlags.Members)]
public sealed class ResourceA : Identifiable
public sealed class ResourceA : Identifiable<int>
{
[Attr]
public bool Attribute01 { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/SubResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace Benchmarks
{
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
public sealed class SubResource : Identifiable
public sealed class SubResource : Identifiable<int>
{
[Attr]
public string Value { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ This section documents the package API and is generated from the XML source comm

- [`JsonApiOptions`](JsonApiDotNetCore.Configuration.JsonApiOptions.yml)
- [`IResourceGraph`](JsonApiDotNetCore.Configuration.IResourceGraph.yml)
- [`JsonApiResourceDefinition<TResource>`](JsonApiDotNetCore.Resources.JsonApiResourceDefinition-1.yml)
- [`JsonApiResourceDefinition<TResource, TId>`](JsonApiDotNetCore.Resources.JsonApiResourceDefinition-2.yml)
14 changes: 7 additions & 7 deletions docs/getting-started/step-by-step.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ Install-Package JsonApiDotNetCore
### Define Models

Define your domain models such that they implement `IIdentifiable<TId>`.
The easiest way to do this is to inherit from `Identifiable`
The easiest way to do this is to inherit from `Identifiable<TId>`.

```c#
public class Person : Identifiable
public class Person : Identifiable<int>
{
[Attr]
public string Name { get; set; }
Expand All @@ -47,7 +47,7 @@ public class Person : Identifiable

### Define DbContext

Nothing special here, just an ordinary `DbContext`
Nothing special here, just an ordinary `DbContext`.

```
public class AppDbContext : DbContext
Expand All @@ -63,14 +63,14 @@ public class AppDbContext : DbContext

### Define Controllers

You need to create controllers that inherit from `JsonApiController<TResource>` or `JsonApiController<TResource, TId>`
where `TResource` is the model that inherits from `Identifiable<TId>`
You need to create controllers that inherit from `JsonApiController<TResource, TId>`
where `TResource` is the model that inherits from `Identifiable<TId>`.

```c#
public class PeopleController : JsonApiController<Person>
public class PeopleController : JsonApiController<Person, int>
{
public PeopleController(IJsonApiOptions options, ILoggerFactory loggerFactory,
IResourceService<Person> resourceService)
IResourceService<Person, int> resourceService)
: base(options, loggerFactory, resourceService)
{
}
Expand Down
31 changes: 7 additions & 24 deletions docs/usage/extensibility/controllers.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,12 @@
# Controllers

You need to create controllers that inherit from `JsonApiController<TResource>`

```c#
public class ArticlesController : JsonApiController<Article>
{
public ArticlesController(IJsonApiOptions options, ILoggerFactory loggerFactory,
IResourceService<Article> resourceService)
: base(options, loggerFactory, resourceService)
{
}
}
```

## Non-Integer Type Keys

If your model is using a type other than `int` for the primary key, you must explicitly declare it in the controller/service/repository definitions.
You need to create controllers that inherit from `JsonApiController<TResource, TId>`

```c#
public class ArticlesController : JsonApiController<Article, Guid>
//---------------------------------------------------------- ^^^^
{
public ArticlesController(IJsonApiOptions options, ILoggerFactory loggerFactory,
IResourceService<Article, Guid> resourceService)
//----------------------- ^^^^
: base(options, loggerFactory, resourceService)
{
}
Expand All @@ -39,10 +22,10 @@ In this example, if a client attempts to do anything other than GET a resource,
This approach is ok, but introduces some boilerplate that can easily be avoided.

```c#
public class ArticlesController : BaseJsonApiController<Article>
public class ArticlesController : BaseJsonApiController<Article, int>
{
public ArticlesController(IJsonApiOptions options, ILoggerFactory loggerFactory,
IResourceService<Article> resourceService)
IResourceService<Article, int> resourceService)
: base(options, loggerFactory, resourceService)
{
}
Expand Down Expand Up @@ -76,10 +59,10 @@ An attempt to use one of the blacklisted methods will result in a HTTP 405 Metho

```c#
[HttpReadOnly]
public class ArticlesController : BaseJsonApiController<Article>
public class ArticlesController : BaseJsonApiController<Article, int>
{
public ArticlesController(IJsonApiOptions options, ILoggerFactory loggerFactory,
IResourceService<Article> resourceService)
IResourceService<Article, int> resourceService)
: base(options, loggerFactory, resourceService)
{
}
Expand All @@ -95,10 +78,10 @@ As with the ActionFilter attributes, if a service implementation is not availabl
For more information about resource service injection, see [Replacing injected services](~/usage/extensibility/layer-overview.md#replacing-injected-services) and [Resource Services](~/usage/extensibility/services.md).

```c#
public class ReportsController : BaseJsonApiController<Report>
public class ReportsController : BaseJsonApiController<Report, int>
{
public ReportsController(IJsonApiOptions options, ILoggerFactory loggerFactory,
IGetAllService<Report> getAllService)
IGetAllService<Report, int> getAllService)
: base(options, loggerFactory, getAllService)
{
}
Expand Down
31 changes: 15 additions & 16 deletions docs/usage/extensibility/repositories.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ The repository should then be registered in Startup.cs.
```c#
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IResourceRepository<Article>, ArticleRepository>();
services.AddScoped<IResourceReadRepository<Article>, ArticleRepository>();
services.AddScoped<IResourceWriteRepository<Article>, ArticleRepository>();
services.AddScoped<IResourceRepository<Article, int>, ArticleRepository>();
services.AddScoped<IResourceReadRepository<Article, int>, ArticleRepository>();
services.AddScoped<IResourceWriteRepository<Article, int>, ArticleRepository>();
}
```

Expand All @@ -34,18 +34,18 @@ 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.

```c#
public class ArticleRepository : EntityFrameworkCoreRepository<Article>
public class ArticleRepository : EntityFrameworkCoreRepository<Article, int>
{
private readonly IAuthenticationService _authenticationService;

public ArticleRepository(IAuthenticationService authenticationService,
ITargetedFields targetedFields, IDbContextResolver dbContextResolver,
IResourceGraph resourceGraph, IGenericServiceFactory genericServiceFactory,
IResourceFactory resourceFactory,
IResourceGraph resourceGraph, IResourceFactory resourceFactory,
IEnumerable<IQueryConstraintProvider> constraintProviders,
ILoggerFactory loggerFactory)
: base(targetedFields, dbContextResolver, resourceGraph, genericServiceFactory,
resourceFactory, constraintProviders, loggerFactory)
ILoggerFactory loggerFactory,
IResourceDefinitionAccessor resourceDefinitionAccessor)
: base(targetedFields, dbContextResolver, resourceGraph, resourceFactory,
constraintProviders, loggerFactory, resourceDefinitionAccessor)
{
_authenticationService = authenticationService;
}
Expand All @@ -64,18 +64,17 @@ If you need to use multiple Entity Framework Core DbContexts, first create a rep
This example shows a single `DbContextARepository` for all entities that are members of `DbContextA`.

```c#
public class DbContextARepository<TResource> : EntityFrameworkCoreRepository<TResource>
where TResource : class, IIdentifiable<int>
public class DbContextARepository<TResource, TId> : EntityFrameworkCoreRepository<TResource, TId>
where TResource : class, IIdentifiable<TId>
{
public DbContextARepository(ITargetedFields targetedFields,
DbContextResolver<DbContextA> dbContextResolver,
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
IResourceGraph resourceGraph, IGenericServiceFactory genericServiceFactory,
IResourceFactory resourceFactory,
IResourceGraph resourceGraph, IResourceFactory resourceFactory,
IEnumerable<IQueryConstraintProvider> constraintProviders,
ILoggerFactory loggerFactory)
: base(targetedFields, dbContextResolver, resourceGraph, genericServiceFactory,
resourceFactory, constraintProviders, loggerFactory)
ILoggerFactory loggerFactory, IResourceDefinitionAccessor resourceDefinitionAccessor)
: base(targetedFields, dbContextResolver, resourceGraph, resourceFactory,
constraintProviders, loggerFactory, resourceDefinitionAccessor)
{
}
}
Expand Down
16 changes: 8 additions & 8 deletions docs/usage/extensibility/resource-definitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ public class Startup
resource definition on the container yourself:

```c#
services.AddScoped<ResourceDefinition<Product>, ProductResource>();
services.AddScoped<ResourceDefinition<Product, int>, ProductDefinition>();
```

## Customizing queries

_since v4.0_

For various reasons (see examples below) you may need to change parts of the query, depending on resource type.
`JsonApiResourceDefinition<TResource>` (which is an empty implementation of `IResourceDefinition<TResource>`) provides overridable methods that pass you the result of query string parameter parsing.
`JsonApiResourceDefinition<TResource, TId>` (which is an empty implementation of `IResourceDefinition<TResource, TId>`) provides overridable methods that pass you the result of query string parameter parsing.
The value returned by you determines what will be used to execute the query.

An intermediate format (`QueryExpression` and derived types) is used, which enables us to separate JSON:API implementation
Expand All @@ -45,7 +45,7 @@ For example, you may accept some sensitive data that should only be exposed to a
**Note:** to exclude attributes unconditionally, use `[Attr(Capabilities = ~AttrCapabilities.AllowView)]` on a resource class property.

```c#
public class UserDefinition : JsonApiResourceDefinition<User>
public class UserDefinition : JsonApiResourceDefinition<User, int>
{
public UserDefinition(IResourceGraph resourceGraph)
: base(resourceGraph)
Expand Down Expand Up @@ -104,7 +104,7 @@ Content-Type: application/vnd.api+json
You can define the default sort order if no `sort` query string parameter is provided.

```c#
public class AccountDefinition : JsonApiResourceDefinition<Account>
public class AccountDefinition : JsonApiResourceDefinition<Account, int>
{
public AccountDefinition(IResourceGraph resourceGraph)
: base(resourceGraph)
Expand Down Expand Up @@ -132,7 +132,7 @@ public class AccountDefinition : JsonApiResourceDefinition<Account>
You may want to enforce pagination on large database tables.

```c#
public class AccessLogDefinition : JsonApiResourceDefinition<AccessLog>
public class AccessLogDefinition : JsonApiResourceDefinition<AccessLog, int>
{
public AccessLogDefinition(IResourceGraph resourceGraph)
: base(resourceGraph)
Expand Down Expand Up @@ -163,7 +163,7 @@ public class AccessLogDefinition : JsonApiResourceDefinition<AccessLog>
The next example filters out `Account` resources that are suspended.

```c#
public class AccountDefinition : JsonApiResourceDefinition<Account>
public class AccountDefinition : JsonApiResourceDefinition<Account, int>
{
public AccountDefinition(IResourceGraph resourceGraph)
: base(resourceGraph)
Expand Down Expand Up @@ -192,7 +192,7 @@ public class AccountDefinition : JsonApiResourceDefinition<Account>
In the example below, an error is returned when a user tries to include the manager of an employee.

```c#
public class EmployeeDefinition : JsonApiResourceDefinition<Employee>
public class EmployeeDefinition : JsonApiResourceDefinition<Employee, int>
{
public EmployeeDefinition(IResourceGraph resourceGraph)
: base(resourceGraph)
Expand Down Expand Up @@ -227,7 +227,7 @@ Note this directly influences the Entity Framework Core `IQueryable`. As opposed
But it only works on primary resource endpoints (for example: /articles, but not on /blogs/1/articles or /blogs?include=articles).

```c#
public class ItemDefinition : JsonApiResourceDefinition<Item>
public class ItemDefinition : JsonApiResourceDefinition<Item, int>
{
public ItemDefinition(IResourceGraph resourceGraph)
: base(resourceGraph)
Expand Down
12 changes: 6 additions & 6 deletions docs/usage/extensibility/services.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ This allows you to customize it however you want. This is also a good place to i

## Supplementing Default Behavior

If you don't need to alter the underlying mechanisms, you can inherit from `JsonApiResourceService<TResource>` and override the existing methods.
If you don't need to alter the underlying mechanisms, you can inherit from `JsonApiResourceService<TResource, TId>` and override the existing methods.
In simple cases, you can also just wrap the base implementation with your custom logic.

A simple example would be to send notifications when a resource gets created.

```c#
public class TodoItemService : JsonApiResourceService<TodoItem>
public class TodoItemService : JsonApiResourceService<TodoItem, int>
{
private readonly INotificationService _notificationService;

Expand Down Expand Up @@ -43,21 +43,21 @@ public class TodoItemService : JsonApiResourceService<TodoItem>
## Not Using Entity Framework Core?

As previously discussed, this library uses Entity Framework Core by default.
If you'd like to use another ORM that does not provide what JsonApiResourceService depends upon, you can use a custom `IResourceService<TResource>` implementation.
If you'd like to use another ORM that does not provide what JsonApiResourceService depends upon, you can use a custom `IResourceService<TResource, TId>` implementation.

```c#
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// add the service override for Product
services.AddScoped<IResourceService<Product>, ProductService>();
services.AddScoped<IResourceService<Product, int>, ProductService>();

// add your own Data Access Object
services.AddScoped<IProductDao, ProductDao>();
}

// ProductService.cs
public class ProductService : IResourceService<Product>
public class ProductService : IResourceService<Product, int>
{
private readonly IProductDao _dao;

Expand Down Expand Up @@ -154,7 +154,7 @@ public class Startup
Then in the controller, you should inherit from the base controller and pass the services into the named, optional base parameters:

```c#
public class ArticlesController : BaseJsonApiController<Article>
public class ArticlesController : BaseJsonApiController<Article, int>
{
public ArticlesController(IJsonApiOptions options, ILoggerFactory loggerFactory,
ICreateService<Article, int> create, IDeleteService<Article, int> delete)
Expand Down
4 changes: 2 additions & 2 deletions docs/usage/meta.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ public sealed class CopyrightResponseMeta : IResponseMeta

## Resource Meta

Resource-specific metadata can be added by implementing `IResourceDefinition<TResource, TId>.GetMeta` (or overriding it on `JsonApiResourceDefinition`):
Resource-specific metadata can be added by implementing `IResourceDefinition<TResource, TId>.GetMeta` (or overriding it on `JsonApiResourceDefinition<TResource, TId>`):

```c#
public class PersonDefinition : JsonApiResourceDefinition<Person>
public class PersonDefinition : JsonApiResourceDefinition<Person, int>
{
public PersonDefinition(IResourceGraph resourceGraph)
: base(resourceGraph)
Expand Down
2 changes: 1 addition & 1 deletion docs/usage/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ options.ValidateModelState = true;
```

```c#
public class Person : Identifiable
public class Person : Identifiable<int>
{
[Attr]
[Required]
Expand Down
4 changes: 2 additions & 2 deletions docs/usage/resource-graph.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,15 @@ services.AddJsonApi(resources: builder =>
2. The model is decorated with a `ResourceAttribute`
```c#
[Resource("myResources")]
public class MyModel : Identifiable
public class MyModel : Identifiable<int>
{
}
```

3. The configured naming convention (by default this is camel-case).
```c#
// this will be registered as "myModels"
public class MyModel : Identifiable
public class MyModel : Identifiable<int>
{
}
```
Expand Down
Loading