繁体   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