创建一个简单的Query对象

时间:2014-09-09 18:33:49

标签: c# asp.net-mvc

我创建了一个查询,如下所示:

public class FindPostEditModelByIdQuery : IQuery<PostEditModel> {

  private readonly ILogger _logger;
  private readonly ISession _session;

  public FindPostEditModelByIdQuery(ISession session, ILogger logger) {
    _logger = logger;
    _session = session;        
  }

  public async Task<PostEditModel> Run(int postId, out exception) {
    // Code here
  }

}

所以基本上我正在通过IOC请求我将使用的构造函数...

我希望这种方法允许以简单的方式使用异步来保持简单。

几个问题:

  1. 在我的控制器中,我会使用很多命令...... 我想避免在构造函数上有很多它们。 你认为注入一个请求命令的工厂会没问题吗? 我该怎么做?

  2. 我是否正确使用异步?

  3. 您如何将异常作为参数传递? 可以创建一些东西来记录它在Run方法上发生的异常。这甚至有意义吗?

  4. 更新

    与我正在使用的相比,我发布的内容是尝试使用async更容易。

    我正在使用查询/回复和命令(我当时称他们为订单),我有:

    public interface IOrderHandler {
      void Handle(Order order);
    }
    public interface IOrderHandler<TOrder> : IOrderHandler where TOrder : Order {
      void Handle(TOrder order);
    }
    public interface IQueryHandler {
      Reply Handle(Query query);
    }
    public interface IQueryHandler<TQuery, TReply> : IQueryHandler
      where TQuery : Query
      where TReply : Reply {
      Reply Handle(TQuery query);
    }
    
    public abstract class OrderHandler : IOrderHandler {
      public abstract void Handle(Order order); // Handle
    }
    public abstract class OrderHandler<TOrder> : OrderHandler, IOrderHandler<TOrder> where TOrder : Order {
      public override void Handle(Order order) {
        TOrder torder = (TOrder)order;
        Handle(torder);
      }
      public abstract void Handle(TOrder order); // Handle
    }
    
    public abstract class QueryHandler<TQuery, TReply> : QueryHandler, IQueryHandler<TQuery, TReply>
    where TQuery : Query
    where TReply : Reply {
      public override Reply Handle(Query query) {
        TQuery tquery = (TQuery)query;
        Reply treply = Handle(tquery);
        return treply;
      }
      public abstract Reply Handle(TQuery query);
    }
    
    public abstract class QueryHandler : IQueryHandler {
      public abstract Reply Handle(Query query);
    }
    
    public abstract class QueryHandler<TQuery, TReply> : QueryHandler, IQueryHandler<TQuery, TReply>
      where TQuery : Query
      where TReply : Reply {
      public override Reply Handle(Query query) {
        TQuery tquery = (TQuery)query;
        Reply treply = Handle(tquery);
        return treply;
      }
      public abstract Reply Handle(TQuery query);
    }
    
    public abstract class Order { } // Order
    
    public abstract class Query { }
    
    public abstract class Reply {
      public Exception Exception { get; set; }
    }
    
    public interface IDispatcher {
      void Send(Order order);
      void Send(Order order, out Exception exception);
      TReply Send<TReply>(Query query) where TReply : Reply, new();
    }
    
    public class Dispatcher : IDispatcher {
    
      public void Send(Order order) {
    
        Type type = typeof(IOrderHandler<>).MakeGenericType(order.GetType());
        IOrderHandler handler = (IOrderHandler)ObjectFactory.GetInstance(type);
    
        try {
          handler.Handle(order);
        } catch (Exception exception) {
          ILogger logger = ObjectFactory.GetInstance<ILogger>();
          logger.Send(exception);
          if (Debugger.IsAttached) throw;
        }
    
      }
    
      public void Send(Order order, out Exception exception) {
        Type type = typeof(IOrderHandler<>).MakeGenericType(order.GetType());
        IOrderHandler handler = (IOrderHandler)ObjectFactory.GetInstance(type);
        exception = null;
        try {
          handler.Handle(order);
        } catch (Exception ex) {
          ILogger logger = ObjectFactory.GetInstance<ILogger>();
          logger.Send(ex);
          exception = ex;
          if (Debugger.IsAttached) throw;
        }
    
      }
    
      public TReply Send<TReply>(Query query) where TReply : Reply, new() {
    
        Type type = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TReply));
    
        IQueryHandler handler = (IQueryHandler)ObjectFactory.GetInstance(type);
    
        try {
          return (TReply)handler.Handle(query);
        } catch (Exception exception) {
          ILogger logger = ObjectFactory.GetInstance<ILogger>();
          logger.Send(exception);
          if (Debugger.IsAttached) throw;
          return new TReply { Exception = exception };
        }
      }
    
    } // Dispatcher
    

    一些注意事项和问题

    1. 这是我在控制器上注入的调度程序。然后我用它:

      _dispacther.Send(new FindPostByIdQuery(22));

    2. 我不确定在调度程序中记录异常是否是一种好方法。 基本上我记录了服务层上发生的所有异常。

      我正在考虑的一件事是有一个可以附加的ExceptionHandler。

      但我不确定这是否可行。 您的想法是什么?

    3. 如果需要,我不知道如何让我的代码与异步一起使用。

    4. 有谁知道如何测试这种模式?这对我来说并不容易。

1 个答案:

答案 0 :(得分:2)

#1:您的控制器不应该使用那么多注入的命令。 Constructor over injection通常是一种需要进一步分解物体的气味。日志记录,异常处理等问题通常是交叉切割,并且在外层处理得更好。

#2:几乎没有任何代码可以说清楚。签名方式这是可以的,尽管如上所述,你可以取消out exception。调用者需要使用ContinueWithsimilar处理异步链中的错误。

task.ContinueWith(x => x.Exception.Handle(ex =>
                  {
                      logger.Error(ex.Message, ex);
                      return false;
                  }), TaskContinuationOptions.OnlyOnFaulted);

#3:异常日志记录不应该是查询对象的责任,而是位于其上方的东西 - 使用动作过滤器,或者在调用者级别或服务或更好 - 使用面向方面的编程。

<强>更新

看看ShortBus。除非您想DIY,否则这包含您正在寻找的所有内容,包括async handlersbubble up exceptions和单元测试的能力。

相关问题