Autofac 通用装饰器,用于具有相同基础的不同接口

问题描述 投票:0回答:1

我有一个由两层组成的应用程序,WebApiDomain

WebApi 具有通过名为

IInteractor
的接口调用域中业务逻辑的控制器。

public interface IInteractor<TRequestModel, TResponseModel>
{
    Task<TResponseModel> Execute(TRequestModel model);
}

控制器操作的职责是:

  1. 接受网络请求
  2. 将其转换为请求模型
  3. 通过接口调用交互器
    IInteractor
  4. 将交互者的响应模型转换为 DTO
  5. 并返回给客户

以下是此类控制器操作的示例:

[HttpGet(Name = nameof(GetFoobar))]
public async Task<ActionResult> GetFoobar(
    [FromServices] IInteractor<GetFoobarRequestModel, OneOf<GetFoobarResponseModel, NotFound>> getFoobar,
    [FromQuery] string candidate
)
{
    var requestModel = new GetFoobarRequestModel(candidate);
    var result = await getFoobar.Execute(requestModel);

    return result
        .Match<ActionResult>(
            foobar => Ok(foobar),
            notFound => NotFound());
}

具体的交互器如下所示:

public class GetFoobarInteractor : IInteractor<GetFoobarRequestModel, OneOf<GetFoobarResponseModel, NotFound>>
{ ... }

交互器的 DI 注册是按照约定完成的。

builder.RegisterAssemblyTypes(assemblyThatContainsInteractors)
    .AsClosedTypesOf(typeof(IInteractor<,>))
    .AsImplementedInterfaces()
    .InstancePerLifetimeScope();

还有一个用于日志记录的装饰器,注册如下:

builder.RegisterGenericDecorator(typeof(InteractorLoggingDecorator<,>), typeof(IInteractor<,>));

到目前为止,效果非常好。
但是注入一个封闭的通用接口,例如

IInteractor<GetFoobarRequestModel, OneOf<GetFoobarResponseModel, NotFound>>
,有点麻烦,因为很难阅读。
我希望有一个更精简的界面来放大业务关注。
这可以通过为每个交互器引入一个接口来实现。

public interface IGetFoobar : IInteractor<GetFoobarRequestModel, OneOf<GetFoobarResponseModel, NotFound>>;

然后具体交互器实现新的精简接口:

public class GetFoobarInteractor : IGetFoobar
{ ... }

这样做,控制器动作可以摆脱笨拙的界面,变得更加清晰:

[HttpGet(Name = nameof(GetFoobar))]
public async Task<ActionResult> GetFoobar(
    [FromServices] IGetFoobar getFoobar,
    [FromQuery] string candidate
)
{ ... }

但是,现在有一个问题。

InteractorLoggingDecorator
不再被调用。
我认为这是因为
.AsImplementedInterfaces()
,因为它为两个接口注册了具体的交互器

  • IGetFoobar
  • IInteractor<GetFoobarRequestModel, OneOf<GetFoobarResponseModel, NotFound>>

并且装饰器仅适用于第二个。

如何按照约定为所有接口注册

InteractorLoggingDecorator

c# asp.net-core dependency-injection autofac
1个回答
0
投票

我想出了一种使用源生成器的不同方法。

在设计时,我只需创建实现

GetFoobarInteractor
接口的交互器
IInteractor

public class GetFoobarInteractor : IInteractor<GetFoobarRequestModel, OneOf<GetFoobarResponseModel, NotFound>>
{ ... }

点击

CTRL + SHIFT + B
后,源生成器启动并生成两个东西。

First:类似于封闭通用交互器接口

IInteractor<GetFoobarRequestModel, OneOf<GetFoobarResponseModel, NotFound>>
的接口,但不是通用的

public interface IGetFoobar
{
    Task<OneOf<GetFoobarResponseModel, NotFound> Execute(GetFoobarRequestModel requestModel);
} 

第二:实现上面接口的代理类。

public sealed class GetFoobarInteractorProxy : IGetFoobar
{
    private readonly IInteractor<GetFoobarRequestModel, OneOf<GetFoobarResponseModel, NotFound>>  _interactor;
    
    public GetFoobarInteractorProxy(
            IInteractor<GetFoobarRequestModel, OneOf<GetFoobarResponseModel, NotFound>> interactor)
    {
        _interactor = interactor;
    }
    
    Task<OneOf<GetFoobarResponseModel, NotFound> Execute(GetFoobarRequestModel requestModel) 
    {
        return _interactor.Execute(requestModel);
    }
}  

GetFoobarOverview

具体交互器

GetFoobarInteractor
将作为
IInteractor<GetFoobarRequestModel, OneOf<GetFoobarResponseModel, NotFound>>
注入到代理的构造函数中。
为了使其正常工作,我进行了以下 DI 注册:

builder.RegisterAssemblyTypes(assemblyThatContainsInteractors)
    .AsClosedTypesOf(typeof(IInteractor<,>))
    .AsImplementedInterfaces()
    .InstancePerLifetimeScope();

为了使用生成的界面

IGetFoobar
我需要添加以下代码:

builder.RegisterAssemblyTypes(assemblyThatContainsUseCases)
    .Where(x => x.Name.EndsWith("InteractorProxy"))
    .AsImplementedInterfaces()
    .InstancePerLifetimeScope();

现在,我有一个精简的界面

IGetFoobar
,可以将其注入到控制器操作中,并且装饰器也可以正常工作。

此外,我可以向生成的代理添加更多逻辑,例如验证。

© www.soinside.com 2019 - 2024. All rights reserved.