Skip to content

Update examples #1269

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 14 commits into from
May 11, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
32 changes: 26 additions & 6 deletions src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using JetBrains.Annotations;
using JsonApiDotNetCoreExample.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;

// @formatter:wrap_chained_method_calls chop_always

Expand All @@ -18,14 +19,33 @@ public AppDbContext(DbContextOptions<AppDbContext> options)

protected override void OnModelCreating(ModelBuilder builder)
{
// When deleting a person, un-assign him/her from existing todo items.
// When deleting a person, un-assign him/her from existing todo-items.
builder.Entity<Person>()
.HasMany(person => person.AssignedTodoItems)
.WithOne(todoItem => todoItem.Assignee!);
.WithOne(todoItem => todoItem.Assignee);

// When deleting a person, the todo items he/she owns are deleted too.
builder.Entity<TodoItem>()
.HasOne(todoItem => todoItem.Owner)
.WithMany();
// When deleting a person, the todo-items he/she owns are deleted too.
builder.Entity<Person>()
.HasMany(person => person.OwnedTodoItems)
.WithOne(todoItem => todoItem.Owner);

AdjustDeleteBehaviorForJsonApi(builder);
}

private static void AdjustDeleteBehaviorForJsonApi(ModelBuilder builder)
{
foreach (IMutableForeignKey foreignKey in builder.Model.GetEntityTypes()
.SelectMany(entityType => entityType.GetForeignKeys()))
{
if (foreignKey.DeleteBehavior == DeleteBehavior.ClientSetNull)
{
foreignKey.DeleteBehavior = DeleteBehavior.SetNull;
}

if (foreignKey.DeleteBehavior == DeleteBehavior.ClientCascade)
{
foreignKey.DeleteBehavior = DeleteBehavior.Cascade;
}
}
}
}
35 changes: 35 additions & 0 deletions src/Examples/JsonApiDotNetCoreExample/Data/RotatingList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
namespace JsonApiDotNetCoreExample.Data;

internal abstract class RotatingList
{
public static RotatingList<T> Create<T>(int count, Func<int, T> createElement)
{
List<T> elements = new();

for (int index = 0; index < count; index++)
{
T element = createElement(index);
elements.Add(element);
}

return new RotatingList<T>(elements);
}
}

internal sealed class RotatingList<T>
{
private int _index = -1;

public IList<T> Elements { get; }

public RotatingList(IList<T> elements)
{
Elements = elements;
}

public T GetNext()
{
_index++;
return Elements[_index % Elements.Count];
}
}
56 changes: 56 additions & 0 deletions src/Examples/JsonApiDotNetCoreExample/Data/Seeder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using JetBrains.Annotations;
using JsonApiDotNetCoreExample.Models;

namespace JsonApiDotNetCoreExample.Data;

