簡體   English   中英

使用 Autofac Registration 中間件解析服務的一個版本

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM