我创建了一个朴素的缓存装饰器(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";
}
}
答案 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中有条件地应用装饰器。