diff --git a/Directory.Packages.props b/Directory.Packages.props
index 1438ca8..569a221 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -6,34 +6,42 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
+
+
\ No newline at end of file
diff --git a/GerstITS.Examples.Api/Common/Configurations/WebApiConfiguration.cs b/GerstITS.Examples.Api/Common/Configurations/WebApiConfiguration.cs
index fc4b105..bbc0517 100644
--- a/GerstITS.Examples.Api/Common/Configurations/WebApiConfiguration.cs
+++ b/GerstITS.Examples.Api/Common/Configurations/WebApiConfiguration.cs
@@ -1,4 +1,5 @@
-using GerstITS.Authentication.OpenId;
+using GerstITS.Authentication.ApiKey;
+using GerstITS.Authentication.OpenId;
using GerstITS.Data.EntityFramework;
using GerstITS.System.Configurations;
using GerstITS.Web.Api.Builder;
@@ -11,6 +12,7 @@ internal sealed class WebApiConfiguration : GerstITS.System.Configurations.IConf
#region Properties
public IServerConfiguration Server { get; }
+ public IApiKeyConfiguration ApiKey { get; }
public IOpenIdConfiguration OpenId { get; }
public ISwaggerConfiguration Swagger { get; set; }
public IEntityFrameworkMigrationConfiguration EntityFrameworkMigration { get; set; }
@@ -24,7 +26,8 @@ internal sealed class WebApiConfiguration : GerstITS.System.Configurations.IConf
var prefix = this.ToConfigurationPrefix();
EntityFrameworkMigration = new EntityFrameworkMigrationConfiguration(prefix, configuration);
- //OpenId = new OpenIdConfiguration(prefix, configuration);
+ ApiKey = new ApiKeyConfiguration(prefix, configuration);
+ OpenId = new OpenIdConfiguration(prefix, configuration);
Server = new ServerConfiguration(prefix, configuration);
Swagger = new SwaggerConfiguration(prefix, configuration);
}
diff --git a/GerstITS.Examples.Api/Common/StartupTasks/ExampleStartupTask.cs b/GerstITS.Examples.Api/Common/StartupTasks/ExampleStartupTask.cs
index eb8de52..692d8b4 100644
--- a/GerstITS.Examples.Api/Common/StartupTasks/ExampleStartupTask.cs
+++ b/GerstITS.Examples.Api/Common/StartupTasks/ExampleStartupTask.cs
@@ -7,14 +7,14 @@ namespace GerstITS.Examples.Api.StartupTasks;
internal class ExampleStartupTask : IStartupTask
{
- #region Fields#
+ #region Fields
private readonly IExampleProvider _provider;
private readonly IJsonConvert _jsonConvert;
#endregion
- #region Construtcors
+ #region Constructors
public ExampleStartupTask(IExampleProvider provider,
IJsonConvert jsonConvert)
diff --git a/GerstITS.Examples.Api/GerstITS.Examples.Api.csproj b/GerstITS.Examples.Api/GerstITS.Examples.Api.csproj
index 4239de7..d7f0335 100644
--- a/GerstITS.Examples.Api/GerstITS.Examples.Api.csproj
+++ b/GerstITS.Examples.Api/GerstITS.Examples.Api.csproj
@@ -6,7 +6,7 @@
© 2025 Gerst ITS
Gerst ITS
Gerst ITS Examples Web Api
- Exapmle Web Api
+ Example Web Api
0.0.0.0
0.0.0.0
0.0.0.0
@@ -18,7 +18,7 @@
true
1591
- bin\Release\net8.0\GerstITS.Example.Api.xml
+ bin\Debug\net10.0\GerstITS.Examples.Api.xml
@@ -27,18 +27,23 @@
true
1591
- bin\Release\net8.0\GerstITS.Example.Api.xml
+ bin\Release\net10.0\GerstITS.Examples.Api.xml
+
+
+
+
+
diff --git a/GerstITS.Examples.Api/Module.Configurations.cs b/GerstITS.Examples.Api/Module.Configurations.cs
index a9f8771..b60ee12 100644
--- a/GerstITS.Examples.Api/Module.Configurations.cs
+++ b/GerstITS.Examples.Api/Module.Configurations.cs
@@ -11,6 +11,7 @@ public sealed partial class Module
container.AddSingleton();
container.AddSingleton(c => c.GetService().EntityFrameworkMigration);
+ container.AddSingleton(c => c.GetService().ApiKey);
container.AddSingleton(c => c.GetService().OpenId);
container.AddSingleton(c => c.GetService().Server);
container.AddSingleton(c => c.GetService().Swagger);
diff --git a/GerstITS.Examples.Api/Module.cs b/GerstITS.Examples.Api/Module.cs
index aba8ba2..cad4a73 100644
--- a/GerstITS.Examples.Api/Module.cs
+++ b/GerstITS.Examples.Api/Module.cs
@@ -10,6 +10,7 @@ public sealed partial class Module : IIoCModule
{
RegisterConfigurations(container);
RegisterExceptionHandling(container);
+ RegisterSession(container);
RegisterStartupTasks(container);
}
diff --git a/GerstITS.Examples.Api/Program.cs b/GerstITS.Examples.Api/Program.cs
index 6a2cbc7..2816d83 100644
--- a/GerstITS.Examples.Api/Program.cs
+++ b/GerstITS.Examples.Api/Program.cs
@@ -31,6 +31,7 @@ public class Program
.UseAuthentication()
.UseAuthorization()
.UseRouting()
+ .UseSession()
.UseEndpoints(endpoints => endpoints.MapControllers())
.UseRewriteUnknownPathsToIndexSite(ApplicationEnvironment.WebApi.BaseUrl)
.UseSystemIndependentStaticFiles()
diff --git a/GerstITS.Examples.Api/appsettings.json b/GerstITS.Examples.Api/appsettings.json
index 39c7877..e0c61db 100644
--- a/GerstITS.Examples.Api/appsettings.json
+++ b/GerstITS.Examples.Api/appsettings.json
@@ -7,7 +7,23 @@
}
},
"AllowedHosts": "*",
+ "ConnectionStrings": {
+ "CustomerDbContext": "Host=localhost;Port=5432;Database=gerstits_examples;Username=postgres;Password=postgres"
+ },
+ "Mail": {
+ "SmtpServer": "smtp.example.com",
+ "SmtpPort": 587,
+ "SenderDisplayName": "Gerst ITS Examples",
+ "SenderAddress": "noreply@example.com",
+ "Username": "noreply@example.com",
+ "Password": "change-me",
+ "UseSsl": true
+ },
"WebApi": {
+ "ApiKey": {
+ "Header": "X-Api-Key",
+ "Key": "example-api-key"
+ },
"Swagger": {
"SupportEMail": "support@example.com",
"TermsOfService": "https://www.example.com/impressum/",
@@ -16,7 +32,7 @@
"Url": "https://opensource.org/licenses/MIT"
}
},
- "EntityFrameworkmigration": {
+ "EntityFrameworkMigration": {
"AutoMigrate": true
},
"Server": {
diff --git a/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldJob.cs b/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldJob.cs
index 57abec4..e33b480 100644
--- a/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldJob.cs
+++ b/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldJob.cs
@@ -16,7 +16,7 @@ public class SayHelloWorldJob : JobBase
#endregion
- #region Cosntructors
+ #region Constructors
public SayHelloWorldJob(SayHelloWorldJobConfiguration configuration,
ISystemClock systemClock)
diff --git a/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldWithDefaultNameUsageJob.cs b/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldWithDefaultNameUsageJob.cs
index de269d1..c78d0ed 100644
--- a/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldWithDefaultNameUsageJob.cs
+++ b/GerstITS.Examples.Jobs.SayHelloWorld/Jobs/SayHelloWorldWithDefaultNameUsageJob.cs
@@ -15,7 +15,7 @@ public class SayHelloWorldWithDefaultNameUsageJob : JobBase
#endregion
- #region Cosntructors
+ #region Constructors
public SayHelloWorldWithDefaultNameUsageJob(SayHelloWorldWithDefaultNameUsageJobConfiguration configuration)
{
diff --git a/GerstITS.Examples.Jobs.SayHelloWorld/Module.cs b/GerstITS.Examples.Jobs.SayHelloWorld/Module.cs
index f1a6a02..662f5d8 100644
--- a/GerstITS.Examples.Jobs.SayHelloWorld/Module.cs
+++ b/GerstITS.Examples.Jobs.SayHelloWorld/Module.cs
@@ -13,7 +13,8 @@ public sealed class Module : IIoCModule
public void RegisterComponents(IServiceCollection container)
{
container.RegisterJob();
- }
+ container.RegisterJob(IgnoreConditions.EntityFramework, IgnoreConditions.Swagger);
+ }
#endregion
}
\ No newline at end of file
diff --git a/GerstITS.Examples.Logic/Example/Mappings/IntegerMapping.cs b/GerstITS.Examples.Logic/Example/Mappings/IntegerMapping.cs
index 32badda..37ae917 100644
--- a/GerstITS.Examples.Logic/Example/Mappings/IntegerMapping.cs
+++ b/GerstITS.Examples.Logic/Example/Mappings/IntegerMapping.cs
@@ -4,7 +4,7 @@ namespace GerstITS.Examples.Logic.Example;
internal sealed class IntegerMapping : Profile
{
- #region Construtcors
+ #region Constructors
public IntegerMapping()
{
diff --git a/GerstITS.Examples.Logic/GerstITS.Examples.Logic.csproj b/GerstITS.Examples.Logic/GerstITS.Examples.Logic.csproj
index eee5ea9..456ceda 100644
--- a/GerstITS.Examples.Logic/GerstITS.Examples.Logic.csproj
+++ b/GerstITS.Examples.Logic/GerstITS.Examples.Logic.csproj
@@ -31,9 +31,19 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/GerstITS.Examples.Logic/Module.cs b/GerstITS.Examples.Logic/Module.cs
index 01ca2e8..7cfb9eb 100644
--- a/GerstITS.Examples.Logic/Module.cs
+++ b/GerstITS.Examples.Logic/Module.cs
@@ -9,9 +9,10 @@ public sealed partial class Module : IIoCModule
public void RegisterComponents(IServiceCollection container)
{
+ RegisterCustomers(container);
RegisterExample(container);
RegisterShared(container);
- }
+ }
#endregion
}
\ No newline at end of file
diff --git a/GerstITS.Examples.Logic/Shared/Extensions/IReadOnlyRepositoryExtensions.cs b/GerstITS.Examples.Logic/Shared/Extensions/IReadOnlyRepositoryExtensions.cs
index 1120736..8ca3774 100644
--- a/GerstITS.Examples.Logic/Shared/Extensions/IReadOnlyRepositoryExtensions.cs
+++ b/GerstITS.Examples.Logic/Shared/Extensions/IReadOnlyRepositoryExtensions.cs
@@ -4,7 +4,7 @@ using GerstITS.Data;
namespace GerstITS.Examples.Logic.Shared;
-internal static class ISearchEngineExtensions
+internal static class IReadOnlyRepositoryExtensions
{
#region Methods
diff --git a/GerstITS.Examples.WebClient.Console/GerstITS.Examples.WebClient.Console.csproj b/GerstITS.Examples.WebClient.Console/GerstITS.Examples.WebClient.Console.csproj
index 931d618..5ddf7db 100644
--- a/GerstITS.Examples.WebClient.Console/GerstITS.Examples.WebClient.Console.csproj
+++ b/GerstITS.Examples.WebClient.Console/GerstITS.Examples.WebClient.Console.csproj
@@ -41,9 +41,10 @@
+
+
-
diff --git a/GerstITS.Examples.WebClient.Console/Module.cs b/GerstITS.Examples.WebClient.Console/Module.cs
index d5a2d2e..ed5115f 100644
--- a/GerstITS.Examples.WebClient.Console/Module.cs
+++ b/GerstITS.Examples.WebClient.Console/Module.cs
@@ -12,6 +12,7 @@ public class Module : IIoCModule
public void RegisterComponents(IServiceCollection container)
{
+ container.AddTransient();
container.AddTransient();
container.AddTransient(c => new ConfigurationBuilder().SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
diff --git a/GerstITS.Examples.WebClient.Console/Tests/TestRunner.cs b/GerstITS.Examples.WebClient.Console/Tests/TestRunner.cs
index 71c31ba..373fc74 100644
--- a/GerstITS.Examples.WebClient.Console/Tests/TestRunner.cs
+++ b/GerstITS.Examples.WebClient.Console/Tests/TestRunner.cs
@@ -1,4 +1,4 @@
-using GerstITS.Examples.WebClients.Examples.Api;
+using GerstITS.Examples.WebClients.Examples.Api;
using GerstITS.IoC.DotNetCore;
using GerstITS.Web.WebClients;
@@ -12,37 +12,55 @@ internal sealed class TestRunner : IApplicationStart
#endregion
- #region Construtors
+ #region Constructors
public TestRunner(IWebClient webClient)
{
_webClient = webClient;
- }
-
+ }
+
#endregion
- #region ITestRunner
+ #region IApplicationStart
public void Run()
{
- var result1 = _webClient.Request()
- .Execute(c => c.Search(new SearchCriteria
- { SortBy = nameof(Country.Name), SortDirection = SortingDirections.Descending, Skip = 5, Take = 5}));
+ var created = _webClient.Request()
+ .Execute(c => c.Create(new Customer
+ { FirstName = "Erika", LastName = $"Mustermann-{Guid.NewGuid():N}", EMail = "erika.mustermann@example.com" }));
- var result2 = _webClient.Request()
- .Execute(c => c.Get(result1.Value.Result.First().Id));
+ var loaded = _webClient.Request()
+ .Execute(c => c.Get(created.Value.Id));
- var test3 = _webClient.Request()
- .Execute(c => c.Create(new Country {Name = $"Country-{Guid.NewGuid()}"}));
+ var searched = _webClient.Request()
+ .Execute(c => c.Search(new CustomerSearchCriteria
+ { Name = "Mustermann", SortBy = nameof(Customer.LastName), SortDirection = SortingDirections.Descending, Skip = 0, Take = 5 }));
- test3.Value.Name += "_Renamed";
+ loaded.Value.FirstName = "Max";
- var test4 = _webClient.Request()
- .Execute(c => c.Update(test3.Value));
+ var updated = _webClient.Request()
+ .Execute(c => c.Update(loaded.Value));
+
+ var note = _webClient.Request()
+ .Execute(c => c.AddNote(updated.Value.Id, new CustomerNote { Text = "Created by the example web client." }));
+
+ var notes = _webClient.Request()
+ .Execute(c => c.GetNotes(updated.Value.Id));
+
+ var deleted = _webClient.Request()
+ .Execute(c => c.Delete(updated.Value.Id));
+
+ Print($"---> {nameof(TestRunner)}: Found {searched.Value.TotalCount} customer(s), added {notes.Value.Count()} note(s), deleted: {deleted.Value}");
+ }
- var test5 = _webClient.Request()
- .Execute(c => c.Delete(test3.Value.Id));
- }
-
#endregion
-}
\ No newline at end of file
+
+ #region Methods
+
+ private static void Print(string message)
+ {
+ global::System.Console.WriteLine(message);
+ }
+
+ #endregion
+}
diff --git a/GerstITS.Examples.WebClient.Console/appsettings.json b/GerstITS.Examples.WebClient.Console/appsettings.json
index df91a93..9f49388 100644
--- a/GerstITS.Examples.WebClient.Console/appsettings.json
+++ b/GerstITS.Examples.WebClient.Console/appsettings.json
@@ -8,6 +8,6 @@
},
"AllowedHosts": "*",
"ExampleApiClient": {
- "BaseUrl": "https://localhost:44350/api/v1.1/"
+ "BaseUrl": "https://localhost:44350/api/"
}
}
diff --git a/GerstITS.Examples.sln b/GerstITS.Examples.sln
index 4e8c5f7..3514e18 100644
--- a/GerstITS.Examples.sln
+++ b/GerstITS.Examples.sln
@@ -19,32 +19,104 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
Directory.Packages.props = Directory.Packages.props
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GerstITS.Examples.Data", "GerstITS.Examples.Data\GerstITS.Examples.Data.csproj", "{BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GerstITS.Examples.Tests", "GerstITS.Examples.Tests\GerstITS.Examples.Tests.csproj", "{1F71C429-A9B3-46C2-BDEE-8F8413765EB5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Debug|x64.Build.0 = Debug|Any CPU
+ {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Debug|x86.Build.0 = Debug|Any CPU
{417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Release|x64.ActiveCfg = Release|Any CPU
+ {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Release|x64.Build.0 = Release|Any CPU
+ {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Release|x86.ActiveCfg = Release|Any CPU
+ {417B5C77-05BF-4562-9A1A-AEE0531B59C1}.Release|x86.Build.0 = Release|Any CPU
{A2E37A84-9331-48C7-8CCA-991528063087}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A2E37A84-9331-48C7-8CCA-991528063087}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A2E37A84-9331-48C7-8CCA-991528063087}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A2E37A84-9331-48C7-8CCA-991528063087}.Debug|x64.Build.0 = Debug|Any CPU
+ {A2E37A84-9331-48C7-8CCA-991528063087}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A2E37A84-9331-48C7-8CCA-991528063087}.Debug|x86.Build.0 = Debug|Any CPU
{A2E37A84-9331-48C7-8CCA-991528063087}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A2E37A84-9331-48C7-8CCA-991528063087}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A2E37A84-9331-48C7-8CCA-991528063087}.Release|x64.ActiveCfg = Release|Any CPU
+ {A2E37A84-9331-48C7-8CCA-991528063087}.Release|x64.Build.0 = Release|Any CPU
+ {A2E37A84-9331-48C7-8CCA-991528063087}.Release|x86.ActiveCfg = Release|Any CPU
+ {A2E37A84-9331-48C7-8CCA-991528063087}.Release|x86.Build.0 = Release|Any CPU
{4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Debug|x64.Build.0 = Debug|Any CPU
+ {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Debug|x86.Build.0 = Debug|Any CPU
{4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Release|x64.ActiveCfg = Release|Any CPU
+ {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Release|x64.Build.0 = Release|Any CPU
+ {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Release|x86.ActiveCfg = Release|Any CPU
+ {4A83DB6A-01BB-4B56-B38B-268444F22B1E}.Release|x86.Build.0 = Release|Any CPU
{A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Debug|x64.Build.0 = Debug|Any CPU
+ {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Debug|x86.Build.0 = Debug|Any CPU
{A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Release|x64.ActiveCfg = Release|Any CPU
+ {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Release|x64.Build.0 = Release|Any CPU
+ {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Release|x86.ActiveCfg = Release|Any CPU
+ {A7AD8A07-C9C0-480B-A96B-2D03ACD57060}.Release|x86.Build.0 = Release|Any CPU
{34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Debug|x64.Build.0 = Debug|Any CPU
+ {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Debug|x86.Build.0 = Debug|Any CPU
{34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Release|x64.ActiveCfg = Release|Any CPU
+ {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Release|x64.Build.0 = Release|Any CPU
+ {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Release|x86.ActiveCfg = Release|Any CPU
+ {34720C2D-09AD-41F4-B7CC-D703D9F24EDD}.Release|x86.Build.0 = Release|Any CPU
+ {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Debug|x64.Build.0 = Debug|Any CPU
+ {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Debug|x86.Build.0 = Debug|Any CPU
+ {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Release|x64.ActiveCfg = Release|Any CPU
+ {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Release|x64.Build.0 = Release|Any CPU
+ {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Release|x86.ActiveCfg = Release|Any CPU
+ {BE4FCB29-7B1E-4559-B6F0-F8F5A1BEBF44}.Release|x86.Build.0 = Release|Any CPU
+ {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Debug|x64.Build.0 = Debug|Any CPU
+ {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Debug|x86.Build.0 = Debug|Any CPU
+ {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Release|x64.ActiveCfg = Release|Any CPU
+ {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Release|x64.Build.0 = Release|Any CPU
+ {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Release|x86.ActiveCfg = Release|Any CPU
+ {1F71C429-A9B3-46C2-BDEE-8F8413765EB5}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/GerstITS.WebClients.Example.Api/Contracts/ICountry.cs b/GerstITS.WebClients.Example.Api/Contracts/ICountry.cs
deleted file mode 100644
index b10ce07..0000000
--- a/GerstITS.WebClients.Example.Api/Contracts/ICountry.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using GerstITS.Web.WebClients;
-
-namespace GerstITS.Examples.WebClients.Examples.Api;
-
-public interface ICountry : IWebService
-{
- [WebMethod(WebMethods.Get),
- ResourceUrl("Country/{id}")]
- Country Get(int id);
-
- [WebMethod(WebMethods.Get),
- ResourceUrl("Country/Search")]
- SearchResult Search(SearchCriteria criteria);
-
- [WebMethod(WebMethods.Post),
- ResourceUrl("Country")]
- Country Create(Country model);
-
- [WebMethod(WebMethods.Put),
- ResourceUrl("Country")]
- void Update(Country model);
-
- [WebMethod(WebMethods.Delete),
- ResourceUrl("Country/{id}")]
- void Delete(int id);
-}
\ No newline at end of file
diff --git a/GerstITS.WebClients.Example.Api/Contracts/Serialization/Country.cs b/GerstITS.WebClients.Example.Api/Contracts/Serialization/Country.cs
deleted file mode 100644
index dca3174..0000000
--- a/GerstITS.WebClients.Example.Api/Contracts/Serialization/Country.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace GerstITS.Examples.WebClients.Examples.Api;
-
-public class Country
-{
- #region Properties
-
- public int Id { get; set; }
- public string Name { get; set; }
-
- #endregion
-}
\ No newline at end of file
diff --git a/GerstITS.WebClients.Example.Api/Contracts/Serialization/SearchCriteria.cs b/GerstITS.WebClients.Example.Api/Contracts/Serialization/SearchCriteria.cs
deleted file mode 100644
index 912fb77..0000000
--- a/GerstITS.WebClients.Example.Api/Contracts/Serialization/SearchCriteria.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace GerstITS.Examples.WebClients.Examples.Api;
-
-public class SearchCriteria
-{
- #region Properties
-
- internal int Id { get; set; }
- public string Name { get; set; }
-
- public int? Skip { get; set; }
- public int? Take { get; set; }
-
- public string SortBy { get; set; }
- public SortingDirections SortDirection { get; set; }
-
- #endregion
-}
\ No newline at end of file
diff --git a/GerstITS.WebClients.Example.Api/CreationRules/ExampleApiClientCreatingRule.cs b/GerstITS.WebClients.Example.Api/CreationRules/ExampleApiClientCreatingRule.cs
index c4040ef..85c29ad 100644
--- a/GerstITS.WebClients.Example.Api/CreationRules/ExampleApiClientCreatingRule.cs
+++ b/GerstITS.WebClients.Example.Api/CreationRules/ExampleApiClientCreatingRule.cs
@@ -7,7 +7,7 @@ internal sealed class ExampleApiClientCreatingRule : RestApiClientCreationRuleBa
{
#region Properties
- protected override Type[] SupportedTypes => new[] { typeof(ICountry) };
+ protected override Type[] SupportedTypes => new[] { typeof(ICustomer) };
#endregion
diff --git a/README.md b/README.md
index e69de29..5340c88 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,138 @@
+# GerstITS Framework Examples
+
+This solution demonstrates all features of the GerstITS framework packages in a small, coherent customer management domain.
+
+## Solution layout
+
+| Project | Purpose |
+|---------|---------|
+| `GerstITS.Examples.Api` | ASP.NET Core Web API host: fluent controllers, versioning, exception handling, Swagger, authentication, session cache, jobs and auto migration. |
+| `GerstITS.Examples.Logic` | Business logic: providers, search engine integration, validation rules, customizations and AutoMapper profiles. |
+| `GerstITS.Examples.Data` | Data access: entities with framework aspects, EF Core DbContext, entity mappings, database configurator and migrations. |
+| `GerstITS.Examples.Jobs.SayHelloWorld` | Scheduled Quartz jobs with cron configuration. |
+| `GerstITS.WebClients.Example.Api` | Typed REST client contract for the example API (`ICustomer`). |
+| `GerstITS.Examples.WebClient.Console` | Console client consuming the API via `IWebClient` plus demos of the common/system utilities. |
+| `GerstITS.Examples.Tests` | xUnit tests using the GerstITS test framework categories. |
+
+## Feature matrix
+
+### GerstITS.Common
+| Feature | Example |
+|---------|---------|
+| String/object/enum/enumerable extensions | `WebClient.Console/Tests/CommonFeaturesRunner.cs` |
+| Fluent exception handling (`Is(...).ThenThrows<...>().WithMessage(...)`) | `CommonFeaturesRunner.cs`, `Logic/Customers/CustomerProvider.cs` (`Import`) |
+| `Range` + `Intersects` | `CommonFeaturesRunner.cs` |
+| AES cryptography extensions (`Encrypt`/`Decrypt`) | `CommonFeaturesRunner.cs` |
+| `ApplicationEnvironment` detection | `Api/Program.cs` (`IsEfCoreActionStarted`) |
+
+### GerstITS.System
+| Feature | Example |
+|---------|---------|
+| `ISystemClock` | `MaintenanceController.Ping`, `SayHelloWorldJob` |
+| `IJsonConvert` | `Api/Common/StartupTasks/ExampleStartupTask.cs`, `CommonFeaturesRunner.cs` |
+| `IActivator` | `CommonFeaturesRunner.cs` |
+| `ICache` (encrypted, session backed) | `Api/Controllers/1.1/CacheController.cs` |
+| Customizations (`CustomizationBase`, `ApplyCustomizations()`) | `Logic/Customers/Customizations/CustomerDisplayNameCustomization.cs` |
+| Configuration prefix pattern (`ToConfigurationPrefix`) | `Api/Common/Configurations/WebApiConfiguration.*.cs` |
+
+### GerstITS.IoC / GerstITS.IoC.DotNetCore
+| Feature | Example |
+|---------|---------|
+| `IIoCModule` modules (partial pattern) | `Module.cs` in every project |
+| `DotNetCoreApplicationBootstrapper` + `IApplicationStart` | `WebClient.Console/Program.cs`, `Tests/TestRunner.cs`, `Tests/CommonFeaturesRunner.cs` |
+| `IStartupTask` with priorities | `Api/Common/StartupTasks/ExampleStartupTask.cs` |
+| Namespace based assembly scanning (`Register...Of()`) | `Logic/Module.Customers.cs` |
+
+### GerstITS.Data / GerstITS.Data.EntityFramework / GerstITS.Data.EntityFramework.PostgreSql
+| Feature | Example |
+|---------|---------|
+| `EntityBase` + aspects (`IActivatable`, `ICreatable`, `IModifiable`, `IVersionable`) | `Data/Entities/CustomerEntity.cs` |
+| Historization (`IHistoricizable`) | `Data/Entities/CustomerNoteEntity.cs` |
+| `EntityMappingBase` | `Data/Mappings/*.cs` |
+| `EntityFrameworkDbContextBase` + `IDbContext` | `Data/CustomerDbContext.cs` |
+| `PostgreSqlDatabaseConfiguratorBase` | `Data/Migrations/CustomerDatabaseConfigurator.cs` |
+| Design time factory + migrations | `Data/Migrations/DesignTime/CustomerDbContextDesignTimeFactory.cs`, `Data/Migrations/20260703*_InitialCreate.cs` |
+| Registration (`AddMigration`, `AddEntityFrameworkSession`) | `Data/Module.cs` |
+| Repository queries (active only by default, `includeInactive` opt-in) | `Logic/Customers/CustomerProvider.cs` (`GetById`) |
+| Soft delete by default, `forceDelete` opt-in | `CustomerProvider.Delete` |
+| Bulk operations (`BulkSave`) | `CustomerProvider.Import` |
+| Query projection (`Project`) | `Logic/Customers/Search/CustomerSearchQueryBuilderRule.cs` |
+| `ThrowsAnExceptionIfNotExisting` / `EntityNotFoundException` | `CustomerProvider.cs`, `Api/.../EntityNotFoundExceptionTransformation.cs` |
+| Auto migration on startup (`WebApi:EntityFrameworkMigration:AutoMigrate`) | `Api/appsettings.json` |
+
+> `GerstITS.Data.EntityFramework.SqlServer` works exactly like the PostgreSql package: derive from
+> `SqlServerDatabaseConfiguratorBase` instead. Note that only one `IDatabaseConfigurator`
+> implementation is allowed per assembly.
+
+### GerstITS.Search
+| Feature | Example |
+|---------|---------|
+| `ISearchEngine` + `SearchResult` | `Logic/Customers/CustomerProvider.cs` (`Search`) |
+| `SearchFilterBase` (paging + sorting) | `Logic/Customers/Contracts/CustomerSearchFilter.cs` |
+| `SearchQueryBuilderRuleBase` | `Logic/Customers/Search/CustomerSearchQueryBuilderRule.cs` |
+| `InitializationRuleBase` (filter defaults) | `Logic/Customers/Search/CustomerSearchFilterInitializationRule.cs` |
+| `PagingValidationRuleBase` | `Logic/Customers/Validation/CustomerSearchFilterValidationRule.cs` |
+| Custom `ISortingProvider` | `Logic/Shared/Search/Sorting/SortingProvider.cs` |
+
+### GerstITS.Validation
+| Feature | Example |
+|---------|---------|
+| `ValidationRuleBase` (FluentValidation) | `Logic/Customers/Validation/CustomerValidationRule.cs`, `Logic/Shared/Validation/IdValidationRule.cs` |
+| `IsEMail()` rule builder extension | `CustomerValidationRule.cs` |
+| Automatic validation in fluent controllers (`IValidator`) | All controllers via `FluentApiControllerBase` |
+
+### GerstITS.Mapping.AutoMapper
+| Feature | Example |
+|---------|---------|
+| Profiles + `RegisterMappingsOf()` | `Logic/Customers/Mappings/CustomerMapping.cs`, `Logic/Example/Mappings/IntegerMapping.cs` |
+
+### GerstITS.Web / GerstITS.Web.Api / GerstITS.Web.Api.Swagger / GerstITS.Web.Rest
+| Feature | Example |
+|---------|---------|
+| `HostingStartup` fluent pipeline | `Api/Program.cs` |
+| `FluentApiControllerBase` (`Api().Use(...).Get/Create/Update/Delete`) | `Api/Controllers/1.1/*.cs` |
+| API versioning (side by side controllers) | `Api/Controllers/1.0` vs `Api/Controllers/1.1` |
+| Global exception transformations | `Api/Common/ExceptionHandling/Transformations/*.cs` |
+| Server configuration (CORS, HSTS, forwarded headers) | `Api/Common/Configurations/WebApiConfiguration.Server.*.cs` |
+| Swagger configuration incl. security schemes | `Api/Common/Configurations/WebApiConfiguration.Swagger*.cs`, `WebApiConfiguration.OpenApiSecurityScheme.cs` |
+| Session based cache storage (`AddHttpSession`) | `Api/Module.Session.cs`, `CacheController.cs` |
+| Typed REST clients (`IWebService`, `WebMethod`, `ResourceUrl`, `HttpHeader`) | `GerstITS.WebClients.Example.Api/Contracts/ICustomer.cs` |
+| `RestWebServiceConfigurationBase` + creation rules | `GerstITS.WebClients.Example.Api/Configurations`, `CreationRules` |
+| `IWebClient.Request().Execute(...)` | `WebClient.Console/Tests/TestRunner.cs` |
+
+### GerstITS.Authentication.ApiKey / GerstITS.Authentication.OpenId
+| Feature | Example |
+|---------|---------|
+| `[ApiKey]` attribute + `IApiKeyConfiguration` | `Api/Controllers/1.1/MaintenanceController.cs`, `WebApiConfiguration.ApiKey.cs` |
+| JWT bearer via `IOpenIdConfiguration` + `[Authorize]` | `Api/Controllers/1.1/SecuredController.cs`, `WebApiConfiguration.OpenId*.cs` |
+
+### GerstITS.Mail
+| Feature | Example |
+|---------|---------|
+| `IMailProvider`, `MailNotification`, `ContactRequest` | `Api/Controllers/1.1/ContactController.cs` |
+| SMTP configuration (`Mail` section) | `Api/appsettings.json` |
+
+### GerstITS.Job / GerstITS.Job.Scheduling
+| Feature | Example |
+|---------|---------|
+| `JobBase` + cron scheduling | `Jobs.SayHelloWorld/Jobs/*.cs` |
+| `JobSchedulingConfigurationBase` (named + convention based) | `Jobs.SayHelloWorld/Configurations/*.cs`, `Api/appsettings.json` (`Jobs` section) |
+| `RegisterJob()` + `IgnoreConditions` | `Jobs.SayHelloWorld/Module.cs` |
+
+### GerstITS.TestFramework
+| Feature | Example |
+|---------|---------|
+| `[TestCategory(TestCategories.Unit/Integration)]` traits | `GerstITS.Examples.Tests/**/*Test.cs` |
+| Filtering: `dotnet test --filter "Category=Unit"` | see below |
+
+## Running
+
+1. **Database**: the API auto-migrates on startup (`AutoMigrate: true`). Adjust the `CustomerDbContext` connection string in `GerstITS.Examples.Api/appsettings.json` (PostgreSQL).
+2. **API**: start `GerstITS.Examples.Api`. Swagger UI is available in development at `/api/swagger`.
+3. **Console client**: start `GerstITS.Examples.WebClient.Console` while the API is running.
+4. **Tests**: `dotnet test` (optionally `--filter "Category=Unit"`).
+5. **Migrations**: `dotnet ef migrations add ` inside `GerstITS.Examples.Data` (uses the design time factory and `appsettings.json` of that project).
+
+### Api key protected endpoints
+
+`GET /api/Maintenance/Ping` requires the header configured under `WebApi:ApiKey` (default: `X-Api-Key: example-api-key`).