[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
internal sealed class Seeder
{
public static async Task CreateSampleDataAsync(AppDbContext dbContext)
{
const int todoItemCount = 500;
const int personCount = 50;
const int tagCount = 25;

RotatingList<Person> people = RotatingList.Create(personCount, index => new Person
{
FirstName = $"FirstName{index + 1:D2}",
LastName = $"LastName{index + 1:D2}"
});

RotatingList<Tag> tags = RotatingList.Create(tagCount, index => new Tag
{
Name = $"TagName{index + 1:D2}"
});

RotatingList<TodoItemPriority> priorities = RotatingList.Create(3, index => (TodoItemPriority)(index + 1));

RotatingList<TodoItem> todoItems = RotatingList.Create(todoItemCount, index =>
{
var todoItem = new TodoItem
{
Description = $"TodoItem{index + 1:D3}",
Priority = priorities.GetNext(),
DurationInHours = index,
CreatedAt = DateTimeOffset.UtcNow,
Owner = people.GetNext(),
Tags = new HashSet<Tag>
{
tags.GetNext(),
tags.GetNext(),
tags.GetNext()
}
};

if (index % 3 == 0)
{
todoItem.Assignee = people.GetNext();
}

return todoItem;
});

dbContext.TodoItems.AddRange(todoItems.Elements);
await dbContext.SaveChangesAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
namespace JsonApiDotNetCoreExample.Definitions;

[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
public sealed class TodoItemDefinition : JsonApiResourceDefinition<TodoItem, int>
public sealed class TodoItemDefinition : JsonApiResourceDefinition<TodoItem, long>
{
private readonly ISystemClock _systemClock;

Expand All @@ -29,7 +29,7 @@ private SortExpression GetDefaultSortOrder()
{
return CreateSortExpressionFromLambda(new PropertySortOrder
{
(todoItem => todoItem.Priority, ListSortDirection.Descending),
(todoItem => todoItem.Priority, ListSortDirection.Ascending),
(todoItem => todoItem.LastModifiedAt, ListSortDirection.Descending)
});
}
Expand Down
10 changes: 9 additions & 1 deletion src/Examples/JsonApiDotNetCoreExample/Models/Person.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.ComponentModel.DataAnnotations.Schema;
using JetBrains.Annotations;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Resources.Annotations;
Expand All @@ -6,14 +7,21 @@ namespace JsonApiDotNetCoreExample.Models;

[UsedImplicitly(ImplicitUseTargetFlags.Members)]
[Resource]
public sealed class Person : Identifiable<int>
public sealed class Person : Identifiable<long>
{
[Attr]
public string? FirstName { get; set; }

[Attr]
public string LastName { get; set; } = null!;

[Attr(Capabilities = AttrCapabilities.AllowView)]
[NotMapped]
public string DisplayName => FirstName != null ? $"{FirstName} {LastName}" : LastName;

[HasMany]
public ISet<TodoItem> OwnedTodoItems { get; set; } = new HashSet<TodoItem>();

[HasMany]
public ISet<TodoItem> AssignedTodoItems { get; set; } = new HashSet<TodoItem>();
}
2 changes: 1 addition & 1 deletion src/Examples/JsonApiDotNetCoreExample/Models/Tag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreExample.Models;

[UsedImplicitly(ImplicitUseTargetFlags.Members)]
[Resource]
public sealed class Tag : Identifiable<int>
public sealed class Tag : Identifiable<long>
{
[Attr]
[MinLength(1)]
Expand Down
9 changes: 6 additions & 3 deletions src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreExample.Models;

[UsedImplicitly(ImplicitUseTargetFlags.Members)]
[Resource]
public sealed class TodoItem : Identifiable<int>
public sealed class TodoItem : Identifiable<long>
{
[Attr]
public string Description { get; set; } = null!;
Expand All @@ -16,6 +16,9 @@ public sealed class TodoItem : Identifiable<int>
[Required]
public TodoItemPriority? Priority { get; set; }

[Attr]
public long? DurationInHours { get; set; }

[Attr(Capabilities = AttrCapabilities.AllowFilter | AttrCapabilities.AllowSort | AttrCapabilities.AllowView)]
public DateTimeOffset CreatedAt { get; set; }

Expand All @@ -25,9 +28,9 @@ public sealed class TodoItem : Identifiable<int>
[HasOne]
public Person Owner { get; set; } = null!;

[HasOne(Capabilities = HasOneCapabilities.AllowView | HasOneCapabilities.AllowSet)]
[HasOne]
public Person? Assignee { get; set; }

[HasMany(Capabilities = HasManyCapabilities.AllowView | HasManyCapabilities.AllowFilter)]
[HasMany]
public ISet<Tag> Tags { get; set; } = new HashSet<Tag>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace JsonApiDotNetCoreExample.Models;
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
public enum TodoItemPriority
{
Low,
Medium,
High
High = 1,
Medium = 2,
Low = 3
}
4 changes: 4 additions & 0 deletions src/Examples/JsonApiDotNetCoreExample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ static void ConfigureServices(WebApplicationBuilder builder)
options.IncludeTotalResourceCount = true;
options.SerializerOptions.WriteIndented = true;
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());

#if DEBUG
options.IncludeExceptionStackTraceInErrors = true;
options.IncludeRequestBodyInErrors = true;
Expand Down Expand Up @@ -98,5 +99,8 @@ static async Task CreateDatabaseAsync(IServiceProvider serviceProvider)
await using AsyncServiceScope scope = serviceProvider.CreateAsyncScope();

var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await dbContext.Database.EnsureDeletedAsync();
await dbContext.Database.EnsureCreatedAsync();

await Seeder.CreateSampleDataAsync(dbContext);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"launchUrl": "api/todoItems",
"launchBrowser": true,
"launchUrl": "api/todoItems?include=tags&filter=equals(priority,'High')",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Kestrel": {
"commandName": "Project",
"launchBrowser": false,
"launchUrl": "api/todoItems",
"launchBrowser": true,
"launchUrl": "api/todoItems?include=tags&filter=equals(priority,'High')",
"applicationUrl": "https://localhost:44340;http://localhost:14140",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
Expand Down