@chill-sharp/ui-core 1.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/chillsharp-current-user-preferences/SKILL.md +70 -0
- package/.agents/skills/chillsharp-ui-template/SKILL.md +18 -0
- package/README.md +103 -0
- package/chill-sharp-ui-core.d.ts.map +1 -0
- package/doc/AIAssistedDevelopment/README.md +185 -0
- package/doc/AttachmentModel/README.md +173 -0
- package/doc/AuthenticationModel/README.md +213 -0
- package/doc/AuthenticationModel/how-to-integreate-auth-minimal-api.md +293 -0
- package/doc/ChillSharpClient.md +464 -0
- package/doc/ClientGeneration/README.md +172 -0
- package/doc/ComplianceGuide/README.md +178 -0
- package/doc/Configuration/README.md +94 -0
- package/doc/CurrentUserPreferences.md +114 -0
- package/doc/DateTimePolicy/README.md +154 -0
- package/doc/DateTimeSerialization.md +423 -0
- package/doc/Endpoints.md +260 -0
- package/doc/HowTo/01-simple-blog-sqlite.md +153 -0
- package/doc/HowTo/02-blog-schema-labels.md +140 -0
- package/doc/HowTo/03-authentication.md +218 -0
- package/doc/HowTo/04-blog-posts-one-to-many.md +194 -0
- package/doc/HowTo/05-docker-env-variables.md +274 -0
- package/doc/HowTo/06-chunk-transactions-autocomplete.md +196 -0
- package/doc/Mcp/ChatGPT.md +291 -0
- package/doc/Mcp/README.md +799 -0
- package/doc/MenuGuide/README.md +49 -0
- package/doc/ModelPreparation.md +255 -0
- package/doc/PermissionModel/README.md +277 -0
- package/doc/README.md +228 -0
- package/doc/ReferenceExistence.md +130 -0
- package/doc/RegisterContext.md +217 -0
- package/doc/UiCore/CRUD.md +170 -0
- package/doc/UiCore/README.md +13 -0
- package/doc/ValidationModel/README.md +117 -0
- package/doc/it/AIAssistedDevelopment/README.md +185 -0
- package/doc/it/AttachmentModel/README.md +173 -0
- package/doc/it/AuthenticationModel/README.md +171 -0
- package/doc/it/AuthenticationModel/how-to-integreate-auth-minimal-api.md +292 -0
- package/doc/it/ChillSharpClient.md +464 -0
- package/doc/it/ClientGeneration/README.md +152 -0
- package/doc/it/ComplianceGuide/README.md +178 -0
- package/doc/it/Configuration/README.md +94 -0
- package/doc/it/CurrentUserPreferences.md +114 -0
- package/doc/it/DateTimePolicy/README.md +154 -0
- package/doc/it/DateTimeSerialization.md +423 -0
- package/doc/it/Endpoints.md +260 -0
- package/doc/it/HowTo/01-simple-blog-sqlite.md +152 -0
- package/doc/it/HowTo/02-blog-schema-labels.md +139 -0
- package/doc/it/HowTo/03-authentication.md +221 -0
- package/doc/it/HowTo/04-blog-posts-one-to-many.md +193 -0
- package/doc/it/HowTo/05-docker-env-variables.md +268 -0
- package/doc/it/HowTo/06-chunk-transactions-autocomplete.md +196 -0
- package/doc/it/Mcp/ChatGPT.md +291 -0
- package/doc/it/Mcp/README.md +799 -0
- package/doc/it/MenuGuide/README.md +49 -0
- package/doc/it/ModelPreparation.md +254 -0
- package/doc/it/PermissionModel/README.md +190 -0
- package/doc/it/README.md +172 -0
- package/doc/it/ReferenceExistence.md +130 -0
- package/doc/it/RegisterContext.md +218 -0
- package/doc/it/UiCore/CRUD.md +170 -0
- package/doc/it/UiCore/README.md +13 -0
- package/doc/it/ValidationModel/README.md +117 -0
- package/fesm2022/chill-sharp-ui-core.mjs +16725 -0
- package/fesm2022/chill-sharp-ui-core.mjs.map +1 -0
- package/index.d.ts +6 -0
- package/lib/chill-sharp-ui-root.component.d.ts +6 -0
- package/lib/chill-sharp-ui-root.component.d.ts.map +1 -0
- package/lib/chill-sharp-ui.routes.d.ts +3 -0
- package/lib/chill-sharp-ui.routes.d.ts.map +1 -0
- package/lib/chill.config.d.ts +5 -0
- package/lib/chill.config.d.ts.map +1 -0
- package/lib/layouts/auth-shell.component.d.ts +8 -0
- package/lib/layouts/auth-shell.component.d.ts.map +1 -0
- package/lib/layouts/workspace-page.component.d.ts +49 -0
- package/lib/layouts/workspace-page.component.d.ts.map +1 -0
- package/lib/lib/chill-form.component.d.ts +151 -0
- package/lib/lib/chill-form.component.d.ts.map +1 -0
- package/lib/lib/chill-i18n-button-label.component.d.ts +27 -0
- package/lib/lib/chill-i18n-button-label.component.d.ts.map +1 -0
- package/lib/lib/chill-i18n-label.component.d.ts +30 -0
- package/lib/lib/chill-i18n-label.component.d.ts.map +1 -0
- package/lib/lib/chill-json-input.component.d.ts +31 -0
- package/lib/lib/chill-json-input.component.d.ts.map +1 -0
- package/lib/lib/chill-polymorphic-input-controls/chill-polymorphic-boolean-control.component.d.ts +12 -0
- package/lib/lib/chill-polymorphic-input-controls/chill-polymorphic-boolean-control.component.d.ts.map +1 -0
- package/lib/lib/chill-polymorphic-input-controls/chill-polymorphic-editor-control.component.d.ts +17 -0
- package/lib/lib/chill-polymorphic-input-controls/chill-polymorphic-editor-control.component.d.ts.map +1 -0
- package/lib/lib/chill-polymorphic-input-controls/chill-polymorphic-lookup-control.component.d.ts +45 -0
- package/lib/lib/chill-polymorphic-input-controls/chill-polymorphic-lookup-control.component.d.ts.map +1 -0
- package/lib/lib/chill-polymorphic-input-controls/chill-polymorphic-scalar-control.component.d.ts +19 -0
- package/lib/lib/chill-polymorphic-input-controls/chill-polymorphic-scalar-control.component.d.ts.map +1 -0
- package/lib/lib/chill-polymorphic-input-controls/chill-polymorphic-select-control.component.d.ts +12 -0
- package/lib/lib/chill-polymorphic-input-controls/chill-polymorphic-select-control.component.d.ts.map +1 -0
- package/lib/lib/chill-polymorphic-input-controls/chill-polymorphic-textarea-control.component.d.ts +13 -0
- package/lib/lib/chill-polymorphic-input-controls/chill-polymorphic-textarea-control.component.d.ts.map +1 -0
- package/lib/lib/chill-polymorphic-input.component.d.ts +452 -0
- package/lib/lib/chill-polymorphic-input.component.d.ts.map +1 -0
- package/lib/lib/chill-polymorphic-output-controls/chill-polymorphic-output-boolean-control.component.d.ts +7 -0
- package/lib/lib/chill-polymorphic-output-controls/chill-polymorphic-output-boolean-control.component.d.ts.map +1 -0
- package/lib/lib/chill-polymorphic-output-controls/chill-polymorphic-output-lookup-control.component.d.ts +7 -0
- package/lib/lib/chill-polymorphic-output-controls/chill-polymorphic-output-lookup-control.component.d.ts.map +1 -0
- package/lib/lib/chill-polymorphic-output-controls/chill-polymorphic-output-number-control.component.d.ts +7 -0
- package/lib/lib/chill-polymorphic-output-controls/chill-polymorphic-output-number-control.component.d.ts.map +1 -0
- package/lib/lib/chill-polymorphic-output-controls/chill-polymorphic-output-temporal-control.component.d.ts +8 -0
- package/lib/lib/chill-polymorphic-output-controls/chill-polymorphic-output-temporal-control.component.d.ts.map +1 -0
- package/lib/lib/chill-polymorphic-output-controls/chill-polymorphic-output-value-control.component.d.ts +7 -0
- package/lib/lib/chill-polymorphic-output-controls/chill-polymorphic-output-value-control.component.d.ts.map +1 -0
- package/lib/lib/chill-polymorphic-output.component.d.ts +75 -0
- package/lib/lib/chill-polymorphic-output.component.d.ts.map +1 -0
- package/lib/lib/chill-table.component.d.ts +434 -0
- package/lib/lib/chill-table.component.d.ts.map +1 -0
- package/lib/lib/chill-text-editor-dialog.component.d.ts +14 -0
- package/lib/lib/chill-text-editor-dialog.component.d.ts.map +1 -0
- package/lib/lib/crud-configuration.utils.d.ts +4 -0
- package/lib/lib/crud-configuration.utils.d.ts.map +1 -0
- package/lib/lib/culture-name-options.d.ts +3 -0
- package/lib/lib/culture-name-options.d.ts.map +1 -0
- package/lib/lib/date-format-options.d.ts +3 -0
- package/lib/lib/date-format-options.d.ts.map +1 -0
- package/lib/lib/iana-time-zone-options.d.ts +3 -0
- package/lib/lib/iana-time-zone-options.d.ts.map +1 -0
- package/lib/lib/notice-transition.directive.d.ts +17 -0
- package/lib/lib/notice-transition.directive.d.ts.map +1 -0
- package/lib/lib/schema-property-dialog.component.d.ts +74 -0
- package/lib/lib/schema-property-dialog.component.d.ts.map +1 -0
- package/lib/models/chill-auth.models.d.ts +176 -0
- package/lib/models/chill-auth.models.d.ts.map +1 -0
- package/lib/models/chill-menu.models.d.ts +12 -0
- package/lib/models/chill-menu.models.d.ts.map +1 -0
- package/lib/models/chill-schema.models.d.ts +141 -0
- package/lib/models/chill-schema.models.d.ts.map +1 -0
- package/lib/models/workspace-dialog.models.d.ts +16 -0
- package/lib/models/workspace-dialog.models.d.ts.map +1 -0
- package/lib/models/workspace-task.models.d.ts +36 -0
- package/lib/models/workspace-task.models.d.ts.map +1 -0
- package/lib/pages/confirm-reset-page.component.d.ts +21 -0
- package/lib/pages/confirm-reset-page.component.d.ts.map +1 -0
- package/lib/pages/crud/attachment-upload-dialog.component.d.ts +24 -0
- package/lib/pages/crud/attachment-upload-dialog.component.d.ts.map +1 -0
- package/lib/pages/crud/crud-page.component.d.ts +244 -0
- package/lib/pages/crud/crud-page.component.d.ts.map +1 -0
- package/lib/pages/login-page.component.d.ts +21 -0
- package/lib/pages/login-page.component.d.ts.map +1 -0
- package/lib/pages/permissions/auth-role-dialog.component.d.ts +29 -0
- package/lib/pages/permissions/auth-role-dialog.component.d.ts.map +1 -0
- package/lib/pages/permissions/auth-search-select.component.d.ts +28 -0
- package/lib/pages/permissions/auth-search-select.component.d.ts.map +1 -0
- package/lib/pages/permissions/auth-user-dialog.component.d.ts +35 -0
- package/lib/pages/permissions/auth-user-dialog.component.d.ts.map +1 -0
- package/lib/pages/permissions/permission-editor.component.d.ts +55 -0
- package/lib/pages/permissions/permission-editor.component.d.ts.map +1 -0
- package/lib/pages/permissions/permissions-page.component.d.ts +38 -0
- package/lib/pages/permissions/permissions-page.component.d.ts.map +1 -0
- package/lib/pages/permissions/role-permission.component.d.ts +43 -0
- package/lib/pages/permissions/role-permission.component.d.ts.map +1 -0
- package/lib/pages/permissions/user-permission.component.d.ts +43 -0
- package/lib/pages/permissions/user-permission.component.d.ts.map +1 -0
- package/lib/pages/register-page.component.d.ts +24 -0
- package/lib/pages/register-page.component.d.ts.map +1 -0
- package/lib/pages/reset-password-page.component.d.ts +18 -0
- package/lib/pages/reset-password-page.component.d.ts.map +1 -0
- package/lib/provide-chill-sharp-ui-core.d.ts +8 -0
- package/lib/provide-chill-sharp-ui-core.d.ts.map +1 -0
- package/lib/services/chill.service.d.ts +249 -0
- package/lib/services/chill.service.d.ts.map +1 -0
- package/lib/services/workspace-dialog.service.d.ts +22 -0
- package/lib/services/workspace-dialog.service.d.ts.map +1 -0
- package/lib/services/workspace-layout.service.d.ts +13 -0
- package/lib/services/workspace-layout.service.d.ts.map +1 -0
- package/lib/services/workspace-task-registry.service.d.ts +35 -0
- package/lib/services/workspace-task-registry.service.d.ts.map +1 -0
- package/lib/services/workspace-toolbar.service.d.ts +23 -0
- package/lib/services/workspace-toolbar.service.d.ts.map +1 -0
- package/lib/services/workspace.service.d.ts +114 -0
- package/lib/services/workspace.service.d.ts.map +1 -0
- package/lib/storage-keys.d.ts +5 -0
- package/lib/storage-keys.d.ts.map +1 -0
- package/lib/tasks/crud-task/crud-task.component.d.ts +33 -0
- package/lib/tasks/crud-task/crud-task.component.d.ts.map +1 -0
- package/lib/tasks/goto-url-task/goto-url-task.component.d.ts +31 -0
- package/lib/tasks/goto-url-task/goto-url-task.component.d.ts.map +1 -0
- package/lib/workspace/confirm-message-dialog.component.d.ts +16 -0
- package/lib/workspace/confirm-message-dialog.component.d.ts.map +1 -0
- package/lib/workspace/entity-options-dialog.component.d.ts +29 -0
- package/lib/workspace/entity-options-dialog.component.d.ts.map +1 -0
- package/lib/workspace/external-task-api.d.ts +2 -0
- package/lib/workspace/external-task-api.d.ts.map +1 -0
- package/lib/workspace/user-profile-dialog.component.d.ts +30 -0
- package/lib/workspace/user-profile-dialog.component.d.ts.map +1 -0
- package/lib/workspace/workspace-dialog-host.component.d.ts +26 -0
- package/lib/workspace/workspace-dialog-host.component.d.ts.map +1 -0
- package/lib/workspace/workspace-menu-item-dialog.component.d.ts +61 -0
- package/lib/workspace/workspace-menu-item-dialog.component.d.ts.map +1 -0
- package/lib/workspace/workspace-menu.component.d.ts +104 -0
- package/lib/workspace/workspace-menu.component.d.ts.map +1 -0
- package/lib/workspace/workspace-taskbar.component.d.ts +14 -0
- package/lib/workspace/workspace-taskbar.component.d.ts.map +1 -0
- package/package.json +54 -0
- package/public-api.d.ts +56 -0
- package/public-api.d.ts.map +1 -0
- package/service-worker/chill-sharp-service-worker.js +166 -0
- package/styles/core-theme.scss +1268 -0
- package/template-customization/upgrade.ps1.template +342 -0
- package/template-customization/upgrade.sh.template +271 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# HOW-TO: Simple Blog API on SQLite
|
|
2
|
+
|
|
3
|
+
Versione italiana: [Italiano](../it/HowTo/01-simple-blog-sqlite.md)
|
|
4
|
+
|
|
5
|
+
This example shows the smallest useful ChillSharp setup: one `Blog` entity, one EF Core SQLite context, and a ChillSharp API with no schema label texts in `ChillEntity` or `ChillProperty` attributes.
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
Build a minimal API that can create and read `Blog` entities through ChillSharp.
|
|
10
|
+
|
|
11
|
+
## 1. Define the entity
|
|
12
|
+
|
|
13
|
+
Use the parameterless versions of `ChillEntity` and `ChillProperty` when you do not want to provide schema texts yet.
|
|
14
|
+
|
|
15
|
+
```csharp
|
|
16
|
+
using ChillSharp.Annotations;
|
|
17
|
+
using ChillSharp.EF;
|
|
18
|
+
using System.ComponentModel.DataAnnotations;
|
|
19
|
+
|
|
20
|
+
namespace MyBlogApp.Model;
|
|
21
|
+
|
|
22
|
+
[ChillEntity]
|
|
23
|
+
public class Blog : ChillEntity
|
|
24
|
+
{
|
|
25
|
+
[Key]
|
|
26
|
+
public override Guid Guid { get; set; }
|
|
27
|
+
|
|
28
|
+
[ChillProperty]
|
|
29
|
+
public string Name { get; set; } = string.Empty;
|
|
30
|
+
|
|
31
|
+
[ChillProperty]
|
|
32
|
+
public string? Url { get; set; }
|
|
33
|
+
|
|
34
|
+
public override string GetLabel(IChillContext context) => Name;
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 2. Define a query
|
|
39
|
+
|
|
40
|
+
`Find` is enough to load one entity by `Guid`, but a query type is still useful when you want normal filtering and projections.
|
|
41
|
+
|
|
42
|
+
```csharp
|
|
43
|
+
using ChillSharp.Annotations;
|
|
44
|
+
using ChillSharp.EF;
|
|
45
|
+
|
|
46
|
+
namespace MyBlogApp.Query;
|
|
47
|
+
|
|
48
|
+
[ChillEntity]
|
|
49
|
+
public class BlogQuery : ChillQuery
|
|
50
|
+
{
|
|
51
|
+
[ChillProperty]
|
|
52
|
+
public string? Name { get; set; }
|
|
53
|
+
|
|
54
|
+
public override IQueryable<IChillEntity> OnQuery(IChillContext context)
|
|
55
|
+
{
|
|
56
|
+
var db = (BloggingContext)context;
|
|
57
|
+
var query = db.Blogs.AsQueryable();
|
|
58
|
+
|
|
59
|
+
if (Guid.HasValue)
|
|
60
|
+
query = query.Where(x => x.Guid == Guid.Value);
|
|
61
|
+
|
|
62
|
+
if (!string.IsNullOrWhiteSpace(Name))
|
|
63
|
+
query = query.Where(x => x.Name.Contains(Name));
|
|
64
|
+
|
|
65
|
+
return query;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 3. Create the SQLite context
|
|
71
|
+
|
|
72
|
+
The context must implement `IChillContext` and return the namespace prefix used by your model and query types.
|
|
73
|
+
|
|
74
|
+
```csharp
|
|
75
|
+
using Microsoft.EntityFrameworkCore;
|
|
76
|
+
using MyBlogApp.Model;
|
|
77
|
+
|
|
78
|
+
namespace MyBlogApp;
|
|
79
|
+
|
|
80
|
+
public class BloggingContext : DbContext, IChillContext
|
|
81
|
+
{
|
|
82
|
+
public DbSet<Blog> Blogs => Set<Blog>();
|
|
83
|
+
|
|
84
|
+
public BloggingContext(DbContextOptions<BloggingContext> options) : base(options)
|
|
85
|
+
{
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public string GetChillTypePrefix() => "MyBlogApp";
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## 4. Register ChillSharp
|
|
93
|
+
|
|
94
|
+
```csharp
|
|
95
|
+
using ChillSharp.Api;
|
|
96
|
+
using Microsoft.EntityFrameworkCore;
|
|
97
|
+
|
|
98
|
+
var builder = WebApplication.CreateBuilder(args);
|
|
99
|
+
|
|
100
|
+
builder.Services.AddDbContext<BloggingContext>(options =>
|
|
101
|
+
options.UseSqlite("Data Source=blogging.db"));
|
|
102
|
+
|
|
103
|
+
builder.Services.AddChillApi<BloggingContext>();
|
|
104
|
+
|
|
105
|
+
var app = builder.Build();
|
|
106
|
+
|
|
107
|
+
using (var scope = app.Services.CreateScope())
|
|
108
|
+
{
|
|
109
|
+
var db = scope.ServiceProvider.GetRequiredService<BloggingContext>();
|
|
110
|
+
db.Database.EnsureCreated();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
app.MapChillApi();
|
|
114
|
+
app.Run();
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## 5. Create and read a blog with `ChillSharpClient`
|
|
118
|
+
|
|
119
|
+
```csharp
|
|
120
|
+
using ChillSharp.Client;
|
|
121
|
+
using ChillSharp.Client.Dto;
|
|
122
|
+
|
|
123
|
+
var client = new ChillSharpClient("http://localhost:5000/api/chill");
|
|
124
|
+
|
|
125
|
+
var blog = new ChillDtoEntity
|
|
126
|
+
{
|
|
127
|
+
ChillType = "Model.Blog",
|
|
128
|
+
Guid = Guid.NewGuid()
|
|
129
|
+
};
|
|
130
|
+
blog.Properties["Name"] = "SQLite Notes";
|
|
131
|
+
blog.Properties["Url"] = "https://example.local/sqlite-notes";
|
|
132
|
+
|
|
133
|
+
var created = client.Create(blog);
|
|
134
|
+
|
|
135
|
+
var query = new ChillDtoQuery
|
|
136
|
+
{
|
|
137
|
+
ChillType = "Query.BlogQuery"
|
|
138
|
+
};
|
|
139
|
+
query.Properties["Guid"] = created.Guid;
|
|
140
|
+
query.ResultProperties = ChillDtoProperty.Build("Guid", "Name", "Url");
|
|
141
|
+
|
|
142
|
+
var result = client.Query(query);
|
|
143
|
+
var loadedBlog = result.Results.Single();
|
|
144
|
+
Console.WriteLine($"{loadedBlog.GetString("Name")} -> {loadedBlog.GetString("Url")}");
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Notes
|
|
148
|
+
|
|
149
|
+
- `Model.Blog` and `Query.BlogQuery` are short type names. ChillSharp expands them using `GetChillTypePrefix()`.
|
|
150
|
+
- With parameterless attributes, schema metadata falls back to the CLR type and property names.
|
|
151
|
+
|
|
152
|
+
Next example: [Add schema labels and read them through ChillSharp](02-blog-schema-labels.md)
|
|
153
|
+
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# HOW-TO: Add Schema Labels to Blog and Read Them
|
|
2
|
+
|
|
3
|
+
Versione italiana: [Italiano](../it/HowTo/02-blog-schema-labels.md)
|
|
4
|
+
|
|
5
|
+
This example updates the previous `Blog` model by adding schema texts to `ChillEntity` and `ChillProperty`, then shows how to read those labels back.
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
Decorate the model with schema metadata and retrieve it through ChillSharp schema services.
|
|
10
|
+
|
|
11
|
+
## 1. Add labels to the entity and properties
|
|
12
|
+
|
|
13
|
+
```csharp
|
|
14
|
+
using ChillSharp.Annotations;
|
|
15
|
+
using ChillSharp.EF;
|
|
16
|
+
using System.ComponentModel.DataAnnotations;
|
|
17
|
+
|
|
18
|
+
namespace MyBlogApp.Model;
|
|
19
|
+
|
|
20
|
+
[ChillEntity(
|
|
21
|
+
UniquePropertyKeyString: "4E16F6C0-6B95-4D67-98BC-9F4D0D63EAF1",
|
|
22
|
+
PrimaryLanguageLabel: "Blog",
|
|
23
|
+
SecondaryLanguageLabel: "Blog")]
|
|
24
|
+
public class Blog : ChillEntity
|
|
25
|
+
{
|
|
26
|
+
[Key]
|
|
27
|
+
public override Guid Guid { get; set; }
|
|
28
|
+
|
|
29
|
+
[ChillProperty(
|
|
30
|
+
UniquePropertyKeyString: "50B1BB6C-D794-41E4-A85C-D4F9D7A6FA7E",
|
|
31
|
+
PrimaryLanguageLabel: "Blog name",
|
|
32
|
+
SecondaryLanguageLabel: "Nome blog")]
|
|
33
|
+
public string Name { get; set; } = string.Empty;
|
|
34
|
+
|
|
35
|
+
[ChillProperty(
|
|
36
|
+
UniquePropertyKeyString: "A18E7754-D8F7-45FE-B8A8-EA762A4EC9E6",
|
|
37
|
+
PrimaryLanguageLabel: "Blog url",
|
|
38
|
+
SecondaryLanguageLabel: "Url blog")]
|
|
39
|
+
public string? Url { get; set; }
|
|
40
|
+
|
|
41
|
+
public override string GetLabel(IChillContext context) => Name;
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 2. Enable schema persistence
|
|
46
|
+
|
|
47
|
+
To expose schema metadata through `get-schema`, the EF Core context must also implement `IChillSchemaDbContext`, add the schema model, and register `AddChillSchema<TContext>()`.
|
|
48
|
+
|
|
49
|
+
```csharp
|
|
50
|
+
using ChillSharp.Schema;
|
|
51
|
+
using ChillSharp.Schema.Model;
|
|
52
|
+
using Microsoft.EntityFrameworkCore;
|
|
53
|
+
using MyBlogApp.Model;
|
|
54
|
+
|
|
55
|
+
namespace MyBlogApp;
|
|
56
|
+
|
|
57
|
+
public class BloggingContext : DbContext, IChillContext, IChillSchemaDbContext
|
|
58
|
+
{
|
|
59
|
+
public DbSet<Blog> Blogs => Set<Blog>();
|
|
60
|
+
public DbSet<ChillSchemaEntry> SchemaEntries => Set<ChillSchemaEntry>();
|
|
61
|
+
|
|
62
|
+
public BloggingContext(DbContextOptions<BloggingContext> options) : base(options)
|
|
63
|
+
{
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public string GetChillTypePrefix() => "MyBlogApp";
|
|
67
|
+
|
|
68
|
+
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
69
|
+
{
|
|
70
|
+
base.OnModelCreating(modelBuilder);
|
|
71
|
+
modelBuilder.AddChillSchemaModel();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Register the schema service together with the API:
|
|
77
|
+
|
|
78
|
+
```csharp
|
|
79
|
+
using ChillSharp.Api;
|
|
80
|
+
using ChillSharp.Schema;
|
|
81
|
+
using Microsoft.EntityFrameworkCore;
|
|
82
|
+
|
|
83
|
+
var builder = WebApplication.CreateBuilder(args);
|
|
84
|
+
|
|
85
|
+
builder.Services.AddDbContext<BloggingContext>(options =>
|
|
86
|
+
options.UseSqlite("Data Source=blogging.db"));
|
|
87
|
+
|
|
88
|
+
builder.Services.AddChillApi<BloggingContext>();
|
|
89
|
+
builder.Services.AddChillSchema<BloggingContext>();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## 3. Read schema metadata from a client
|
|
93
|
+
|
|
94
|
+
`ChillSharpClient.GetSchema(...)` returns property metadata generated from the decorated model.
|
|
95
|
+
|
|
96
|
+
```csharp
|
|
97
|
+
using ChillSharp.Client;
|
|
98
|
+
|
|
99
|
+
var client = new ChillSharpClient("http://localhost:5000/api/chill");
|
|
100
|
+
var schema = client.GetSchema("Model.Blog", "default");
|
|
101
|
+
|
|
102
|
+
var nameProperty = schema?.Properties.Single(x => x.Name == "Name");
|
|
103
|
+
var urlProperty = schema?.Properties.Single(x => x.Name == "Url");
|
|
104
|
+
|
|
105
|
+
Console.WriteLine(nameProperty?.DisplayName); // Blog name
|
|
106
|
+
Console.WriteLine(urlProperty?.DisplayName); // Blog url
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## 4. Read the entity display name in server-side code
|
|
110
|
+
|
|
111
|
+
The server-side `ChillSharp.Dto.ChillDtoSchema` also carries the entity-level `DisplayName`, which is populated from `ChillEntityAttribute.PrimaryLanguageLabel`.
|
|
112
|
+
|
|
113
|
+
```csharp
|
|
114
|
+
using ChillSharp;
|
|
115
|
+
|
|
116
|
+
public class SchemaDebugService
|
|
117
|
+
{
|
|
118
|
+
private readonly IChillDtoEngine _dtoEngine;
|
|
119
|
+
|
|
120
|
+
public SchemaDebugService(IChillDtoEngine dtoEngine)
|
|
121
|
+
{
|
|
122
|
+
_dtoEngine = dtoEngine;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public string? GetBlogSchemaDisplayName()
|
|
126
|
+
{
|
|
127
|
+
var schema = _dtoEngine.GetSchema("Model.Blog", "default");
|
|
128
|
+
return schema?.DisplayName;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Notes
|
|
134
|
+
|
|
135
|
+
- Property `DisplayName` comes from `ChillPropertyAttribute.PrimaryLanguageLabel`.
|
|
136
|
+
- Entity `DisplayName` comes from `ChillEntityAttribute.PrimaryLanguageLabel`.
|
|
137
|
+
- If you later call `SetSchema(...)`, persisted schema values can override the generated defaults for a given `ChillType` and `ChillViewCode`.
|
|
138
|
+
|
|
139
|
+
Next example: [Use authentication with ChillSharp](03-authentication.md)
|
|
140
|
+
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# HOW-TO: Use Authentication with ChillSharp
|
|
2
|
+
|
|
3
|
+
Versione italiana: [Italiano](../it/HowTo/03-authentication.md)
|
|
4
|
+
|
|
5
|
+
This example shows the smallest useful authentication setup for a ChillSharp API: protect the API, enable the auth module, register an account, log in, and let `ChillSharpClient` reuse and refresh tokens automatically.
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
Expose a protected ChillSharp API backed by ASP.NET Core Identity and authenticate with `ChillSharpClient`.
|
|
10
|
+
|
|
11
|
+
## 1. Use a context that supports Identity and ChillSharp auth
|
|
12
|
+
|
|
13
|
+
The context must support your normal ChillSharp model and the auth tables.
|
|
14
|
+
|
|
15
|
+
```csharp
|
|
16
|
+
using ChillSharp;
|
|
17
|
+
using ChillSharp.Auth;
|
|
18
|
+
using Microsoft.AspNetCore.Identity;
|
|
19
|
+
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
|
20
|
+
using Microsoft.EntityFrameworkCore;
|
|
21
|
+
|
|
22
|
+
namespace MyBlogApp;
|
|
23
|
+
|
|
24
|
+
public class BloggingContext : IdentityDbContext<IdentityUser>, IChillContext, IChillAuthDbContext
|
|
25
|
+
{
|
|
26
|
+
public BloggingContext(DbContextOptions<BloggingContext> options) : base(options)
|
|
27
|
+
{
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public string GetChillTypePrefix() => "MyBlogApp";
|
|
31
|
+
|
|
32
|
+
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
33
|
+
{
|
|
34
|
+
base.OnModelCreating(modelBuilder);
|
|
35
|
+
modelBuilder.AddChillAuthModel();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 2. Register Identity, authentication, and ChillSharp auth services
|
|
41
|
+
|
|
42
|
+
Protect the normal ChillSharp API and add the auth endpoints on top of it.
|
|
43
|
+
|
|
44
|
+
```csharp
|
|
45
|
+
using ChillSharp.Api;
|
|
46
|
+
using ChillSharp.Auth;
|
|
47
|
+
using ChillSharp.Auth.Api;
|
|
48
|
+
using Microsoft.AspNetCore.Identity;
|
|
49
|
+
using Microsoft.EntityFrameworkCore;
|
|
50
|
+
|
|
51
|
+
var builder = WebApplication.CreateBuilder(args);
|
|
52
|
+
|
|
53
|
+
builder.Services.AddDbContext<BloggingContext>(options =>
|
|
54
|
+
options.UseSqlite("Data Source=blogging-auth.db"));
|
|
55
|
+
|
|
56
|
+
builder.Services.AddIdentityCore<IdentityUser>()
|
|
57
|
+
.AddEntityFrameworkStores<BloggingContext>()
|
|
58
|
+
.AddSignInManager()
|
|
59
|
+
.AddDefaultTokenProviders();
|
|
60
|
+
|
|
61
|
+
builder.Services.AddAuthentication(ChillAuthIdentityDefaults.AuthenticationScheme)
|
|
62
|
+
.AddChillAuthBearer();
|
|
63
|
+
|
|
64
|
+
builder.Services.AddAuthorization();
|
|
65
|
+
|
|
66
|
+
builder.Services.AddChillApi<BloggingContext, IdentityUser>(options =>
|
|
67
|
+
{
|
|
68
|
+
options.ProtectedApi = true;
|
|
69
|
+
options.ReturnPasswordResetTokensInResponse = true;
|
|
70
|
+
options.InitializeRootUserOnStartup = true;
|
|
71
|
+
options.CreateChillAuthUserForRoot = true;
|
|
72
|
+
options.RootUserName = "root";
|
|
73
|
+
options.RootPassword = "Pass123$";
|
|
74
|
+
options.RootEmail = "root@example.com";
|
|
75
|
+
options.RootDisplayName = "Root Administrator";
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
When `CreateChillAuthUserForRoot = true`, startup also creates the linked ChillSharp `AuthUser` and sets `CanManagePermissions = true` for that root user.
|
|
80
|
+
|
|
81
|
+
You can also provide the same bootstrap values through environment variables instead of hardcoding them:
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
CHILLSHARP_AUTH_ROOT_USERNAME=root
|
|
85
|
+
CHILLSHARP_AUTH_ROOT_PASSWORD=Pass123$
|
|
86
|
+
CHILLSHARP_AUTH_ROOT_EMAIL=root@example.com
|
|
87
|
+
CHILLSHARP_AUTH_ROOT_DISPLAY_NAME=Root Administrator
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 3. Enable middleware and map the API
|
|
91
|
+
|
|
92
|
+
`MapChillApi()` exposes both the normal ChillSharp endpoints and the auth endpoints once the auth services are registered.
|
|
93
|
+
|
|
94
|
+
```csharp
|
|
95
|
+
var app = builder.Build();
|
|
96
|
+
|
|
97
|
+
app.UseAuthentication();
|
|
98
|
+
app.UseAuthorization();
|
|
99
|
+
|
|
100
|
+
using (var scope = app.Services.CreateScope())
|
|
101
|
+
{
|
|
102
|
+
var db = scope.ServiceProvider.GetRequiredService<BloggingContext>();
|
|
103
|
+
db.Database.EnsureCreated();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
app.MapChillApi();
|
|
107
|
+
app.Run();
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 4. Use the root user to manage permissions
|
|
111
|
+
|
|
112
|
+
The root-user initializer is the easiest bootstrap path for a new protected system because the linked ChillSharp auth user is created with permission-management enabled.
|
|
113
|
+
|
|
114
|
+
```csharp
|
|
115
|
+
var rootClient = new ChillSharpClient("http://localhost:5000/api/chill");
|
|
116
|
+
|
|
117
|
+
var rootLogin = rootClient.LoginAuthAccount(new LoginAuthIdentityRequest
|
|
118
|
+
{
|
|
119
|
+
UserNameOrEmail = "root",
|
|
120
|
+
Password = "Pass123$"
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
var authUsers = rootClient.GetAuthUsers();
|
|
124
|
+
Console.WriteLine(authUsers.Count);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
That login can call auth-management endpoints such as users, roles, and permission rules because the generated root `AuthUser` has `CanManagePermissions = true`.
|
|
128
|
+
|
|
129
|
+
## 5. Register the first normal account
|
|
130
|
+
|
|
131
|
+
Create the client with the normal Chill base URL. Auth calls automatically switch from `/api/chill` to `/api/chill-auth`.
|
|
132
|
+
|
|
133
|
+
```csharp
|
|
134
|
+
using ChillSharp.Auth.Contracts;
|
|
135
|
+
using ChillSharp.Client;
|
|
136
|
+
|
|
137
|
+
var client = new ChillSharpClient("http://localhost:5000/api/chill");
|
|
138
|
+
|
|
139
|
+
var registerResponse = client.RegisterAuthAccount(new RegisterAuthIdentityRequest
|
|
140
|
+
{
|
|
141
|
+
UserName = "admin",
|
|
142
|
+
Email = "admin@example.com",
|
|
143
|
+
Password = "Pass123$",
|
|
144
|
+
DisplayName = "Administrator",
|
|
145
|
+
DisplayCultureName = "it-IT",
|
|
146
|
+
CreateChillAuthUser = true
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
After a successful registration, the client stores the returned access token and refresh token internally.
|
|
151
|
+
|
|
152
|
+
If `DisplayCultureName` is provided and `CreateChillAuthUser = true`, the linked `AuthUser` is initialized with culture-based defaults for:
|
|
153
|
+
|
|
154
|
+
- `DisplayTimeZone`
|
|
155
|
+
- `DisplayDateFormat`
|
|
156
|
+
- `DisplayNumberFormat`
|
|
157
|
+
|
|
158
|
+
For example, `it-IT` typically produces presets like:
|
|
159
|
+
|
|
160
|
+
- `DisplayTimeZone = "W. Europe Standard Time"`
|
|
161
|
+
- `DisplayDateFormat = "DD/MM/YYYY"`
|
|
162
|
+
- `DisplayNumberFormat = "1.000,00"`
|
|
163
|
+
|
|
164
|
+
These are server-side presets and can be edited later through auth-user management.
|
|
165
|
+
|
|
166
|
+
## 6. Log in explicitly
|
|
167
|
+
|
|
168
|
+
If the account already exists, log in with the same `ChillSharpClient`.
|
|
169
|
+
|
|
170
|
+
```csharp
|
|
171
|
+
var loginResponse = client.LoginAuthAccount(new LoginAuthIdentityRequest
|
|
172
|
+
{
|
|
173
|
+
UserNameOrEmail = "admin",
|
|
174
|
+
Password = "Pass123$"
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## 7. Call protected endpoints
|
|
179
|
+
|
|
180
|
+
Once authenticated, the same client can call protected ChillSharp endpoints.
|
|
181
|
+
|
|
182
|
+
```csharp
|
|
183
|
+
using ChillSharp.Client.Dto;
|
|
184
|
+
|
|
185
|
+
var query = new ChillDtoQuery
|
|
186
|
+
{
|
|
187
|
+
ChillType = "Query.BlogQuery"
|
|
188
|
+
};
|
|
189
|
+
query.ResultProperties = ChillDtoProperty.Build("Guid", "Name");
|
|
190
|
+
|
|
191
|
+
var result = client.Query(query);
|
|
192
|
+
Console.WriteLine(result.Results.Count);
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## 8. Let the client refresh tokens automatically
|
|
196
|
+
|
|
197
|
+
You do not need to manually attach bearer tokens on every request. If the client already has a refresh token, authenticated calls renew the access token when needed.
|
|
198
|
+
|
|
199
|
+
```csharp
|
|
200
|
+
var roles = client.GetAuthRoles();
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
You can also refresh explicitly:
|
|
204
|
+
|
|
205
|
+
```csharp
|
|
206
|
+
var refreshed = client.RefreshAuthAccount();
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Notes
|
|
210
|
+
|
|
211
|
+
- Use `options.ProtectedApi = true` on `AddChillApi<TContext>(...)` if your ChillSharp endpoints must require authentication.
|
|
212
|
+
- The root user created by `AddChillApi<TContext, TUser>(...)` is the bootstrap administrator path. When `CreateChillAuthUserForRoot = true`, the linked ChillSharp `AuthUser` is created with `CanManagePermissions = true`.
|
|
213
|
+
- `CreateChillAuthUser = true` creates the linked ChillSharp `AuthUser`, but it does not automatically grant admin permissions.
|
|
214
|
+
- `DisplayCultureName` on registration presets `DisplayTimeZone`, `DisplayDateFormat`, and `DisplayNumberFormat` for the linked `AuthUser`.
|
|
215
|
+
- For production, bootstrap the first administrator deliberately, for example through root-user initialization or a trusted install-time flow.
|
|
216
|
+
|
|
217
|
+
Next example: [Create a Docker image and configure it with environment variables](04-docker-env-variables.md)
|
|
218
|
+
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# HOW-TO: Handle a One-to-Many Blog-Posts Relation
|
|
2
|
+
|
|
3
|
+
Versione italiana: [Italiano](../it/HowTo/04-blog-posts-one-to-many.md)
|
|
4
|
+
|
|
5
|
+
This example extends the blog model with posts and shows how to load one blog together with its posts in a single `ChillSharpClient.Query(...)` call.
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
Model a `Blog` to `Posts` one-to-many relation and project the nested collection from the ChillSharp client.
|
|
10
|
+
|
|
11
|
+
## 1. Define the entities
|
|
12
|
+
|
|
13
|
+
```csharp
|
|
14
|
+
using ChillSharp.Annotations;
|
|
15
|
+
using ChillSharp.EF;
|
|
16
|
+
using System.ComponentModel.DataAnnotations;
|
|
17
|
+
|
|
18
|
+
namespace MyBlogApp.Model;
|
|
19
|
+
|
|
20
|
+
[ChillEntity]
|
|
21
|
+
public class Blog : ChillEntity
|
|
22
|
+
{
|
|
23
|
+
[Key]
|
|
24
|
+
public override Guid Guid { get; set; }
|
|
25
|
+
|
|
26
|
+
[ChillProperty]
|
|
27
|
+
public string Name { get; set; } = string.Empty;
|
|
28
|
+
|
|
29
|
+
[ChillProperty]
|
|
30
|
+
public string? Url { get; set; }
|
|
31
|
+
|
|
32
|
+
[ChillProperty]
|
|
33
|
+
public ICollection<Post>? Posts { get; set; }
|
|
34
|
+
|
|
35
|
+
public override string GetLabel(IChillContext context) => Name;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
[ChillEntity]
|
|
39
|
+
public class Post : ChillEntity
|
|
40
|
+
{
|
|
41
|
+
[Key]
|
|
42
|
+
public override Guid Guid { get; set; }
|
|
43
|
+
|
|
44
|
+
[ChillProperty]
|
|
45
|
+
public Blog? Blog { get; set; }
|
|
46
|
+
|
|
47
|
+
[ChillProperty]
|
|
48
|
+
public DateTime? CreatedAt { get; set; }
|
|
49
|
+
|
|
50
|
+
[ChillProperty]
|
|
51
|
+
public string Title { get; set; } = string.Empty;
|
|
52
|
+
|
|
53
|
+
[ChillProperty]
|
|
54
|
+
public string Content { get; set; } = string.Empty;
|
|
55
|
+
|
|
56
|
+
public override void OnCreate(IChillContext context)
|
|
57
|
+
{
|
|
58
|
+
base.OnCreate(context);
|
|
59
|
+
CreatedAt = DateTime.UtcNow;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public override string GetLabel(IChillContext context) => Title;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 2. Add both sets to the context
|
|
67
|
+
|
|
68
|
+
```csharp
|
|
69
|
+
using Microsoft.EntityFrameworkCore;
|
|
70
|
+
using MyBlogApp.Model;
|
|
71
|
+
|
|
72
|
+
namespace MyBlogApp;
|
|
73
|
+
|
|
74
|
+
public class BloggingContext : DbContext, IChillContext
|
|
75
|
+
{
|
|
76
|
+
public DbSet<Blog> Blogs => Set<Blog>();
|
|
77
|
+
public DbSet<Post> Posts => Set<Post>();
|
|
78
|
+
|
|
79
|
+
public BloggingContext(DbContextOptions<BloggingContext> options) : base(options)
|
|
80
|
+
{
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public string GetChillTypePrefix() => "MyBlogApp";
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 3. Define a query for blogs
|
|
88
|
+
|
|
89
|
+
```csharp
|
|
90
|
+
using ChillSharp.Annotations;
|
|
91
|
+
using ChillSharp.EF;
|
|
92
|
+
|
|
93
|
+
namespace MyBlogApp.Query;
|
|
94
|
+
|
|
95
|
+
[ChillEntity]
|
|
96
|
+
public class BlogQuery : ChillQuery
|
|
97
|
+
{
|
|
98
|
+
[ChillProperty]
|
|
99
|
+
public string? Name { get; set; }
|
|
100
|
+
|
|
101
|
+
public override IQueryable<IChillEntity> OnQuery(IChillContext context)
|
|
102
|
+
{
|
|
103
|
+
var db = (BloggingContext)context;
|
|
104
|
+
var query = db.Blogs.AsQueryable();
|
|
105
|
+
|
|
106
|
+
if (Guid.HasValue)
|
|
107
|
+
query = query.Where(x => x.Guid == Guid.Value);
|
|
108
|
+
|
|
109
|
+
if (!string.IsNullOrWhiteSpace(Name))
|
|
110
|
+
query = query.Where(x => x.Name.Contains(Name));
|
|
111
|
+
|
|
112
|
+
return query;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## 4. Create a blog and two posts
|
|
118
|
+
|
|
119
|
+
The relation is created by sending the parent `Blog` DTO as the `Blog` property of each `Post`.
|
|
120
|
+
|
|
121
|
+
```csharp
|
|
122
|
+
using ChillSharp.Client;
|
|
123
|
+
using ChillSharp.Client.Dto;
|
|
124
|
+
|
|
125
|
+
var client = new ChillSharpClient("http://localhost:5000/api/chill");
|
|
126
|
+
|
|
127
|
+
var blog = new ChillDtoEntity
|
|
128
|
+
{
|
|
129
|
+
ChillType = "Model.Blog",
|
|
130
|
+
Guid = Guid.NewGuid()
|
|
131
|
+
};
|
|
132
|
+
blog.Properties["Name"] = "ChillSharp Blog";
|
|
133
|
+
blog.Properties["Url"] = "https://example.local/chillsharp-blog";
|
|
134
|
+
|
|
135
|
+
var createdBlog = client.Create(blog);
|
|
136
|
+
|
|
137
|
+
var firstPost = new ChillDtoEntity
|
|
138
|
+
{
|
|
139
|
+
ChillType = "Model.Post",
|
|
140
|
+
Guid = Guid.NewGuid()
|
|
141
|
+
};
|
|
142
|
+
firstPost.Properties["Title"] = "First post";
|
|
143
|
+
firstPost.Properties["Content"] = "Hello from ChillSharp";
|
|
144
|
+
firstPost.Properties["Blog"] = createdBlog.Mock();
|
|
145
|
+
client.Create(firstPost);
|
|
146
|
+
|
|
147
|
+
var secondPost = new ChillDtoEntity
|
|
148
|
+
{
|
|
149
|
+
ChillType = "Model.Post",
|
|
150
|
+
Guid = Guid.NewGuid()
|
|
151
|
+
};
|
|
152
|
+
secondPost.Properties["Title"] = "Second post";
|
|
153
|
+
secondPost.Properties["Content"] = "Nested queries are handy";
|
|
154
|
+
secondPost.Properties["Blog"] = createdBlog.Mock();
|
|
155
|
+
client.Create(secondPost);
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## 5. Load one blog with its posts in one call
|
|
159
|
+
|
|
160
|
+
This is the part that matters most for a one-to-many relation: the nested projection is described through `ChillDtoProperty.With(...)`, and the returned collection is read with `GetCollection(...)`.
|
|
161
|
+
|
|
162
|
+
```csharp
|
|
163
|
+
using ChillSharp.Client.Dto;
|
|
164
|
+
|
|
165
|
+
var query = new ChillDtoQuery
|
|
166
|
+
{
|
|
167
|
+
ChillType = "Query.BlogQuery"
|
|
168
|
+
};
|
|
169
|
+
query.Properties["Guid"] = createdBlog.Guid;
|
|
170
|
+
query.ResultProperties = ChillDtoProperty.Build(
|
|
171
|
+
"Guid",
|
|
172
|
+
"Name",
|
|
173
|
+
"Url",
|
|
174
|
+
ChillDtoProperty.With("Posts", "Guid", "Title", "CreatedAt"));
|
|
175
|
+
|
|
176
|
+
var queryResult = client.Query(query);
|
|
177
|
+
var loadedBlog = queryResult.Results.Single();
|
|
178
|
+
|
|
179
|
+
Console.WriteLine(loadedBlog.GetString("Name"));
|
|
180
|
+
|
|
181
|
+
foreach (var post in loadedBlog.GetCollection("Posts"))
|
|
182
|
+
{
|
|
183
|
+
Console.WriteLine(post.GetString("Title"));
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## What this gives you
|
|
188
|
+
|
|
189
|
+
- one HTTP call to load the parent entity and the child collection
|
|
190
|
+
- a strongly-typed projection builder on the client side
|
|
191
|
+
- DTO-based relation handling without writing a dedicated controller for `Blog` or `Post`
|
|
192
|
+
|
|
193
|
+
Next: [Back to the documentation index](../README.md)
|
|
194
|
+
|