如何配置 Autofac 以解析 CQRS 处理程序并在 Web API 项目中编写其查询调度程序

How to configure Autofac to resolve CQRS handlers and write its query dispatcher in a Web API project

我在 Project1 的查询端有以下内容,主要包含接口

public interface IQueryResult {}
public interface IQuery<TResult> where TResult : IQueryResult {}
public interface IQueryHandler<TQuery, TResult> 
    where TQuery : IQuery<TResult> 
    where TResult : IQueryResult
{
    Task<TResult> HandleAsync(TQuery query);
}

public class PersonQueryResult : IQueryResult
{
    public string Name { get; set; }
}
public class GetPersonDetailsQuery : IQuery<PersonQueryResult>
{
    public int Id { get; set; }
}
public interface IQueryDispatcher
{
    Task<TResult> DispatchAsync<TQuery, TResult>(TQuery query)
        where TQuery : IQuery<TResult>
        where TResult : IQueryResult;
}

在引用项目 1 的第二个 Project2 中,我有

public class GetPersonDetailsQueryHandler : 
IQueryHandler<GetPersonDetailsQuery, PersonQueryResult>
{
    public Task<PersonQueryResult> HandleAsync(GetPersonDetailsQuery query)
    {
        return Task.FromResult( new PersonQueryResult {Name = "Bamboo"});
    }
}

最后一个 项目 3 是一个 Web API 项目,它只引用项目 1 而不是项目 2。因此它只知道接口、命令和查询。 我需要以一种我可以轻松地执行类似操作的方式配置 autofac

var query = new GetPersonDetailsQuery { Id = 1 };
var magicHappensHere = new QueryDispatcher(); //any better way?
PersonQueryResult result = magicHappensHere.Dispatch(query); 

此外,我在项目 1 中的 IQueryDispatcher 似乎不适合上述工作。 开放建议的该接口的示例实现是

public class QueryDispatcher : IQueryDispatcher
{
    private readonly IComponentContext _context;

    public QueryDispatcher(IComponentContext context)
    {
        this._context = context;
    }

    public Task<TResult> DispatchAsync<TQuery, TResult>(TQuery query) where TQuery : IQuery<TResult> where TResult : IQueryResult
    {
        var handler = _context.Resolve<IQueryHandler<TQuery, TResult>>();

        return handler.HandleAsync(query);
    }
}

我不知道如何实施的可能解决方案 (A) 在项目 2 中定义一个 Autofac 模块,然后在 Web API 中扫描项目 2 程序集?.. (乙) http://docs.autofac.org/en/latest/register/registration.html#open-generic-components (C) 扫描程序集并尝试自动映射。 需要有关在此处插入代码的帮助

private static void ConfigureAutofac(HttpConfiguration config) 
{
    var builder = new ContainerBuilder();
    //*************//
    //what to do here?
    //**************//
    builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
    builder.RegisterWebApiFilterProvider(config);

    var container = builder.Build();
    config.DependencyResolver = new   AutofacWebApiDependencyResolver(container);
}    

您的 Web API 项目包含应用程序的 Composition Root 并且组合根根据定义引用应用程序中的所有其他程序集。不引用它是没有意义的,只会使事情复杂化。

请阅读this q/a以获得更详细的讨论:

Many developers don’t want their [Web API] assembly to depend on the DAL assembly, but that's not really a problem. Don't forget that assemblies are a deployment artifact; you split code into multiple assemblies to allow code to be deployed separately. An architectural layer on the other hand is a logical artifact. It's very well possible (and common) to have multiple layers in the same assembly. In this case we'll end up having the Composition Root (layer) and the Presentation Layer in the same web application project (thus in the same assembly). And even though that assembly references the assembly containing the DAL, the Presentation Layer still does not reference the Data Access Layer. This is a big distinction. Of course, when we do this, we lose the ability for the compiler to check this architectural rule at compile time, but this shouldn't be a problem. Most architectural rules actually can't be checked by the compiler and there's always something like common sense. And if there's no common sense in your team, you can always use code reviews (which every team should IMO always do btw). You can also use a tool such as NDepend (which is commercial), which helps you verifying your architectural rules.

一般来说,只有在您有插件模型的情况下,才需要阻止从组合根到其他程序集的硬引用,其中程序集在编译时未知,并且可以在编译期间或之后添加部署。然而,这些类型的场景对于 LOB 应用程序并不常见。

最终使建议的解决方案 (B) http://docs.autofac.org/en/latest/register/registration.html#open-generic-components 生效。我还可以确认没有将对 dll 的引用添加到解决方案中

不过,我仍然需要一种从 API 项目发送查询的好方法。更直观的东西

private static void ConfigureAutofac(HttpConfiguration config) 
{
    var builder = new ContainerBuilder();
    //*************//
    //heres what i did
    //*************//
    var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>();

    foreach (var assembly in assemblies)
    {
        builder.RegisterAssemblyTypes(assembly).AssignableTo<IQueryResult>().AsImplementedInterfaces();

        builder.RegisterAssemblyTypes(assembly).AsClosedTypesOf(typeof(IQuery<>)).AsImplementedInterfaces();

        builder.RegisterAssemblyTypes(assembly).AsClosedTypesOf(typeof(IQueryHandler<,>)).AsImplementedInterfaces();

    } 
    //rest of the code
}