Skip to content

Commit 0657acf

Browse files
committed
Seed core example with sample data, open browser on run, use long IDs
1 parent a6feeeb commit 0657acf

File tree

10 files changed

+146
-20
lines changed

10 files changed

+146
-20
lines changed

src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using JetBrains.Annotations;
22
using JsonApiDotNetCoreExample.Models;
33
using Microsoft.EntityFrameworkCore;
4+
using Microsoft.EntityFrameworkCore.Metadata;
45

56
// @formatter:wrap_chained_method_calls chop_always
67

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

1920
protected override void OnModelCreating(ModelBuilder builder)
2021
{
21-
// When deleting a person, un-assign him/her from existing todo items.
22+
// When deleting a person, un-assign him/her from existing todo-items.
2223
builder.Entity<Person>()
2324
.HasMany(person => person.AssignedTodoItems)
24-
.WithOne(todoItem => todoItem.Assignee!);
25+
.WithOne(todoItem => todoItem.Assignee);
2526

26-
// When deleting a person, the todo items he/she owns are deleted too.
27-
builder.Entity<TodoItem>()
28-
.HasOne(todoItem => todoItem.Owner)
29-
.WithMany();
27+
// When deleting a person, the todo-items he/she owns are deleted too.
28+
builder.Entity<Person>()
29+
.HasMany(person => person.OwnedTodoItems)
30+
.WithOne(todoItem => todoItem.Owner);
31+
32+
AdjustDeleteBehaviorForJsonApi(builder);
33+
}
34+
35+
private static void AdjustDeleteBehaviorForJsonApi(ModelBuilder builder)
36+
{
37+
foreach (IMutableForeignKey foreignKey in builder.Model.GetEntityTypes()
38+
.SelectMany(entityType => entityType.GetForeignKeys()))
39+
{
40+
if (foreignKey.DeleteBehavior == DeleteBehavior.ClientSetNull)
41+
{
42+
foreignKey.DeleteBehavior = DeleteBehavior.SetNull;
43+
}
44+
45+
if (foreignKey.DeleteBehavior == DeleteBehavior.ClientCascade)
46+
{
47+
foreignKey.DeleteBehavior = DeleteBehavior.Cascade;
48+
}
49+
}
3050
}
3151
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
namespace JsonApiDotNetCoreExample.Data;
2+
3+
internal abstract class RotatingList
4+
{
5+
public static RotatingList<T> Create<T>(int count, Func<int, T> createElement)
6+
{
7+
List<T> elements = new();
8+
9+
for (int index = 0; index < count; index++)
10+
{
11+
T element = createElement(index);
12+
elements.Add(element);
13+
}
14+
15+
return new RotatingList<T>(elements);
16+
}
17+
}
18+
19+
internal sealed class RotatingList<T>
20+
{
21+
private int _index = -1;
22+
23+
public IList<T> Elements { get; }
24+
25+
public RotatingList(IList<T> elements)
26+
{
27+
Elements = elements;
28+
}
29+
30+
public T GetNext()
31+
{
32+
_index++;
33+
return Elements[_index % Elements.Count];
34+
}
35+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using JetBrains.Annotations;
2+
using JsonApiDotNetCoreExample.Models;
3+
4+
namespace JsonApiDotNetCoreExample.Data;
5+
6+
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
7+
internal sealed class Seeder
8+
{
9+
public static async Task CreateSampleDataAsync(AppDbContext dbContext)
10+
{
11+
const int todoItemCount = 500;
12+
const int personCount = 50;
13+
const int tagCount = 25;
14+
15+
RotatingList<Person> people = RotatingList.Create(personCount, index => new Person
16+
{
17+
FirstName = $"FirstName{index + 1:D2}",
18+
LastName = $"LastName{index + 1:D2}"
19+
});
20+
21+
RotatingList<Tag> tags = RotatingList.Create(tagCount, index => new Tag
22+
{
23+
Name = $"TagName{index + 1:D2}"
24+
});
25+
26+
RotatingList<TodoItemPriority> priorities = RotatingList.Create(3, index => (TodoItemPriority)(index + 1));
27+
28+
RotatingList<TodoItem> todoItems = RotatingList.Create(todoItemCount, index =>
29+
{
30+
var todoItem = new TodoItem
31+
{
32+
Description = $"TodoItem{index + 1:D3}",
33+
Priority = priorities.GetNext(),
34+
DurationInHours = index,
35+
CreatedAt = DateTimeOffset.UtcNow,
36+
Owner = people.GetNext(),
37+
Tags = new HashSet<Tag>
38+
{
39+
tags.GetNext(),
40+
tags.GetNext(),
41+
tags.GetNext()
42+
}
43+
};
44+
45+
if (index % 3 == 0)
46+
{
47+
todoItem.Assignee = people.GetNext();
48+
}
49+
50+
return todoItem;
51+
});
52+
53+
dbContext.TodoItems.AddRange(todoItems.Elements);
54+
await dbContext.SaveChangesAsync();
55+
}
56+
}

src/Examples/JsonApiDotNetCoreExample/Definitions/TodoItemDefinition.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
namespace JsonApiDotNetCoreExample.Definitions;
1111

1212
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
13-
public sealed class TodoItemDefinition : JsonApiResourceDefinition<TodoItem, int>
13+
public sealed class TodoItemDefinition : JsonApiResourceDefinition<TodoItem, long>
1414
{
1515
private readonly ISystemClock _systemClock;
1616

@@ -29,7 +29,7 @@ private SortExpression GetDefaultSortOrder()
2929
{
3030
return CreateSortExpressionFromLambda(new PropertySortOrder
3131
{
32-
(todoItem => todoItem.Priority, ListSortDirection.Descending),
32+
(todoItem => todoItem.Priority, ListSortDirection.Ascending),
3333
(todoItem => todoItem.LastModifiedAt, ListSortDirection.Descending)
3434
});
3535
}
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.ComponentModel.DataAnnotations.Schema;
12
using JetBrains.Annotations;
23
using JsonApiDotNetCore.Resources;
34
using JsonApiDotNetCore.Resources.Annotations;
@@ -6,14 +7,21 @@ namespace JsonApiDotNetCoreExample.Models;
67

78
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
89
[Resource]
9-
public sealed class Person : Identifiable<int>
10+
public sealed class Person : Identifiable<long>
1011
{
1112
[Attr]
1213
public string? FirstName { get; set; }
1314

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

18+
[Attr(Capabilities = AttrCapabilities.AllowView)]
19+
[NotMapped]
20+
public string DisplayName => FirstName != null ? $"{FirstName} {LastName}" : LastName;
21+
22+
[HasMany]
23+
public ISet<TodoItem> OwnedTodoItems { get; set; } = new HashSet<TodoItem>();
24+
1725
[HasMany]
1826
public ISet<TodoItem> AssignedTodoItems { get; set; } = new HashSet<TodoItem>();
1927
}

src/Examples/JsonApiDotNetCoreExample/Models/Tag.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreExample.Models;
77

88
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
99
[Resource]
10-
public sealed class Tag : Identifiable<int>
10+
public sealed class Tag : Identifiable<long>
1111
{
1212
[Attr]
1313
[MinLength(1)]

src/Examples/JsonApiDotNetCoreExample/Models/TodoItem.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace JsonApiDotNetCoreExample.Models;
77

88
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
99
[Resource]
10-
public sealed class TodoItem : Identifiable<int>
10+
public sealed class TodoItem : Identifiable<long>
1111
{
1212
[Attr]
1313
public string Description { get; set; } = null!;
@@ -16,6 +16,9 @@ public sealed class TodoItem : Identifiable<int>
1616
[Required]
1717
public TodoItemPriority? Priority { get; set; }
1818

19+
[Attr]
20+
public long? DurationInHours { get; set; }
21+
1922
[Attr(Capabilities = AttrCapabilities.AllowFilter | AttrCapabilities.AllowSort | AttrCapabilities.AllowView)]
2023
public DateTimeOffset CreatedAt { get; set; }
2124

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

28-
[HasOne(Capabilities = HasOneCapabilities.AllowView | HasOneCapabilities.AllowSet)]
31+
[HasOne]
2932
public Person? Assignee { get; set; }
3033

31-
[HasMany(Capabilities = HasManyCapabilities.AllowView | HasManyCapabilities.AllowFilter)]
34+
[HasMany]
3235
public ISet<Tag> Tags { get; set; } = new HashSet<Tag>();
3336
}

src/Examples/JsonApiDotNetCoreExample/Models/TodoItemPriority.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace JsonApiDotNetCoreExample.Models;
55
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
66
public enum TodoItemPriority
77
{
8-
Low,
9-
Medium,
10-
High
8+
High = 1,
9+
Medium = 2,
10+
Low = 3
1111
}

src/Examples/JsonApiDotNetCoreExample/Program.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ static void ConfigureServices(WebApplicationBuilder builder)
6565
options.IncludeTotalResourceCount = true;
6666
options.SerializerOptions.WriteIndented = true;
6767
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
68+
6869
#if DEBUG
6970
options.IncludeExceptionStackTraceInErrors = true;
7071
options.IncludeRequestBodyInErrors = true;
@@ -98,5 +99,8 @@ static async Task CreateDatabaseAsync(IServiceProvider serviceProvider)
9899
await using AsyncServiceScope scope = serviceProvider.CreateAsyncScope();
99100

100101
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
102+
await dbContext.Database.EnsureDeletedAsync();
101103
await dbContext.Database.EnsureCreatedAsync();
104+
105+
await Seeder.CreateSampleDataAsync(dbContext);
102106
}

src/Examples/JsonApiDotNetCoreExample/Properties/launchSettings.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@
1111
"profiles": {
1212
"IIS Express": {
1313
"commandName": "IISExpress",
14-
"launchBrowser": false,
15-
"launchUrl": "api/todoItems",
14+
"launchBrowser": true,
15+
"launchUrl": "api/todoItems?include=tags&filter=equals(priority,'High')",
1616
"environmentVariables": {
1717
"ASPNETCORE_ENVIRONMENT": "Development"
1818
}
1919
},
2020
"Kestrel": {
2121
"commandName": "Project",
22-
"launchBrowser": false,
23-
"launchUrl": "api/todoItems",
22+
"launchBrowser": true,
23+
"launchUrl": "api/todoItems?include=tags&filter=equals(priority,'High')",
2424
"applicationUrl": "https://localhost:44340;http://localhost:14140",
2525
"environmentVariables": {
2626
"ASPNETCORE_ENVIRONMENT": "Development"

0 commit comments

Comments
 (0)