[英]Using Autofac Registration middleware to resolve a version of the service
我想解决在运行时给出名称的命名注册。
背景故事 - 我用多个版本公开我的端点,例如:
https://localhost/myservice/api/v1/allcities
https://localhost/myservice/api/v2/allcities
...
controller 需要根据调用端点的版本调用不同版本的注入服务。
为了说明,我希望当我调用https://localhost/myservice/api/v1/blahblah
它使用MyServiceV1
执行,当我调用https://localhost/myservice/api/v2/blahblah
它使用MyServiceV2
执行
我正在使用 .NET Core 3.1 API 和Microsoft.AspNetCore.Mvc.Versioning
,Autofac 6。
我可以得到通过IHttpContextAccessor
调用的版本作为contextAccessor.HttpContext.GetRequestedApiVersion()
。 在这个问题中,我想专注于在运行时解决特定版本的服务。
我的想法是将服务注册为Named
(或键控,没关系)并使用注册中间件注册拦截解析过程并注入命名服务的正确版本。
代码:
public interface IMyService
{
string GetImplementationVersion();
}
[MyServiceVersioning("1.0")]
public class MyService1 : IMyService
{
public string GetImplementationVersion() => "Example 1.0";
}
[MyServiceVersioning("2.0")]
public class MyService2 : IMyService
{
public string GetImplementationVersion() => "Example 2.0";
}
public class MyMasterService
{
private IMyService _myService;
public MyMasterService(IMyService myService)
{
_myService = myService;
}
public string GetInjectedVersion() => _myService.GetImplementationVersion();
}
注册(为完整性而编辑)
// ...
builder.RegisterType<VersionService>().As<IVersionService>();
// next two lines registered using extension that's in the next code block example
builder.RegisterVersioned<MyServiceV1, IMyService>();
builder.RegisterVersioned<MyServiceV2, IMyService>();
builder.Register<MyMasterService>();
最后是问题所在的RegisterVersioned
扩展的实现:
public static class AutofacVersioningExtensions
{
public static IRegistrationBuilder<T, ConcreteReflectionActivatorData, SingleRegistrationStyle>
RegisterVersioned<T, TInterface>(this ContainerBuilder builder) where T: class
{
var versioningAttribute = typeof(T).GetCustomAttribute<MyServiceVersionAttribute>();
if (versioningAttribute == null)
{
// no versioning exists, do it simply
return builder.RegisterType<T>().As<TInterface>();
}
return builder.RegisterType<T>().As<TInterface>().Named<TInterface>(versioningAttribute.Version).ConfigurePipeline(p =>
{
p.Use(PipelinePhase.RegistrationPipelineStart, (context, next) =>
{
var invokedVersion = context.Resolve<IVersionService>().CurrentVersion;
// HERE --> in the next line issue is that we have obvious circular resolution
// + it only resolves the last registration
// (I could do Resolve<IEnumerable<TInterface>> to get all registrations but that's an overhead that I'd like to avoid if possible).
var resolved = context.ResolveNamed<TInterface>(invokedVersion);
if (resolved != null)
{
context.Instance = resolved;
}
else
{
next(context);
}
});
});
}
}
你有什么想法? 我的方法是否正确?
您似乎可以通过使用 lambda 注册来简化此操作。
builder.RegisterType<MyServiceV1>().Keyed<IMyService>("1.0");
builder.RegisterType<MyServiceV2>().Keyed<IMyService>("2.0");
builder.Register<MyMasterService>();
builder.Register(ctx =>
{
var version = ctx.Resolve<IHttpContextAccessor>().HttpContext.GetRequestedApiVersion();
return ctx.ResolveKeyed<IMyService>(version);
}).As<IMyService>().InstancePerLifetimeScope();
这样,当您解析未加密的IMyService
时,它将通过检测过程 go 并返回正确的加密版本。 该键控实例将在请求期间被缓存。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.