我有一个由两层组成的应用程序,WebApi和Domain。
WebApi 具有通过名为
IInteractor
的接口调用域中业务逻辑的控制器。
public interface IInteractor<TRequestModel, TResponseModel>
{
Task<TResponseModel> Execute(TRequestModel model);
}
控制器操作的职责是:
IInteractor
以下是此类控制器操作的示例:
[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
?
我想出了一种使用源生成器的不同方法。
在设计时,我只需创建实现
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);
}
}
具体交互器
GetFoobarInteractor
将作为IInteractor<GetFoobarRequestModel, OneOf<GetFoobarResponseModel, NotFound>>
注入到代理的构造函数中。builder.RegisterAssemblyTypes(assemblyThatContainsInteractors)
.AsClosedTypesOf(typeof(IInteractor<,>))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
为了使用生成的界面
IGetFoobar
我需要添加以下代码:
builder.RegisterAssemblyTypes(assemblyThatContainsUseCases)
.Where(x => x.Name.EndsWith("InteractorProxy"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
现在,我有一个精简的界面
IGetFoobar
,可以将其注入到控制器操作中,并且装饰器也可以正常工作。
此外,我可以向生成的代理添加更多逻辑,例如验证。