Skip to content

Commit 77d37c9

Browse files
committed
Updates NoEntityFrameworkExample to use a hardcoded in-memory dataset, demonstrating how to implement a custom read-only resource service and resource repository, which compiles the produced LINQ query and executes it against the dataset.
1 parent 9083b62 commit 77d37c9

28 files changed

+2561
-358
lines changed

src/Examples/NoEntityFrameworkExample/Data/AppDbContext.cs

-16
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
using JetBrains.Annotations;
2+
using NoEntityFrameworkExample.Models;
3+
4+
namespace NoEntityFrameworkExample.Data;
5+
6+
[UsedImplicitly(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
7+
internal sealed class Database
8+
{
9+
public static List<TodoItem> TodoItems { get; }
10+
public static List<Tag> Tags { get; }
11+
public static List<Person> People { get; }
12+
13+
static Database()
14+
{
15+
int personIndex = 0;
16+
int tagIndex = 0;
17+
int todoItemIndex = 0;
18+
19+
var john = new Person
20+
{
21+
Id = ++personIndex,
22+
FirstName = "John",
23+
LastName = "Doe"
24+
};
25+
26+
var jane = new Person
27+
{
28+
Id = ++personIndex,
29+
FirstName = "Jane",
30+
LastName = "Doe"
31+
};
32+
33+
var personalTag = new Tag
34+
{
35+
Id = ++tagIndex,
36+
Name = "Personal"
37+
};
38+
39+
var familyTag = new Tag
40+
{
41+
Id = ++tagIndex,
42+
Name = "Family"
43+
};
44+
45+
var businessTag = new Tag
46+
{
47+
Id = ++tagIndex,
48+
Name = "Business"
49+
};
50+
51+
TodoItems = new List<TodoItem>
52+
{
53+
new()
54+
{
55+
Id = ++todoItemIndex,
56+
Description = "Make homework",
57+
DurationInHours = 3,
58+
Priority = TodoItemPriority.High,
59+
Owner = john,
60+
Assignee = jane,
61+
Tags =
62+
{
63+
personalTag
64+
}
65+
},
66+
new()
67+
{
68+
Id = ++todoItemIndex,
69+
Description = "Book vacation",
70+
DurationInHours = 2,
71+
Priority = TodoItemPriority.Low,
72+
Owner = jane,
73+
Tags =
74+
{
75+
personalTag
76+
}
77+
},
78+
new()
79+
{
80+
Id = ++todoItemIndex,
81+
Description = "Cook dinner",
82+
DurationInHours = 1,
83+
Priority = TodoItemPriority.Medium,
84+
Owner = jane,
85+
Assignee = john,
86+
Tags =
87+
{
88+
familyTag,
89+
personalTag
90+
}
91+
},
92+
new()
93+
{
94+
Id = ++todoItemIndex,
95+
Description = "Check emails",
96+
DurationInHours = 1,
97+
Priority = TodoItemPriority.Low,
98+
Owner = john,
99+
Assignee = john,
100+
Tags =
101+
{
102+
businessTag
103+
}
104+
}
105+
};
106+
107+
Tags = new List<Tag>
108+
{
109+
personalTag,
110+
familyTag,
111+
businessTag
112+
};
113+
114+
People = new List<Person>
115+
{
116+
john,
117+
jane
118+
};
119+
120+
foreach (Tag tag in Tags)
121+
{
122+
tag.TodoItems = TodoItems.Where(todoItem => todoItem.Tags.Any(tagInTodoItem => tagInTodoItem.Id == tag.Id)).ToHashSet();
123+
}
124+
125+
foreach (Person person in People)
126+
{
127+
person.OwnedTodoItems = TodoItems.Where(todoItem => todoItem.Owner == person).ToHashSet();
128+
person.AssignedTodoItems = TodoItems.Where(todoItem => todoItem.Assignee == person).ToHashSet();
129+
}
130+
}
131+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System.Reflection;
2+
using JsonApiDotNetCore.Configuration;
3+
using Microsoft.EntityFrameworkCore.Metadata;
4+
5+
namespace NoEntityFrameworkExample.Data;
6+
7+
internal sealed class InMemoryModel : RuntimeModel
8+
{
9+
public InMemoryModel(IResourceGraph resourceGraph)
10+
{
11+
foreach (ResourceType resourceType in resourceGraph.GetResourceTypes())
12+
{
13+
RuntimeEntityType entityType = AddEntityType(resourceType.ClrType.FullName!, resourceType.ClrType);
14+
SetEntityProperties(entityType, resourceType);
15+
}
16+
}
17+
18+
private static void SetEntityProperties(RuntimeEntityType entityType, ResourceType resourceType)
19+
{
20+
foreach (PropertyInfo property in resourceType.ClrType.GetProperties())
21+
{
22+
entityType.AddProperty(property.Name, property.PropertyType, property);
23+
}
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using JsonApiDotNetCore.Configuration;
2+
using JsonApiDotNetCore.Resources.Annotations;
3+
using NoEntityFrameworkExample.Models;
4+
5+
namespace NoEntityFrameworkExample;
6+
7+
internal sealed class InMemoryInverseNavigationResolver : IInverseNavigationResolver
8+
{
9+
private readonly IResourceGraph _resourceGraph;
10+
11+
public InMemoryInverseNavigationResolver(IResourceGraph resourceGraph)
12+
{
13+
_resourceGraph = resourceGraph;
14+
}
15+
16+
/// <inheritdoc />
17+
public void Resolve()
18+
{
19+
ResourceType todoItemType = _resourceGraph.GetResourceType<TodoItem>();
20+
RelationshipAttribute todoItemOwnerRelationship = todoItemType.GetRelationshipByPropertyName(nameof(TodoItem.Owner));
21+
RelationshipAttribute todoItemAssigneeRelationship = todoItemType.GetRelationshipByPropertyName(nameof(TodoItem.Assignee));
22+
RelationshipAttribute todoItemTagsRelationship = todoItemType.GetRelationshipByPropertyName(nameof(TodoItem.Tags));
23+
24+
ResourceType personType = _resourceGraph.GetResourceType<Person>();
25+
RelationshipAttribute personOwnedTodoItemsRelationship = personType.GetRelationshipByPropertyName(nameof(Person.OwnedTodoItems));
26+
RelationshipAttribute personAssignedTodoItemsRelationship = personType.GetRelationshipByPropertyName(nameof(Person.AssignedTodoItems));
27+
28+
ResourceType tagType = _resourceGraph.GetResourceType<Tag>();
29+
RelationshipAttribute tagTodoItemsRelationship = tagType.GetRelationshipByPropertyName(nameof(Tag.TodoItems));
30+
31+
// Inverse navigations are required for pagination on non-primary endpoints.
32+
todoItemOwnerRelationship.InverseNavigationProperty = personOwnedTodoItemsRelationship.Property;
33+
todoItemAssigneeRelationship.InverseNavigationProperty = personAssignedTodoItemsRelationship.Property;
34+
todoItemTagsRelationship.InverseNavigationProperty = tagTodoItemsRelationship.Property;
35+
36+
personOwnedTodoItemsRelationship.InverseNavigationProperty = todoItemOwnerRelationship.Property;
37+
personAssignedTodoItemsRelationship.InverseNavigationProperty = todoItemAssigneeRelationship.Property;
38+
39+
tagTodoItemsRelationship.InverseNavigationProperty = todoItemTagsRelationship.Property;
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.ComponentModel.DataAnnotations.Schema;
2+
using JetBrains.Annotations;
3+
using JsonApiDotNetCore.Resources;
4+
using JsonApiDotNetCore.Resources.Annotations;
5+
6+
namespace NoEntityFrameworkExample.Models;
7+
8+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9+
[Resource]
10+
public sealed class Person : Identifiable<long>
11+
{
12+
[Attr]
13+
public string? FirstName { get; set; }
14+
15+
[Attr]
16+
public string LastName { get; set; } = null!;
17+
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+
25+
[HasMany]
26+
public ISet<TodoItem> AssignedTodoItems { get; set; } = new HashSet<TodoItem>();
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.ComponentModel.DataAnnotations;
2+
using JetBrains.Annotations;
3+
using JsonApiDotNetCore.Resources;
4+
using JsonApiDotNetCore.Resources.Annotations;
5+
6+
namespace NoEntityFrameworkExample.Models;
7+
8+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
9+
[Resource]
10+
public sealed class Tag : Identifiable<long>
11+
{
12+
[Attr]
13+
[MinLength(1)]
14+
public string Name { get; set; } = null!;
15+
16+
[HasMany]
17+
public ISet<TodoItem> TodoItems { get; set; } = new HashSet<TodoItem>();
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System.ComponentModel.DataAnnotations;
2+
using JetBrains.Annotations;
3+
using JsonApiDotNetCore.Controllers;
4+
using JsonApiDotNetCore.Resources;
5+
using JsonApiDotNetCore.Resources.Annotations;
6+
7+
namespace NoEntityFrameworkExample.Models;
8+
9+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
10+
[Resource(GenerateControllerEndpoints = JsonApiEndpoints.Query)]
11+
public sealed class TodoItem : Identifiable<long>
12+
{
13+
[Attr]
14+
public string Description { get; set; } = null!;
15+
16+
[Attr]
17+
[Required]
18+
public TodoItemPriority? Priority { get; set; }
19+
20+
[Attr]
21+
public long? DurationInHours { get; set; }
22+
23+
[HasOne]
24+
public Person Owner { get; set; } = null!;
25+
26+
[HasOne]
27+
public Person? Assignee { get; set; }
28+
29+
[HasMany]
30+
public ISet<Tag> Tags { get; set; } = new HashSet<Tag>();
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using JetBrains.Annotations;
2+
3+
namespace NoEntityFrameworkExample.Models;
4+
5+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
6+
public enum TodoItemPriority
7+
{
8+
High = 1,
9+
Medium = 2,
10+
Low = 3
11+
}

src/Examples/NoEntityFrameworkExample/Models/WorkItem.cs

-22
This file was deleted.

src/Examples/NoEntityFrameworkExample/NoEntityFrameworkExample.csproj

-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
</ItemGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="Dapper" Version="2.0.123" />
1413
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="$(EFCoreVersion)" />
15-
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="$(EFCorePostgresVersion)" />
1614
</ItemGroup>
1715
</Project>

0 commit comments

Comments
 (0)