如何创建一个忽略Autofac中装饰器的键注册?

时间:2019-03-14 13:39:16

标签: c# .net dependency-injection autofac

我创建了一个朴素的缓存装饰器(ServiceDecorator),该装饰器装饰了在RegisterDecorator中注册的IService的实现。在某些情况下,我不需要装饰实例。 (实际情况是一个REST API,有时应缓存ServiceA,而有时不应该缓存。)

默认注入应该始终解析为服务装饰器,但是对于某些特殊情况,我想使用元数据,属性或其他信号来表示应该使用ServiceA。

有可能实现这一目标吗?在.NET 4.7.2上使用Autofac 4.9.1。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autofac;
using Autofac.Core;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            var cb = new ContainerBuilder();
            cb.RegisterType<ServiceA>().As<IService>();
            var context = cb.Build();
            var service = context.Resolve<IService>();
            //Output: "ServiceA"
            Console.WriteLine(service.DoStuff());

            cb = new ContainerBuilder();
            cb.RegisterType<ServiceA>().As<IService>();
            cb.RegisterDecorator<ServiceDecorator, IService>();
            context = cb.Build();
            service = context.Resolve<IService>();
            //Output: "ServiceDecorator"
            Console.WriteLine(service.DoStuff());

            cb = new ContainerBuilder();
            cb.RegisterType<ServiceA>().As<IService>();
            cb.RegisterDecorator<ServiceDecorator, IService>();
            cb.RegisterType<ServiceA>().Keyed<IService>("notdecorated");
            context = cb.Build();
            service = context.ResolveKeyed<IService>("notdecorated");
            //Output: "ServiceDecorator", but hoped for "ServiceA"
            Console.WriteLine(service.DoStuff());

            Console.ReadKey();
        }
    }


    public interface IService
    {
        string DoStuff();
    }

    public class ServiceA : IService
    {
        public string DoStuff()
        {
            return "ServiceA";
        }
    }

    public class ServiceDecorator : IService
    {
        private readonly IService _decoratedService;
        public ServiceDecorator(IService decoratedService)
        {
            _decoratedService = decoratedService;
        }
        public string DoStuff()
        {
            return "ServiceDecorator";
        }
    }

2 个答案:

答案 0 :(得分:1)

肯定不是最干净的方法,在一个更复杂的示例中,我不确定该方法的效果如何-但实际上可以选择在其上添加一个标志变量

public class ServiceA : IService
    {
        public bool withDecs { get; set; } = true;
        public string DoStuff()
        {
            return "ServiceA";
        }
    }

这意味着您可以执行以下操作:

cb.RegisterType<ServiceA>().As<IService>();
cb.RegisterDecorator<ServiceDecorator, IService>(x=> ((ServiceA)x.CurrentInstance).withDecs);
cb.RegisterType<ServiceA>().Keyed<IService>("notdecorated").WithProperty("withDecs",false);

尽管它将装饰器设置为以withDecs为条件,但它并不是很优雅,{ "name": "twig/twig", "version": "dev-mybranchname", "source": { "type": "git", "url": "https://github.com/myfork/Twig", "reference": <your_revision_number> }, ... } 是在键控上设置的属性,因此不会触发装饰器

答案 1 :(得分:1)

  

默认注入应该始终解析为服务装饰器,但是对于某些特殊情况,我想使用元数据,属性或其他信号来表示应该使用ServiceA。

我不会走这条路线,因为这意味着在构建对象图期间需要运行时数据。 This is a code smell,至少可以说。

相反,希望在执行应用程序时对象图的结构保持不变。相反,如果某些行为需要应用于某些请求,而在同一应用程序的其他请求中应将其省略,则最好在装饰器本身中构建此行为,或者创建一个额外的服务以允许在无行为者之间进行切换。 op和运行时的实际行为(基于现有的运行时数据)。

但是,有效的方法是根据装饰器提供的服务以及其他静态元数据,有条件地应用装饰器。例如,您可以使用ServiceA属性标记[Cache]并使用ServiceB属性标记[NoCache]并相应地应用装饰器。请注意,在这种情况下,图的结构在运行时不会更改,因为ServiceA将始终被修饰,而ServiceB将永远不会被修饰。但是,我不确定要在Autofac中有条件地应用装饰器。