简体   繁体   English

创建一个简单的查询对象

[英]Create a simple Query object

I created a query as follows: 我创建了一个查询,如下所示:

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
  }

}

So basically on the constructor I am requesting through IOC what I will use ... 因此,基本上,在构造函数中,我通过IOC要求使用什么...

This approach allows, I hope, to use async in an easy way an keep things simple. 我希望这种方法允许以简单的方式使用异步,从而使事情保持简单。

A few questions: 几个问题:

  1. In my controllers I will use many commands ... I would like to avoid having a lot of them on the constructor. 在我的控制器中,我将使用许多命令...我想避免在构造函数中使用很多命令。 Do you think injecting a factory that requests a command would be ok? 您认为注入请求命令的工厂是否可以? How should I do this? 我应该怎么做?

  2. Am I using async correctly? 我是否正确使用异步?

  3. What do you think about passing the exception as a parameter? 您如何看待将异常作为参数传递? Would be possible to create something that would log an exception the momment it occurs on the Run methods. 将有可能创建一些东西来记录异常,而该异常会在Run方法上发生。 Does this even make sense? 这有道理吗?

UPDATE UPDATE

What I post is a try to make it easier to use async when compared to what I am using. 我发布的内容是尝试使与我正在使用的内容相比更易于使用异步。

I am using Queries / Replies and Commands (I called them orders at the time) and I have: 我正在使用查询/答复和命令(当时我称它们为订单),并且有:

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

A FEW NOTES AND QUESTIONS 一些注意事项和问题

  1. It is the dispatcher I am injecting on my controllers. 它是我在控制器上注入的调度程序。 And then I use it: 然后我用它:

    _dispacther.Send(new FindPostByIdQuery(22)); _dispacther.Send(new FindPostByIdQuery(22));

  2. I am not sure if logging the exceptions in the dispatcher is a good approach. 我不确定在调度程序中记录异常是否是一个好方法。 Basically I am logging all exceptions that occur on the service layer. 基本上,我记录了服务层上发生的所有异常。

    One thing I was considering was having an ExceptionHandler which could be attached. 我正在考虑的一件事是拥有一个可以附加的ExceptionHandler。

    But I am not sure if this could be possible. 但是我不确定这是否可能。 What do you think? 你怎么看?

  3. I am not sure how to make my code to work with async if I need it. 我不确定如果需要,如何使我的代码与异步一起使用。

  4. Does anyone knows how to test this pattern? 有谁知道如何测试这种模式? It has not been easy for me. 对我来说,这并不容易。

#1: Your controller should not be using that many injected commands. #1:您的控制器不应使用那么多注入的命令。 Constructor over injection is usually a smell that the object needs to be broken down further. 构造函数过度注入通常是需要进一步分解对象的气味。 Concerns like logging, exception handling are often cross cutting and they are better handled at an outer layer. 诸如日志记录,异常处理之类的问题通常是交叉的,最好在外层进行处理。

#2: There is barely any code to be able to say anything conclusively. #2:几乎没有任何代码可以得出结论。 Signature-wise this is ok, although like I said above, you can probably do away with out exception . 从签名的角度来看,这是可以的,尽管就像我在上面说的那样,您可以无out exception消除它。 The caller needs to handle the fault in a async chain using ContinueWith or similar . 调用方需要使用ContinueWith类似方法处理异步链中的错误。

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

#3: Exception logging should not be the responsibility of the query object, rather something that sits above it - using action filters, or at the caller level or a service or better - using aspect oriented programming. #3:异常记录不应由查询对象负责,而应由位于其上方的对象-使用动作过滤器,或在调用者级别或服务或更高级别-使用面向方面的编程。

Update: 更新:

Take a look at ShortBus . 看一下ShortBus Unless you want to DIY, this has all the things you're looking for, including async handlers , ability to bubble up exceptions and unit tests. 除非您想自己动手做,否则它就拥有您正在寻找的所有东西,包括异步处理程序冒泡异常和单元测试的功能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM