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>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="FluentValidation.AspNetCore" Version="9.5.0" />
 | 
			
		||||
    <PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="5.6.3" />
 | 
			
		||||
    <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.6.3" />
 | 
			
		||||
    <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.6.3" />
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,7 @@
 | 
			
		||||
using Demo.Commands;
 | 
			
		||||
using Demo.Queries;
 | 
			
		||||
using FluentValidation;
 | 
			
		||||
using FluentValidation.AspNetCore;
 | 
			
		||||
using Microsoft.AspNetCore.Builder;
 | 
			
		||||
using Microsoft.AspNetCore.Hosting;
 | 
			
		||||
using Microsoft.AspNetCore.HttpsPolicy;
 | 
			
		||||
@ -30,15 +33,27 @@ namespace Demo
 | 
			
		||||
        public void ConfigureServices(IServiceCollection services)
 | 
			
		||||
        {
 | 
			
		||||
            AddQueries(services);
 | 
			
		||||
            AddCommands(services);
 | 
			
		||||
 | 
			
		||||
            services.AddPoweredSoftCQRS();
 | 
			
		||||
            services
 | 
			
		||||
                .AddControllers()
 | 
			
		||||
                .AddPoweredSoftQueryController();
 | 
			
		||||
                .AddPoweredSoftQueryController()
 | 
			
		||||
                .AddPoweredSoftCommandController()
 | 
			
		||||
                .AddFluentValidation();
 | 
			
		||||
 | 
			
		||||
            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)
 | 
			
		||||
        {
 | 
			
		||||
            services.AddQuery<PersonQuery, IQueryable<Person>, PersonQueryHandler>();
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace PoweredSoft.CQRS.Abstractions.Attributes
 | 
			
		||||
{
 | 
			
		||||
    [AttributeUsage(AttributeTargets.Class, Inherited = false)]
 | 
			
		||||
    [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
 | 
			
		||||
    public class CommandNameAttribute : Attribute
 | 
			
		||||
    {
 | 
			
		||||
        public CommandNameAttribute(string name)
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
namespace PoweredSoft.CQRS.Abstractions.Attributes
 | 
			
		||||
{
 | 
			
		||||
    [AttributeUsage(AttributeTargets.Class, Inherited = false)]
 | 
			
		||||
    [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
 | 
			
		||||
    public class QueryNameAttribute : Attribute
 | 
			
		||||
    {
 | 
			
		||||
        public QueryNameAttribute(string name)
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
namespace PoweredSoft.CQRS.AspNetCore.Abstractions.Attributes
 | 
			
		||||
{
 | 
			
		||||
    [AttributeUsage(AttributeTargets.Class)]
 | 
			
		||||
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
 | 
			
		||||
    public class CommandControllerIgnoreAttribute : Attribute
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace PoweredSoft.CQRS.AspNetCore.Abstractions.Attributes
 | 
			
		||||
{
 | 
			
		||||
    [AttributeUsage(AttributeTargets.Class)]
 | 
			
		||||
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
 | 
			
		||||
    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
 | 
			
		||||
    {
 | 
			
		||||
        [HttpPost]
 | 
			
		||||
        public Task<TQueryResult> Handle([FromServices] IQueryHandler<TQuery, TQueryResult> handler, 
 | 
			
		||||
        public async Task<ActionResult<TQueryResult>> Handle([FromServices] IQueryHandler<TQuery, TQueryResult> handler, 
 | 
			
		||||
            [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
 | 
			
		||||
{
 | 
			
		||||
    public class QueryControllerConvention : IControllerModelConvention
 | 
			
		||||
    public class CommandControllerConvention : IControllerModelConvention
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IServiceProvider serviceProvider;
 | 
			
		||||
 | 
			
		||||
        public QueryControllerConvention(IServiceProvider serviceProvider)
 | 
			
		||||
        public CommandControllerConvention(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)
 | 
			
		||||
            if (controller.ControllerType.IsGenericType && controller.ControllerType.Name.Contains("CommandController") && controller.ControllerType.Assembly == typeof(CommandControllerConvention).Assembly)
 | 
			
		||||
            {
 | 
			
		||||
                var genericType = controller.ControllerType.GenericTypeArguments[0];
 | 
			
		||||
                var queryDiscovery = this.serviceProvider.GetRequiredService<IQueryDiscovery>();
 | 
			
		||||
                var query = queryDiscovery.FindQuery(genericType);
 | 
			
		||||
                controller.ControllerName = query.Name;
 | 
			
		||||
                var commandDiscovery = this.serviceProvider.GetRequiredService<ICommandDiscovery>();
 | 
			
		||||
                var command = commandDiscovery.FindCommand(genericType);
 | 
			
		||||
                controller.ControllerName = command.Name;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,6 @@ namespace PoweredSoft.CQRS.AspNetCore.Mvc
 | 
			
		||||
            return builder;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        public static IMvcBuilder AddPoweredSoftCommandController(this IMvcBuilder builder)
 | 
			
		||||
        {
 | 
			
		||||
            var services = builder.Services;
 | 
			
		||||
@ -25,6 +24,6 @@ namespace PoweredSoft.CQRS.AspNetCore.Mvc
 | 
			
		||||
            builder.AddMvcOptions(o => o.Conventions.Add(new CommandControllerConvention(serviceProvider)));
 | 
			
		||||
            builder.ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new CommandControllerFeatureProvider(serviceProvider)));
 | 
			
		||||
            return builder;
 | 
			
		||||
        }*/
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ProjectReference Include="..\PoweredSoft.CQRS.Abstractions\PoweredSoft.CQRS.Abstractions.csproj" />
 | 
			
		||||
    <ProjectReference Include="..\PoweredSoft.CQRS.AspNetCore.Abstractions\PoweredSoft.CQRS.AspNetCore.Abstractions.csproj" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user