在ASP.NET Core中使用Autofac注册通用类型时出现问题 [英] Issue registering generic types with Autofac in ASP.NET Core

查看:66
本文介绍了在ASP.NET Core中使用Autofac注册通用类型时出现问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Autofac和ASP.NET Core的相对较新的用户.我最近将一个小型项目从经典" ASP.NET WebAPI项目移植到了ASP.NET Core.我在使用Autofac时遇到麻烦,特别是在通用类型的注册方面.

I'm a relatively new user of both Autofac and ASP.NET Core. I've recently ported a small project from a 'classic' ASP.NET WebAPI project to ASP.NET Core. I am having trouble with Autofac, specifically in registration of generic types.

该项目使用Command模式,每个命令处理程序都是封闭的泛型,如

This project uses a Command pattern, each command handler is a closed generic like

public class UpdateCustomerCommandHandler: ICommandHandler<UpdateCustomerCommand>

这些命令处理程序将被注入到控制器中,例如:

These command handlers are injected into the controllers like:

readonly private ICommandHandler<UpdateCustomerCommand> _updateCustomerCommand;
public ValuesController(ICommandHandler<UpdateCustomerCommand> updateCustomerCommand)
{
    _updateCustomerCommand = updateCustomerCommand;
}

Autofac部分配置为:

Autofac is configured (partially) as:

 var builder = new ContainerBuilder();
 var assemblies = AppDomain.CurrentDomain.GetAssemblies();
 //This doesn't seem to be working as expected.
 builder.RegisterAssemblyTypes(assemblies)
     .As(t => t.GetInterfaces()
         .Where(a => a.IsClosedTypeOf(typeof(ICommandHandler<>)))
         .Select(a => new KeyedService("commandHandler", a)));

以上似乎并未按预期注册通用.如果我使用以下方法进行注册,则效果很好.

The above does not seem to be registering the generic as expected. If I use the below method for registration, it works well.

 builder.RegisterType<UpdateCustomerCommandHandler>().As<ICommandHandler<UpdateCustomerCommand>>();

当我说它不起作用"时,我的意思是,当尝试实例化控制器时,我收到"InvalidOperationException:无法解析类型为'BusinessLogic.ICommandHandler`1 [BusinessLogic.UpdateCustomerCommand]'的服务"尝试激活"AutoFac_Test.Controllers.ValuesController"."

When I say "It doesn't work", what I mean is that when attempting to instantiate the controller, I get "InvalidOperationException: Unable to resolve service for type 'BusinessLogic.ICommandHandler`1[BusinessLogic.UpdateCustomerCommand]' while attempting to activate 'AutoFac_Test.Controllers.ValuesController'."

在此项目的完整WebAPI版本中,此方法运行良好,但在ASP.NET Core中重新创建它后,效果不佳.需要明确的是,在移植到ASP.NET Core之前,它运行得非常好.

This worked well in the Full WebAPI version of this project, but not after recreating it in ASP.NET Core. To be clear, this was working perfectly well before porting to ASP.NET Core.

这里是我用来重现此问题的代码的链接: https://dl.dropboxusercontent.com/u/185950/AutoFac_Test.zip

Here is a link to the code that I've used to recreate this issue: https://dl.dropboxusercontent.com/u/185950/AutoFac_Test.zip

****发现解决方案后进行编辑****

**** EDIT AFTER SOLUTION DISCOVERED ****

