CommandQuery.AspNetCore
2.0.0
See the version list below for details.
dotnet add package CommandQuery.AspNetCore --version 2.0.0
NuGet\Install-Package CommandQuery.AspNetCore -Version 2.0.0
<PackageReference Include="CommandQuery.AspNetCore" Version="2.0.0" />
paket add CommandQuery.AspNetCore --version 2.0.0
#r "nuget: CommandQuery.AspNetCore, 2.0.0"
// Install CommandQuery.AspNetCore as a Cake Addin #addin nuget:?package=CommandQuery.AspNetCore&version=2.0.0 // Install CommandQuery.AspNetCore as a Cake Tool #tool nuget:?package=CommandQuery.AspNetCore&version=2.0.0
CommandQuery.AspNetCore 🌐
Command Query Separation for ASP.NET Core
- Provides generic actions for handling the execution of commands and queries
- Enables APIs based on HTTP
POST
andGET
Get Started
- Create a new ASP.NET Core project
- Install the
CommandQuery.AspNetCore
package from NuGetPM>
Install-Package CommandQuery.AspNetCore
- Create commands and command handlers
- Implement
ICommand
andICommandHandler<in TCommand>
- Or
ICommand<TResult>
andICommandHandler<in TCommand, TResult>
- Implement
- Create queries and query handlers
- Implement
IQuery<TResult>
andIQueryHandler<in TQuery, TResult>
- Implement
- Configure services in
Startup.cs
Configuration
Configuration in Startup.cs
:
using CommandQuery.AspNetCore;
using CommandQuery.Sample.Contracts.Commands;
using CommandQuery.Sample.Contracts.Queries;
using CommandQuery.Sample.Handlers;
using CommandQuery.Sample.Handlers.Commands;
using CommandQuery.Sample.Handlers.Queries;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
namespace CommandQuery.Sample.AspNetCore.V5
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add commands and queries
services.AddCommandControllers(typeof(FooCommandHandler).Assembly, typeof(FooCommand).Assembly);
services.AddQueryControllers(typeof(BarQueryHandler).Assembly, typeof(BarQuery).Assembly);
// Add handler dependencies
services.AddTransient<IDateTimeProxy, DateTimeProxy>();
services.AddTransient<ICultureService, CultureService>();
// Swagger
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v5", new OpenApiInfo { Title = "CommandQuery.Sample.AspNetCore.V5", Version = "v5" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
// Swagger
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v5/swagger.json", "CommandQuery.Sample.AspNetCore.V5"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
// Validation
app.ApplicationServices.GetService<ICommandProcessor>().AssertConfigurationIsValid();
app.ApplicationServices.GetService<IQueryProcessor>().AssertConfigurationIsValid();
}
}
}
The extension methods AddCommandControllers
and AddQueryControllers
will add controllers and all command/query handlers in the given assemblies to the IoC container.
You can pass in a params
array of Assembly
arguments if your handlers are located in different projects.
If you only have one project you can use typeof(Startup).Assembly
as a single argument.
Commands
The action method from the generated controller will handle commands:
/// <summary>
/// Handle a command.
/// </summary>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>The result for status code <c>200</c>, or an error for status code <c>400</c> and <c>500</c>.</returns>
[HttpPost]
public async Task<IActionResult> HandleAsync(TCommand command, CancellationToken cancellationToken)
- The action is requested via HTTP
POST
with the Content-Typeapplication/json
in the header - The name of the command is the slug of the URL
- The command itself is provided as JSON in the body
- If the command succeeds; the response is empty with the HTTP status code
200
- If the command fails; the response is an error message with the HTTP status code
400
or500
Commands with result:
/// <summary>
/// Handle a command.
/// </summary>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>The result for status code <c>200</c>, or an error for status code <c>400</c> and <c>500</c>.</returns>
[HttpPost]
public async Task<IActionResult> HandleAsync(TCommand command, CancellationToken cancellationToken)
- If the command succeeds; the response is the result as JSON with the HTTP status code
200
. - If the command fails; the response is an error message with the HTTP status code
400
or500
.
Queries
The action methods from the generated controller will handle queries:
/// <summary>
/// Handle a query.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>The result + 200, 400 or 500.</returns>
[HttpPost]
public async Task<IActionResult> HandlePostAsync(TQuery query, CancellationToken cancellationToken)
/// <summary>
/// Handle a query.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>The result + 200, 400 or 500.</returns>
[HttpGet]
public async Task<IActionResult> HandleGetAsync([FromQuery] TQuery query, CancellationToken cancellationToken)
- The action is requested via:
- HTTP
POST
with the Content-Typeapplication/json
in the header and the query itself as JSON in the body - HTTP
GET
and the query itself as query string parameters in the URL
- HTTP
- The name of the query is the slug of the URL.
- If the query succeeds; the response is the result as JSON with the HTTP status code
200
. - If the query fails; the response is an error message with the HTTP status code
400
or500
.
Testing
You can integration test your controllers and command/query handlers with the Microsoft.AspNetCore.Mvc.Testing
package.
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using CommandQuery.Sample.Contracts.Queries;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using NUnit.Framework;
namespace CommandQuery.Sample.AspNetCore.V5.Tests
{
public class QueryControllerTests
{
public class when_using_the_real_controller_via_Post
{
[SetUp]
public void SetUp()
{
Factory = new WebApplicationFactory<Startup>();
Client = Factory.CreateClient();
}
[TearDown]
public void TearDown()
{
Client.Dispose();
Factory.Dispose();
}
[Test]
public async Task should_work()
{
var content = new StringContent("{ \"Id\": 1 }", Encoding.UTF8, "application/json");
var result = await Client.PostAsync("/api/query/BarQuery", content);
var value = await result.Content.ReadAsAsync<Bar>();
result.EnsureSuccessStatusCode();
value.Id.Should().Be(1);
value.Value.Should().NotBeEmpty();
}
[Test]
public async Task should_handle_errors()
{
var content = new StringContent("{ \"Id\": 1 }", Encoding.UTF8, "application/json");
var result = await Client.PostAsync("/api/query/FailQuery", content);
result.StatusCode.Should().Be(HttpStatusCode.NotFound);
(await result.Content.ReadAsStringAsync()).Should().BeEmpty();
}
WebApplicationFactory<Startup> Factory;
HttpClient Client;
}
public class when_using_the_real_controller_via_Get
{
[SetUp]
public void SetUp()
{
Factory = new WebApplicationFactory<Startup>();
Client = Factory.CreateClient();
}
[TearDown]
public void TearDown()
{
Client.Dispose();
Factory.Dispose();
}
[Test]
public async Task should_work()
{
var result = await Client.GetAsync("/api/query/BarQuery?Id=1");
var value = await result.Content.ReadAsAsync<Bar>();
result.EnsureSuccessStatusCode();
value.Id.Should().Be(1);
value.Value.Should().NotBeEmpty();
}
[Test]
public async Task should_handle_errors()
{
var result = await Client.GetAsync("/api/query/FailQuery?Id=1");
result.StatusCode.Should().Be(HttpStatusCode.NotFound);
(await result.Content.ReadAsStringAsync()).Should().BeEmpty();
}
WebApplicationFactory<Startup> Factory;
HttpClient Client;
}
}
}
Samples
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 is compatible. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETCoreApp 3.1
- CommandQuery (>= 2.0.0)
- Microsoft.AspNetCore.Mvc.Core (>= 2.2.5)
-
.NETStandard 2.0
- CommandQuery (>= 2.0.0)
- Microsoft.AspNetCore.Mvc.Core (>= 2.2.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories (1)
Showing the top 1 popular GitHub repositories that depend on CommandQuery.AspNetCore:
Repository | Stars |
---|---|
hlaueriksson/CommandQuery
Command Query Separation for 🌐ASP.NET Core ⚡AWS Lambda ⚡Azure Functions ⚡Google Cloud Functions
|
Version | Downloads | Last updated |
---|---|---|
4.0.0 | 130 | 7/13/2024 |
3.0.0 | 519 | 1/9/2023 |
2.0.0 | 748 | 7/29/2021 |
1.0.0 | 2,185 | 2/2/2020 |
0.9.0 | 1,511 | 11/20/2019 |
0.8.0 | 4,161 | 2/16/2019 |
0.7.0 | 1,017 | 9/22/2018 |
0.6.0 | 957 | 9/15/2018 |
0.5.0 | 1,084 | 7/6/2018 |
0.4.0 | 1,027 | 5/16/2018 |
0.3.2 | 1,133 | 5/1/2018 |
0.3.1 | 1,125 | 1/6/2018 |
0.3.0 | 1,120 | 1/3/2018 |
0.2.0 | 1,073 | 4/25/2017 |
- Changed the target frameworks to netstandard2.0 and netcoreapp3.1 🎯
- Added extension methods AddCommandControllers and AddQueryControllers on IServiceCollection