[英]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.