简体   繁体   English

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

[英]How can I create a keyed registration which ignores decorators in Autofac?

I have created a naive cache decorator (ServiceDecorator) which decorates implementations of IService, registered with RegisterDecorator. 我创建了一个朴素的缓存装饰器(ServiceDecorator),该装饰器装饰了注册到RegisterDecorator的IService的实现。 In some cases, I do not want a decorated instance. 在某些情况下,我不需要装饰实例。 (Actual scenario is a REST API where ServiceA sometimes should be cached, and other times not.) (实际情况是一个REST API,其中有时应缓存ServiceA,而有时不应该缓存。)

The default injection should always resolve to the service decorator, but for some special cases I would like to use meta data, attributes or other to signal that ServiceA should be used. 默认注入应该始终解析为服务装饰器,但是对于某些特殊情况,我想使用元数据,属性或其他信号来表示应该使用ServiceA。

Is it possible to achieve this? 有可能实现这一目标吗? Using Autofac 4.9.1 on .NET 4.7.2. 在.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";
        }
    }

Certainly not the cleanest of ways, and in a more complex example im not sure how well it would work - but there is an option of adding a flag variable on it essentially 当然不是最干净的方法,在一个更复杂的示例中,我不确定该方法的效果如何-但实际上可以选择在其上添加一个标志变量

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

this means you can do something like: 这意味着您可以执行以下操作:

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

its not exactly elegant though it sets up the decorator to be conditional on the withDecs which is property set on the keyed so wont trigger the decorator 尽管它将装饰器设置为以withDecs为条件,但它并不十分优雅, withDecs是在键控上设置的属性,因此不会触发装饰器

The default injection should always resolve to the service decorator, but for some special cases I would like to use meta data, attributes or other to signal that ServiceA should be used. 默认注入应该始终解析为服务装饰器,但是对于某些特殊情况,我想使用元数据,属性或其他信号来表示应该使用ServiceA。

I wouldn't take this route, because it means runtime data is required during the construction of your object graphs. 我不会走这条路线,因为这意味着在构建对象图期间需要运行时数据。 This is a code smell , to say the least. 至少可以说, 这是一种代码气味

Instead, prefer the structure of your object graphs to be unchanged during the execution of your application. 相反,您希望对象图的结构在应用程序执行期间保持不变。 Instead, in case some behavior needs to be applied for some requests, while it should be omitted during other requests of the same application, prefer building this behavior in the decorator itself, or, create an extra service that allows to switch between the no-op and the actual behavior at runtime, based on existing runtime data. 相反,如果某些行为需要应用于某些请求,而在同一应用程序的其他请求中应将其省略,则最好在装饰器本身中构建此行为,或者创建一个额外的服务以允许在无行为者之间进行切换。 op和运行时的实际行为(基于现有的运行时数据)。

A valid approach, however, would be to apply a decorator conditionally, based on the services it decorators, and other static metadata. 但是,有效的方法是根据装饰器提供的服务和其他静态元数据有条件地应用装饰器。 For instance, you could mark ServiceA with a [Cache] attribute and ServiceB with a [NoCache] attribute and apply the decorator accordingly. 例如,您可以将ServiceA标记为[Cache]ServiceB标记为[NoCache]并相应地应用装饰器。 Note that in this case the structure of the graph will not change at runtime, b ecause ServiceA will always be decorated, while ServiceB will never be. 请注意,在这种情况下,图的结构在运行时不会更改,因为ServiceA将始终被修饰,而ServiceB将永远不会被修饰。 I'm unsure, however, to apply decorator conditionally in Autofac. 但是,我不确定在Autofac中有条件地应用装饰器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM