2015-09-16 17 views
5

メディエータのパイプラインを実装するために、私はthis postをJimmy Bogardに追いかけようとしています。そのため、前/後要求ハンドラを使用して作業を行うことができます。その記事のコメントから私はこれに来るgithub gist。私はまだこれをすべてフックする方法を理解していないので、ここに私の最初の行くことです。 FYI - 私はDIとWeb Api 2のためにAutofacを使用しています。CQRSの後に、ここではクエリです。CQRSを使用したメディエータパイプラインでのエラー/例外の処理?

public class GetAccountRequest : IAsyncRequest<GetAccountResponse> 
{ 
    public int Id { get; set; } 
} 

//try using fluent validation 
public class GetAccountRequestValidationHandler 
    : AbstractValidator<GetAccountRequest>, IAsyncPreRequestHandler<GetAccountRequest> 
{ 
    public GetAccountRequestValidationHandler() { 
     RuleFor(m => m.Id).GreaterThan(0).WithMessage("Please specify an id."); 
    } 

    public Task Handle(GetAccountRequest request) { 
     Debug.WriteLine("GetAccountPreProcessor Handler"); 
     return Task.FromResult(true); 
    } 
} 

public class GetAccountResponse 
{ 
    public int AccountId { get; set; } 
    public string Name { get; set; } 
    public string AccountNumber { get; set; } 
    public string Nickname { get; set; } 
    public string PhoneNumber { get; set; } 
    public List<OrderAckNotification> OrderAckNotifications { get; set; } 

    public class OrderAckNotification { 
     public int Id { get; set; } 
     public bool IsDefault { get; set; } 
     public string Description { get; set; } 
     public string Type { get; set; } 
    } 
} 

GetAccountRequestHandler:ここ

public class GetAccountRequestHandler 
    : IAsyncRequestHandler<GetAccountRequest, GetAccountResponse> 
{ 
    private readonly IRedStripeDbContext _dbContext; 

    public GetAccountRequestHandler(IRedStripeDbContext redStripeDbContext) 
    { 
     _dbContext = redStripeDbContext; 
    } 

    public async Task<GetAccountResponse> Handle(GetAccountRequest message) 
    { 
     //some mapping code here.. omitted for brevity 
     Mapper.AssertConfigurationIsValid(); 

     return await _dbContext.Accounts.Where(a => a.AccountId == message.Id) 
      .ProjectToSingleOrDefaultAsync<GetAccountResponse>(); 
    } 

はHTTPGET示す現在のウェブAPI 2コントローラです。私はGetAccountRequestValidationHandlerに取得しています

public void Configuration(IAppBuilder app) 
{ 
    var config = new HttpConfiguration(); 

    ConfigureDependencyInjection(app, config); 

    WebApiConfig.Register(config); 
    app.UseWebApi(config); 
} 

private static void ConfigureDependencyInjection(IAppBuilder app, 
    HttpConfiguration config) 
{ 
    var builder = new ContainerBuilder(); 
    builder.RegisterSource(new ContravariantRegistrationSource()); 
    builder.RegisterAssemblyTypes(typeof(IMediator).Assembly).AsImplementedInterfaces(); 

    builder.Register<SingleInstanceFactory>(ctx => 
    { 
     var c = ctx.Resolve<IComponentContext>(); 
     return t => c.Resolve(t); 
    }); 

    builder.Register<MultiInstanceFactory>(ctx => 
    { 
     var c = ctx.Resolve<IComponentContext>(); 
     return t => (IEnumerable<object>)c.Resolve(
      typeof(IEnumerable<>).MakeGenericType(t)); 
    }); 

    //register all pre handlers 
    builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) 
     .As(type => type.GetInterfaces() 
      .Where(t => t.IsClosedTypeOf(typeof(IAsyncPreRequestHandler<>)))); 

    //register all post handlers 
    builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) 
     .As(type => type.GetInterfaces() 
      .Where(t => t.IsClosedTypeOf(typeof(IAsyncPostRequestHandler<,>)))); 


    //register all handlers 
    builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) 
     .As(type => type.GetInterfaces() 
      .Where(t => t.IsClosedTypeOf(typeof(IAsyncRequestHandler<,>))) 
      .Select(t => new KeyedService("asyncRequestHandler", t))); 

    //register pipeline decorator 
    builder.RegisterGenericDecorator(typeof(AsyncMediatorPipeline<,>), 
     typeof(IAsyncRequestHandler<,>), "asyncRequestHandler"); 

    // Register Web API controller in executing assembly. 
    builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).InstancePerRequest(); 

    //register RedStripeDbContext 
    builder.RegisterType<RedStripeDbContext>().As<IRedStripeDbContext>() 
     .InstancePerRequest(); 

    builder.RegisterType<AutofacServiceLocator>().AsImplementedInterfaces(); 
    var container = builder.Build(); 

    config.DependencyResolver = new AutofacWebApiDependencyResolver(container); 

    // This should be the first middleware added to the IAppBuilder. 
    app.UseAutofacMiddleware(container); 

    // Make sure the Autofac lifetime scope is passed to Web API. 
    app.UseAutofacWebApi(config); 
} 

