zoukankan      html  css  js  c++  java
  • [译]MediatR, FluentValidation, and Ninject using Decorators

    原文

    CQRS

    我是CQRS模式的粉丝。对我来说CQRS能让我有更优雅的实现。它同样也有一些缺点:通常需要更多的类,workflow不总是清晰的。

    MediatR

    MediatR的文档非常不错,在这就不重复了。但是为了有个基本的了解,在这还是举个小例子,来看看command是怎么被处理的:

    [Test]
    public void ItShouldHandleBasicCommands()
    {
        var mediator = GetMediator();
    
        var command = new Command();
        var response = mediator.Send(command);
    
        response.Should().NotBeNull();
    }
    

    MediatR接收一个command,让后将它发送到适当的handler去:

    public class Command : IRequest<Response>
    {
    }
    
    public class CommandHandler : IRequestHandler<Command, Response>
    {
        public Response Handle(Command message)
        {
            return new Response();
        }
    }
    

    注册command和command handler非常简单。下面的例子是关于使用Ninject基于约定来注册。找到所有的handler接口,绑定它们:

    kernel.Bind(scan => scan.FromThisAssembly()
        .SelectAllClasses()
        .Where(o => o.IsAssignableFrom(typeof(IRequestHandler<,>)))
        .BindAllInterfaces());
    

    下面是一些其他的注册,它们也非常重要。注册IMediator。factory实例的调用告诉MediatR如何解析单个和多个实例。然后注册MediatR。

    kernel.Bind<SingleInstanceFactory>()
        .ToMethod(context => (type => context.Kernel.Get(type)));
    kernel.Bind<MultiInstanceFactory>()
        .ToMethod(context => (type => context.Kernel.GetAll(type)));
    var mediator = kernel.Get<IMediator>();
    

    Validation and Decorators

    装饰模式可以让我们在不修改对象的前提下,增加这个对象的行为。一个通常的做法是添加输入验证。使用装饰器包装一个command handler,以使得可以在使用前验证这个command。下面的command和command handler简单的返回一个response。

    public class Foo : IRequest<Response>
    {
        public string Message { get; set; }
    }
    
    public class FooHandler : IRequestHandler<Foo, Response>
    {
        public Response Handle(Foo message)
        {
            return new Response();
        }
    }
    

    FluentValidation是个非常不错的验证包。首先我们需要一个validation类,然后我们需要用Ninject注册它。

    下面是一个简单的验证器。判断Message属性是否为空。如果为空,返回一个错误:

    public class FooValidator : AbstractValidator<Foo>
    {
        public FooValidator()
        {
            RuleFor(ping => ping.Message).NotEmpty();
        }
    }
    

    使用Ninject注册验证器非常简单。下面的代码绑定了程序集中所有的验证器:

    kernel.Bind(scan => scan.FromThisAssembly()
        .SelectAllClasses()
        .InheritedFrom(typeof(AbstractValidator<>))
        .BindAllInterfaces());
    

    下一步是使用这个validator。下面的代码来自Jimmy Bogard的网站。是一个实现用来在命令发送到handler前验证命令的装饰类的例子:

    public class ValidatingHandler<TRequest, TResponse> : IRequestHandler<TRequest, TResponse>
        where TRequest : IRequest<TResponse>
    {
        private readonly IRequestHandler<TRequest, TResponse> handler;
        private readonly IValidator<TRequest> validator;
    
        public ValidatingHandler(IRequestHandler<TRequest, TResponse> handler, IValidator<TRequest> validator)
        {
            this.handler = handler;
            this.validator = validator;
        }
    
        [DebuggerStepThrough]
        public TResponse Handle(TRequest message)
        {
            var validationResult = validator.Validate(message);
    
            if (validationResult.IsValid)
                return handler.Handle(message);
    
            throw new ValidationException(validationResult.Errors);
        }
    }
    

    下一步是如何配置Ninjec来创建一个handler和装饰它。Binding Decorators - Mediators with Ninject这篇文章讲的非常好。"注册handler。当验证handler创建后,注入一个handler。当handler被请求返回一个validating handler。"

    当Ninject被要求创建一个handler,它首先创建一个calidating handler。

    kernel.Bind(scan => scan.FromThisAssembly()
        .SelectAllClasses()
        .Where(o => o.IsAssignableFrom(typeof(IRequestHandler<,>)))
        .BindAllInterfaces());
    
    kernel.Bind(scan => scan.FromThisAssembly()
        .SelectAllClasses()
        .InheritedFrom(typeof(IRequestHandler<,>))
        .BindAllInterfaces()
        .Configure(o => o.WhenInjectedInto(typeof(ValidatingHandler<,>))));
    
    kernel.Bind(typeof(IRequestHandler<,>)).To(typeof(ValidatingHandler<,>));
    

    下面看看如果command的message是空和不是空的时候是什么样子的。

    [Test]
    public void ItShouldProcessCommands()
    {
        var mediator = GetMediator();
    
        var command = new Foo { Message = "valid ping" };
        var response = mediator.Send(command);
    
        response.Should().NotBeNull();
    }
    
    [Test]
    public void ItShouldValidateTheCommand()
    {
        var mediator = GetMediator();
    
        var ping = new Foo();
        Action act = () => mediator.Send(ping);
    
        act.ShouldThrow<ValidationException>();
    }
    

    本文的源码位于github

  • 相关阅读:
    [LeetCode] Power of Three 判断3的次方数
    [LeetCode] 322. Coin Change 硬币找零
    [LeetCode] 321. Create Maximum Number 创建最大数
    ITK 3.20.1 VS2010 Configuration 配置
    VTK 5.10.1 VS2010 Configuration 配置
    FLTK 1.3.3 MinGW 4.9.1 Configuration 配置
    FLTK 1.1.10 VS2010 Configuration 配置
    Inheritance, Association, Aggregation, and Composition 类的继承,关联,聚合和组合的区别
    [LeetCode] Bulb Switcher 灯泡开关
    [LeetCode] Maximum Product of Word Lengths 单词长度的最大积
  • 原文地址:https://www.cnblogs.com/irocker/p/mediatr-fluentvalidation-and-ninject.html
Copyright © 2011-2022 走看看