当前位置: 首页 > 知识库问答 >
问题:

在ASP.NET核心中向Autofac注册泛型类型时出现问题

太叔昊穹
2023-03-14

我是Autofac和 ASP.NET Core的相对较新的用户。我最近将一个小项目从一个“经典”ASP.NET WebAPI项目移植到 ASP.NET Core。我在Autofac上遇到了问题,特别是在泛型类型的注册方面。

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

public class UpdateCustomerCommandHandler: ICommandHandler<UpdateCustomerCommand>

这些命令处理程序被注入到控制器中,如下所示:

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

Autofac(部分)配置为:

 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)));

上面的内容似乎没有像预期的那样注册通用。如果我用下面的方法注册,效果很好。

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

当我说“它不起作用”时,我的意思是当尝试实例化控制器时,我得到“无效操作异常:在尝试激活“AutoFac_Test.Controllers.ValuesController”时无法解析类型“BusinessLogic.ICommandHandler”1[BusinessLogic.UpdateCustomerCommand]的服务。”

这在该项目的完整WebAPI版本中工作得很好,但在ASP.NET核心中重新创建之后就不行了。需要说明的是,在移植到ASP.NET核心之前,这种方法运行得非常好。

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

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

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

共有2个答案

袁英豪
2023-03-14

因为即使当你试图手动注册类型时,Google也会带你到这个页面,所以我认为即使这个页面没有回答你的问题,它对未来的访问者还是有用的。因此,如果您想要手动注册一个泛型类型,可以使用以下格式:

service.AddTransient(typeof(IThing<>), typeof(GenericThing<>));

或者如果没有接口,那么只需:

service.AddTransient(typeof(GenericThing<>));

并且为了完整性,如果您有一个具有多个类型的泛型:

services.AddTransient(typeof(GenericThing<,>));
程冥夜
2023-03-14

Autofac内置支持注册封闭类型的开放通用。

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

这将扫描您的程序集,查找关闭打开的泛型< code>ICommandHandler的类型

在您的示例中不起作用的是您将密钥与服务相关联。Autofac不会查找<code>ICommandHandler的键控版本

在QuietSeditionist的评论后编辑:

我将尝试详细介绍键控服务与默认服务。注册处理程序的方法是将<code>commandHandler</code>键与它们关联。

这意味着,构建容器后,只有这样一种方法可以解析此类处理程序:

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

实例化 ValuesController 时,Autofac 不会查找 ICommandHandler 的键控注册

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

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

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

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

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

但是,由于您不想一个一个地注册所有的处理程序,以下是如何注册它们而不用键入它们:

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

/Edit

我可以看到两种情况下键控服务可能很有用:

>

  • 您有几种类型的实现相同的接口,并且您希望在不同的服务中注入不同的实现。比方说,您将 SqlConnectionDB2Connection 都注册为 IDbConnection。然后,您将有 2 个服务,一个应该面向 SQL Server,另一个面向 DB2。如果它们都依赖于 IDbConnection,则需要确保在每个服务中注入正确的 IDbConnection

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

  •  类似资料:
    • 我想知道如何在ASP中处理依赖注入。NET核心,用于同时具有对象和字符串作为参数的类型。由于字符串无法注册到DI框架,我目前正在使用implementationfactory并使用服务定位器模式,还有其他方法吗?是否有类似于Autofac的命名参数的.WithParameter? Asp.net核心DI使得向DI框架注册一个类型变得容易(并且干净),对于那些已经向DI框架注册了参数的类型。 给定以

    • 我正在尝试用Guice注入泛型类型。我有存储库 所以当我创建光标时

    • 源代码 ENS中的反向解析是指从以太坊地址(比如0x1234...)到ENS域名的映射,它通过一个特定的域名空间(.addr.reverse)来实现。这个域名空间由一个专用注册中心拥有和控制,该注册中心可以接受任何人的调用,并根据调用者的地址为其分配子域名。 例如,账户 0x314159265dd8dbb310642f98f50c066173c1259b 可以通过调用声明 314159265dd8

    • 抱歉,问题太简单了。我正在使用b2c扩展,无法找到定义的定义。有人能告诉我在哪里搜索那些文件吗。

    • 泛型类型 除了反省函数, Swift允许你定义自己的泛型类型. 它们是可以用于任意类型的自定义类、结构体、枚举, 和Array、Dictionary方式类型. 1. 定义泛型类型 定义一个普通的结构体 struct IntStack { var items = [Int]() mutating func push(_ item: Int) { items.appen