cqrs.
This commit is contained in:
parent
b9fbe5aca1
commit
20df5ce79d
33
Demo/Commands/CreatePersonCommand.cs
Normal file
33
Demo/Commands/CreatePersonCommand.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using PoweredSoft.CQRS.Abstractions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Demo.Commands
|
||||||
|
{
|
||||||
|
public class CreatePersonCommand
|
||||||
|
{
|
||||||
|
public string FirstName { get; set; }
|
||||||
|
public string LastName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CreatePersonCommandValidator : AbstractValidator<CreatePersonCommand>
|
||||||
|
{
|
||||||
|
public CreatePersonCommandValidator()
|
||||||
|
{
|
||||||
|
RuleFor(t => t.FirstName).NotEmpty();
|
||||||
|
RuleFor(t => t.LastName).NotEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CreatePersonCommandHandler : ICommandHandler<CreatePersonCommand>
|
||||||
|
{
|
||||||
|
public Task HandleAsync(CreatePersonCommand command, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
Demo/Commands/EchoCommand.cs
Normal file
31
Demo/Commands/EchoCommand.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using PoweredSoft.CQRS.Abstractions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Demo.Commands
|
||||||
|
{
|
||||||
|
public class EchoCommand
|
||||||
|
{
|
||||||
|
public string Message { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EchoCommandValidator : AbstractValidator<EchoCommand>
|
||||||
|
{
|
||||||
|
public EchoCommandValidator()
|
||||||
|
{
|
||||||
|
RuleFor(t => t.Message).NotEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EchoCommandHandler : ICommandHandler<EchoCommand, string>
|
||||||
|
{
|
||||||
|
public Task<string> HandleAsync(EchoCommand command, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return Task.FromResult(command.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="FluentValidation.AspNetCore" Version="9.5.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="5.6.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="5.6.3" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.6.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.6.3" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.6.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.6.3" />
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
using Demo.Commands;
|
||||||
using Demo.Queries;
|
using Demo.Queries;
|
||||||
|
using FluentValidation;
|
||||||
|
using FluentValidation.AspNetCore;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.HttpsPolicy;
|
using Microsoft.AspNetCore.HttpsPolicy;
|
||||||
@ -30,15 +33,27 @@ namespace Demo
|
|||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
AddQueries(services);
|
AddQueries(services);
|
||||||
|
AddCommands(services);
|
||||||
|
|
||||||
services.AddPoweredSoftCQRS();
|
services.AddPoweredSoftCQRS();
|
||||||
services
|
services
|
||||||
.AddControllers()
|
.AddControllers()
|
||||||
.AddPoweredSoftQueryController();
|
.AddPoweredSoftQueryController()
|
||||||
|
.AddPoweredSoftCommandController()
|
||||||
|
.AddFluentValidation();
|
||||||
|
|
||||||
services.AddSwaggerGen();
|
services.AddSwaggerGen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddCommands(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddCommand<CreatePersonCommand, CreatePersonCommandHandler>();
|
||||||
|
services.AddTransient<IValidator<CreatePersonCommand>, CreatePersonCommandValidator>();
|
||||||
|
|
||||||
|
services.AddCommand<EchoCommand, string, EchoCommandHandler>();
|
||||||
|
services.AddTransient<IValidator<EchoCommand>, EchoCommandValidator>();
|
||||||
|
}
|
||||||
|
|
||||||
private void AddQueries(IServiceCollection services)
|
private void AddQueries(IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddQuery<PersonQuery, IQueryable<Person>, PersonQueryHandler>();
|
services.AddQuery<PersonQuery, IQueryable<Person>, PersonQueryHandler>();
|
||||||
|
@ -4,7 +4,7 @@ using System.Text;
|
|||||||
|
|
||||||
namespace PoweredSoft.CQRS.Abstractions.Attributes
|
namespace PoweredSoft.CQRS.Abstractions.Attributes
|
||||||
{
|
{
|
||||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||||
public class CommandNameAttribute : Attribute
|
public class CommandNameAttribute : Attribute
|
||||||
{
|
{
|
||||||
public CommandNameAttribute(string name)
|
public CommandNameAttribute(string name)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace PoweredSoft.CQRS.Abstractions.Attributes
|
namespace PoweredSoft.CQRS.Abstractions.Attributes
|
||||||
{
|
{
|
||||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||||
public class QueryNameAttribute : Attribute
|
public class QueryNameAttribute : Attribute
|
||||||
{
|
{
|
||||||
public QueryNameAttribute(string name)
|
public QueryNameAttribute(string name)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Abstractions.Attributes
|
namespace PoweredSoft.CQRS.AspNetCore.Abstractions.Attributes
|
||||||
{
|
{
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||||
public class CommandControllerIgnoreAttribute : Attribute
|
public class CommandControllerIgnoreAttribute : Attribute
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ using System.Text;
|
|||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Abstractions.Attributes
|
namespace PoweredSoft.CQRS.AspNetCore.Abstractions.Attributes
|
||||||
{
|
{
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||||
public class QueryControllerIgnoreAttribute : Attribute
|
public class QueryControllerIgnoreAttribute : Attribute
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
40
PoweredSoft.CQRS.AspNetCore/Mvc/CommandController.cs
Normal file
40
PoweredSoft.CQRS.AspNetCore/Mvc/CommandController.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using PoweredSoft.CQRS.Abstractions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
||||||
|
{
|
||||||
|
[ApiController, Route("api/command/[controller]")]
|
||||||
|
public class CommandController<TCommand> : Controller
|
||||||
|
where TCommand : class
|
||||||
|
{
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Handle([FromServices] ICommandHandler<TCommand> handler,
|
||||||
|
[FromBody] TCommand command)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
|
||||||
|
await handler.HandleAsync(command, this.Request.HttpContext.RequestAborted);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ApiController, Route("api/command/[controller]")]
|
||||||
|
public class CommandController<TCommand, TTCommandResult> : Controller
|
||||||
|
where TCommand : class
|
||||||
|
{
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<TTCommandResult>> Handle([FromServices] ICommandHandler<TCommand, TTCommandResult> handler,
|
||||||
|
[FromBody] TCommand command)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
|
||||||
|
return Ok(await handler.HandleAsync(command, this.Request.HttpContext.RequestAborted));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using PoweredSoft.CQRS.Abstractions.Discovery;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
||||||
|
{
|
||||||
|
public class QueryControllerConvention : IControllerModelConvention
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider serviceProvider;
|
||||||
|
|
||||||
|
public QueryControllerConvention(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
this.serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Apply(ControllerModel controller)
|
||||||
|
{
|
||||||
|
if (controller.ControllerType.IsGenericType && controller.ControllerType.Name.Contains("QueryController") && controller.ControllerType.Assembly == typeof(QueryControllerConvention).Assembly)
|
||||||
|
{
|
||||||
|
var genericType = controller.ControllerType.GenericTypeArguments[0];
|
||||||
|
var queryDiscovery = this.serviceProvider.GetRequiredService<IQueryDiscovery>();
|
||||||
|
var query = queryDiscovery.FindQuery(genericType);
|
||||||
|
controller.ControllerName = query.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using PoweredSoft.CQRS.Abstractions.Discovery;
|
||||||
|
using PoweredSoft.CQRS.AspNetCore.Abstractions.Attributes;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
||||||
|
{
|
||||||
|
public class CommandControllerFeatureProvider : IApplicationFeatureProvider<ControllerFeature>
|
||||||
|
{
|
||||||
|
private readonly ServiceProvider serviceProvider;
|
||||||
|
|
||||||
|
public CommandControllerFeatureProvider(ServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
this.serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature)
|
||||||
|
{
|
||||||
|
var commandDiscovery = this.serviceProvider.GetRequiredService<ICommandDiscovery>();
|
||||||
|
foreach (var f in commandDiscovery.GetCommands())
|
||||||
|
{
|
||||||
|
var ignoreAttribute = f.CommandType.GetCustomAttribute<CommandControllerIgnoreAttribute>();
|
||||||
|
if (ignoreAttribute != null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (f.CommandResultType == null)
|
||||||
|
{
|
||||||
|
var controllerType = typeof(CommandController<>).MakeGenericType(f.CommandType);
|
||||||
|
var controllerTypeInfo = controllerType.GetTypeInfo();
|
||||||
|
feature.Controllers.Add(controllerTypeInfo);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var controllerType = typeof(CommandController<,>).MakeGenericType(f.CommandType, f.CommandResultType);
|
||||||
|
var controllerTypeInfo = controllerType.GetTypeInfo();
|
||||||
|
feature.Controllers.Add(controllerTypeInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
||||||
|
{
|
||||||
|
public class CommandControllerOptions
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -12,10 +12,14 @@ namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|||||||
where TQuery : class
|
where TQuery : class
|
||||||
{
|
{
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public Task<TQueryResult> Handle([FromServices] IQueryHandler<TQuery, TQueryResult> handler,
|
public async Task<ActionResult<TQueryResult>> Handle([FromServices] IQueryHandler<TQuery, TQueryResult> handler,
|
||||||
[FromBody] TQuery query)
|
[FromBody] TQuery query)
|
||||||
{
|
{
|
||||||
return handler.HandleAsync(query, this.Request.HttpContext.RequestAborted);
|
if (!ModelState.IsValid)
|
||||||
|
return BadRequest(ModelState);
|
||||||
|
|
||||||
|
|
||||||
|
return Ok(await handler.HandleAsync(query, this.Request.HttpContext.RequestAborted));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,23 +5,23 @@ using System;
|
|||||||
|
|
||||||
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
||||||
{
|
{
|
||||||
public class QueryControllerConvention : IControllerModelConvention
|
public class CommandControllerConvention : IControllerModelConvention
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
|
|
||||||
public QueryControllerConvention(IServiceProvider serviceProvider)
|
public CommandControllerConvention(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
this.serviceProvider = serviceProvider;
|
this.serviceProvider = serviceProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Apply(ControllerModel controller)
|
public void Apply(ControllerModel controller)
|
||||||
{
|
{
|
||||||
if (controller.ControllerType.IsGenericType && controller.ControllerType.Name.Contains("QueryController") && controller.ControllerType.Assembly == typeof(QueryControllerConvention).Assembly)
|
if (controller.ControllerType.IsGenericType && controller.ControllerType.Name.Contains("CommandController") && controller.ControllerType.Assembly == typeof(CommandControllerConvention).Assembly)
|
||||||
{
|
{
|
||||||
var genericType = controller.ControllerType.GenericTypeArguments[0];
|
var genericType = controller.ControllerType.GenericTypeArguments[0];
|
||||||
var queryDiscovery = this.serviceProvider.GetRequiredService<IQueryDiscovery>();
|
var commandDiscovery = this.serviceProvider.GetRequiredService<ICommandDiscovery>();
|
||||||
var query = queryDiscovery.FindQuery(genericType);
|
var command = commandDiscovery.FindCommand(genericType);
|
||||||
controller.ControllerName = query.Name;
|
controller.ControllerName = command.Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
public static IMvcBuilder AddPoweredSoftCommandController(this IMvcBuilder builder)
|
public static IMvcBuilder AddPoweredSoftCommandController(this IMvcBuilder builder)
|
||||||
{
|
{
|
||||||
var services = builder.Services;
|
var services = builder.Services;
|
||||||
@ -25,6 +24,6 @@ namespace PoweredSoft.CQRS.AspNetCore.Mvc
|
|||||||
builder.AddMvcOptions(o => o.Conventions.Add(new CommandControllerConvention(serviceProvider)));
|
builder.AddMvcOptions(o => o.Conventions.Add(new CommandControllerConvention(serviceProvider)));
|
||||||
builder.ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new CommandControllerFeatureProvider(serviceProvider)));
|
builder.ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new CommandControllerFeatureProvider(serviceProvider)));
|
||||||
return builder;
|
return builder;
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\PoweredSoft.CQRS.Abstractions\PoweredSoft.CQRS.Abstractions.csproj" />
|
<ProjectReference Include="..\PoweredSoft.CQRS.Abstractions\PoweredSoft.CQRS.Abstractions.csproj" />
|
||||||
|
<ProjectReference Include="..\PoweredSoft.CQRS.AspNetCore.Abstractions\PoweredSoft.CQRS.AspNetCore.Abstractions.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
Loading…
Reference in New Issue
Block a user