[RoutePrefix("api/Accounts")] 
public class AccountsController : ApiController 
{ 
    private readonly IMediator _mediator; 

    public AccountsController(IMediator mediator) 
    { 
     _mediator = mediator; 
    } 

    // GET: api/Accounts/2 
    [Route("{id:int}")] 
    [HttpGet] 
    public async Task<IHttpActionResult> GetById([FromUri] GetAccountRequest request) 
    { 
     var model = await _mediator.SendAsync<GetAccountResponse>(request); 

     return Ok(model); 
    } 
} 

は最後に、ここで依存関係解決コードです。しかし、検証に失敗すると(0のIDが渡されます)、どのように例外をスローするか、パイプラインの実行を停止しますか? .WithMessageを返すにはどうしたらいいですか?

答えて

5

私も半苦労しました。あなたはそれがコマンドをロード要求にエラーやエラーの主なハンドラチェックを持っているのいずれかを処理することができます前に、

1)

前のハンドラを使用して...:2/3つのオプションがあるようです/クエリ

OR

2)事前ハンドラが例外をスローしてもらいます。この練習の周りにはかなりの不一致があるようです。一方では、例外を伴う制御フローを管理するように感じるが、「プロ」キャンプは、クライアントが最初に有効なコマンドを送信する責任を負うべきだと主張する。つまりユーザーが「アカウントの作成」をクリックする前に、ユーザー名が使用可能であることを確認するためのajaxクエリーを送信できます。この場合、このルールを破る例外は競合状態によるものです。

パイプラインに検証ハンドラを直接配置します。

これは、@ jbogardが何を考えていたかに沿っていると思います。 私は現在これを使用していませんが、これがどのように見えるかをスケッチしました。おそらくもっと良い例があります。要点は、それがパイプラインの一部であることで、検証ランナーはメインハンドラが呼び出されずに呼び出し元に戻ることができるということです。

public class AsyncValidationPipeline<TRequest, TResponse> : IAsyncRequestHandler<TRequest, TResponse> 
    where TRequest : IAsyncRequest<TResponse> 
{ 
    private IAsyncRequestHandler<TRequest, TResponse> _inner; 
    private IValidator<TRequest>[] _validators; 

    public AsyncValidationPipeline(IAsyncRequestHandler<TRequest, TResponse> inner, 
     IValidator<TRequest>[] validators) 
    { 
     _inner = inner; 
     _validators = validators; 
    } 
    public Task<TResponse> Handle(TRequest message) 
    { 
     List<string> errors = new List<string>(); 
     if (_validators != null && _validators.Any()) 
     { 
      errors = _validators.Where(v => v.Fails(message)) 
       .Select(v => v.ErrorMessage); 
     } 

     if (errors.Any()) 
     { 
      throw new ValidationException(errors); 
     } 
     return _inner.Handle(message); 
    } 
} 

ここAutoFacとそれをフックするコードです:

  //register all pre handlers 
      builder.RegisterAssemblyTypes(assembliesToScan) 
       .AsClosedTypesOf(typeof(IAsyncPreRequestHandler<>)); 

      //register all post handlers 
      builder.RegisterAssemblyTypes(assembliesToScan) 
       .AsClosedTypesOf(typeof(IAsyncPostRequestHandler<,>)); 

      const string handlerKey = "async-service-handlers"; 
      const string pipelineKey = "async-service-pipelines"; 

      // Request/Response for Query 

      builder.RegisterAssemblyTypes(assembliesToScan) 
       .AsKeyedClosedTypesOf(typeof(IAsyncRequestHandler<,>), handlerKey) 
       ; 

      // Decorate All Services with our Pipeline 
      //builder.RegisterGenericDecorator(typeof(MediatorPipeline<,>), typeof(IRequestHandler<,>), fromKey: "service-handlers", toKey: "pipeline-handlers"); 
      builder.RegisterGenericDecorator(typeof(AsyncMediatorPipeline<,>), typeof(IAsyncRequestHandler<,>), fromKey: handlerKey, toKey: pipelineKey); 

      // Decorate All Pipelines with our Validator 
      builder.RegisterGenericDecorator(typeof(AsyncValidationHandler<,>), typeof(IAsyncRequestHandler<,>), fromKey: pipelineKey);//, toKey: "async-validator-handlers"); 

      // Add as many pipelines as you want, but the to/from keys must be kept in order and unique 

・ホープ、このことができます....

関連する問題