我的Autofac配置实际上并没有错,当然Autofac本身也没有错.发生的事情是,我重命名了相关程序集的输出,以使程序集扫描工作更容易(替换AppDomain.CurrentDomain.GetAssemblies()更为优雅,但是我从未修改API项目的依赖项以引用新程序集.因此,Autofac正在扫描正确加载的程序集,而该程序集恰好是旧版本,其中不包含我期望的接口和实现...

There was nothing in fact wrong with my Autofac configuration and certainly not Autofac itself. What had happened was that I had renamed the output of my dependent assemblies in an effort to make the assembly scanning stuff (replacing of AppDomain.CurrentDomain.GetAssemblies() more elegant, however I never modified the dependencies of the API project to reference the new assemblies. So Autofac was scanning the correctly loaded assemblies which happened to be the older versions, which did not contain the interfaces and implementations I expected...

推荐答案

Autofac具有内置支持,可以注册开放类型的封闭类型.

Autofac has built-in support to register closed types of open-generic.

builder
    .RegisterAssemblyTypes(ThisAssembly)
    .AsClosedTypesOf(typeof(ICommandHandler<>));

这将扫描您的程序集,找到关闭打开的通用ICommandHandler<>接口的类型,并针对它们实现的关闭的通用接口(在您的情况下,为ICommandHandler<UpdateCustomerCommand>)注册每个类型.

This will scan your assembly, find types that close the open generic ICommandHandler<> interface, and register each of them against the closed generic interface they implement - in your case, ICommandHandler<UpdateCustomerCommand>.

在您的示例中无效的是您将密钥与服务相关联.尝试实例化ValuesController时,Autofac不会查找ICommandHandler<UpdateCustomerCommand>的键控版本,这就是为什么您会得到异常的原因.

What doesn't work in your example is that you associate a key to your services. Autofac doesn't look for the keyed version of your ICommandHandler<UpdateCustomerCommand> when trying to instantiate the ValuesController, which is why you get the exception.

在QuietSeditionist评论后

我将尝试详细介绍默认服务.注册处理程序的方式是将commandHandler键与它们相关联.

I'll try to elaborate a bit on the keyed vs. default services. The way you registered your handlers is by associating the commandHandler key to them.

这意味着一旦容器被构建,这是解析此类处理程序的唯一方法:

This means that once the container is built, here's the only way you can resolve such a handler:

// container will look for a registration for ICommandHandler<UpdateCustomerCommand> associated with the "commandHandler" key
container.ResolveKeyed<ICommandHandler<UpdateCustomerCommand>>("commandHandler");

实例化ValuesController时,Autofac不会查找ICommandHandler<UpdateCustomerCommand> keyed 注册,因为没有要求这样做.

When instantiating ValuesController, Autofac doesn't look for a keyed registration of ICommandHandler<UpdateCustomerCommand>, because it wasn't asked to.

它正在执行的等效代码是-,您可以尝试自己运行该代码以获取异常:

The equivalent code it's executing is - and you can try to run that code yourself to get the exception:

// BOOM!
container.Resolve<ICommandHandler<UpdateCustomerCommand>>();

第二次注册有效的原因是因为您没有该服务:

The reason your second registration works is because you didn't key the service:

// No key
builder
    .RegisterType<UpdateCustomerCommandHandler>()
    .As<ICommandHandler<UpdateCustomerCommand>>();

// commandHandler key
builder
    .RegisterType<UpdateCustomerCommandHandler>()
    .Keyed<ICommandHandler<UpdateCustomerCommand>>("commandHandler");

但是,由于您不想一个接一个地注册所有处理程序,因此以下是在不操作它们的情况下注册它们的方法:

But since you don't want to register all your handlers one by one, here's how to register them without keying them:

builder
    .RegisterAssemblyTypes(ThisAssembly)
    .AsClosedTypesOf(typeof(ICommandHandler<>));

/编辑

我看到了 keying 服务可能有用的两种情况:

I can see two scenarios where keying services can be useful:

  • 您有几种实现相同接口的类型,并且想要在不同的服务中注入不同的实现.假设您同时将SqlConnectionDB2Connection注册为IDbConnection.然后,您有2个服务,其中一个应该以SQL Server为目标,另一个是DB2.如果它们都依赖于IDbConnection,则要确保在每个服务中注入正确的服务.

  • You have several types implementing the same interface and you want to inject different implementations in different services. Let's say, you register both SqlConnection and DB2Connection as IDbConnection. You then have 2 services, one which is supposed to target SQL Server, the other one DB2. If they both depend on IDbConnection, you want to make sure you inject the correct one in each service.

如果您使用装饰器 ,注册的工作方式是您通过 key 定义装饰器将应用的服务-第一个示例是不言自明的

If you use decorators, the way registrations work is you define the services to which the decorators will apply by a key - the first example is self-explanatory

这篇关于在ASP.NET Core中使用Autofac注册通用类型时出现问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