C# 如何在autofac中混合装饰器?

C# 如何在autofac中混合装饰器?,c#,.net,dependency-injection,ioc-container,autofac,C#,.net,Dependency Injection,Ioc Container,Autofac,我希望能够与Autofac混合搭配装饰师 例如,假设我有一个IRepository接口,由Repository类实现。 我可以有以下装饰器:RepositoryLocalCache、RepositoryDistributedCache、RepositorySecurity、RepositoryLogging…,您可以得到IDEA 基于配置设置,我想用所需的装饰器装饰基本实现。可以是无装饰,一个或多个装饰 我熟悉按固定顺序注册一个或一系列装饰器的语法,但如何使其动态化?在Autofac中有条件地应

我希望能够与Autofac混合搭配装饰师

例如,假设我有一个IRepository接口,由Repository类实现。
我可以有以下装饰器:RepositoryLocalCache、RepositoryDistributedCache、RepositorySecurity、RepositoryLogging…,您可以得到IDEA

基于配置设置,我想用所需的装饰器装饰基本实现。可以是无装饰,一个或多个装饰


我熟悉按固定顺序注册一个或一系列装饰器的语法,但如何使其动态化?

在Autofac中有条件地应用装饰器实际上相当麻烦。让我们分两步来做。首先,让我们编写无条件应用这些装饰器的代码:

var builder=newcontainerbuilder();
builder.RegisterType()。命名为(“实现者”);
builder.RegisterDecorator(
(c,内部)=>新的RepositoryLocalCache(内部),
fromKey:“实现者”,
托克伊:“装饰师1”);
builder.RegisterDecorator(
(c,内部)=>新的RepositoryDistributedCache(内部),
fromKey:“decorator1”,
托克伊:“装饰师2”;
builder.RegisterDecorator(
(c,内部)=>新回购证券(内部),
fromKey:“decorator2”,
托克伊:“装饰师3”);
builder.RegisterDecorator(
(c,内部)=>新的存储日志记录(内部),
fromKey:“decorator3”,
toKey:null);
在Autofac中应用装饰器是通过使用键控注册(toKey)注册具有相同服务类型的多个组件(
IRepository
,在您的情况下)并使用
fromKey
)将这些注册指向另一个组件来完成的。最外层的装饰器应该是无键的,因为默认情况下Autofac将始终为您解析无键注册

这些密钥注册是Autofac在这方面的最大弱点,因为这些密钥使装饰程序与下一个进行硬连接。如果您只是将
RepositoryDistributedCache
包装在
If
-块中,配置将中断,因为
RepositorySecurity
现在将指向不存在的注册

此问题的解决方案是动态生成密钥,并添加一个额外的“虚拟”无键装饰器,该装饰器不按条件应用:

int计数器=0;
Func getCurrentKey=>()=>计数器;
Func getNextKey=>()=>++计数器;
var builder=new ContainerBuilder();
builder.RegisterType().Named(getCurrentKey());
if(config.UseRepositoryLocalCache){
builder.RegisterDecorator(
(c,内部)=>新的RepositoryLocalCache(内部),
fromKey:getCurrentKey(),toKey:getNextKey();
}
if(config.UseRepositoryDistributedCache){
builder.RegisterDecorator(
(c,内部)=>新的RepositoryDistributedCache(内部),
fromKey:getCurrentKey(),toKey:getNextKey();
}
如果(config.UseRepositorySecurity){
builder.RegisterDecorator(
(c,内部)=>新回购证券(内部),
fromKey:getCurrentKey(),toKey:getNextKey();
}
如果(config.UseRepositoryLogging){
builder.RegisterDecorator(
(c,内部)=>新的存储日志记录(内部),
fromKey:getCurrentKey(),toKey:getNextKey();
}
//无键装饰器,只是通过调用。
builder.RegisterDecorator(
(c,内部)=>新的库存传递(内部),
fromKey:getCurrentKey(),toKey:null);
在这里,我们使用
计数器
变量并创建
getNextKey
getCurrentKey
lambda,使配置更容易。再次注意最后一个
RepositoryPassThrough
decorator。这个装饰者应该简单地给它的装饰者打电话,而不做其他事情。有了这个额外的装饰器,完成配置就容易多了;否则就很难决定最后一个装饰师是什么


使用Autofac使这一点更加困难的一个原因是缺乏对非通用装饰器的自动布线支持。据我所知,这是Autofac API中唯一不支持自动连接(即让容器确定要注入哪些构造函数参数)的部分。如果注册可以使用类型而不是委托来完成,那么会容易得多,因为在这种情况下,我们可以构建要应用的装饰器的初始列表,而不仅仅是迭代列表。尽管如此,我们仍然需要处理这些键控注册。

正如Steven在上面指出的,Autofac中的
RegisterDecorator
方法并不是专门为这种情况设计的,而且使用起来相当笨拙。它们是为一些使用常规Autofac注册很难实现的情况而构建的,“本机”注册方式更干净

例如,
IFoo
是服务,
Impl
是具体的(例如存储库)实现

interface IFoo { }

class Impl : IFoo { }

class DecoratorA : IFoo
{
    public DecoratorA(IFoo decorated) { }
}

class DecoratorB : IFoo
{
    public DecoratorB(IFoo decorated) { }
}
首先,使用其具体类型注册所有组件:

var builder = new ContainerBuilder();

builder.RegisterType<Impl>();
builder.RegisterType<DecoratorA>();
builder.RegisterType<DecoratorB>();

如果你使用泛型,事情就更棘手了,因为你没有提到它们,我就不谈了,但如果你是泛型,请发表另一个问题。

我刚刚偶然发现了这条线索,并想与大家分享我是如何做到这一点的:

不久前,我写了两个扩展方法来简化这个问题。这些方法与@Steven的答案类似,因为它们为动态实现创建名称。但是,它们不使用
RegisterDecorator
,这意味着不需要“传递”实现

interface IFoo { }

class Impl : IFoo { }

class DecoratorA : IFoo
{
    public DecoratorA(IFoo decorated) { }
}

class DecoratorB : IFoo
{
    public DecoratorB(IFoo decorated) { }
}
这些方法可以这样使用:

builder.RegisterDecorated<EnquiryService, IEnquiryService>();

builder.RegisterDecorator<ServiceBusEnquiryServiceDecorator, IEnquiryService>();
builder.RegisterDecorator<EmailNotificationDecorator, IEnquiryService>();
public static class ContainerBuilderExtensions
{
    private static readonly IDictionary<Type, string> _implementationNames = new ConcurrentDictionary<Type, string>();

    public static void RegisterDecorated<T, TImplements>(this ContainerBuilder builder) where T : TImplements
    {
        builder.RegisterType<T>()
            .As<TImplements>()
            .Named<TImplements>(GetNameOf<TImplements>());
    }

    public static void RegisterDecorator<T, TImplements>(this ContainerBuilder builder) where T : TImplements
    {
        var nameOfServiceToDecorate = GetOutermostNameOf<TImplements>();

        builder.RegisterType<T>();

        builder.Register(c =>
        {
            var impl = c.ResolveNamed<TImplements>(nameOfServiceToDecorate);

            impl = c.Resolve<T>(TypedParameter.From(impl));

            return impl;
        })
            .As<TImplements>()
            .Named<TImplements>(GetNameOf<TImplements>());
    }

    private static string GetNameOf<T>()
    {
        var type = typeof(T);
        var name = type.FullName + Guid.NewGuid();

        _implementationNames[type] = name;

        return name;
    }

    private static string GetOutermostNameOf<T>()
    {
        var type = typeof(T);

        if (!_implementationNames.ContainsKey(type))
        {
            throw new Exception("Cannot call RegisterDecorator for an implementation that is not decorated. Ensure that you have called RegisterDecorated for this type before calling RegisterDecorator.");
        }

        return _implementationNames[typeof(T)];
    }
}
上面的片段尽可能简单,但它对我很有用。当然,如果您有更复杂的需求,也可以进行更改


这个概念构成了我对Orchard CMS的贡献的基础,它增加了装饰功能:。

这不是最好的API-Autofac的超动态特性,所以当面对这些问题时
public static class ContainerBuilderExtensions
{
    private static readonly IDictionary<Type, string> _implementationNames = new ConcurrentDictionary<Type, string>();

    public static void RegisterDecorated<T, TImplements>(this ContainerBuilder builder) where T : TImplements
    {
        builder.RegisterType<T>()
            .As<TImplements>()
            .Named<TImplements>(GetNameOf<TImplements>());
    }

    public static void RegisterDecorator<T, TImplements>(this ContainerBuilder builder) where T : TImplements
    {
        var nameOfServiceToDecorate = GetOutermostNameOf<TImplements>();

        builder.RegisterType<T>();

        builder.Register(c =>
        {
            var impl = c.ResolveNamed<TImplements>(nameOfServiceToDecorate);

            impl = c.Resolve<T>(TypedParameter.From(impl));

            return impl;
        })
            .As<TImplements>()
            .Named<TImplements>(GetNameOf<TImplements>());
    }

    private static string GetNameOf<T>()
    {
        var type = typeof(T);
        var name = type.FullName + Guid.NewGuid();

        _implementationNames[type] = name;

        return name;
    }

    private static string GetOutermostNameOf<T>()
    {
        var type = typeof(T);

        if (!_implementationNames.ContainsKey(type))
        {
            throw new Exception("Cannot call RegisterDecorator for an implementation that is not decorated. Ensure that you have called RegisterDecorated for this type before calling RegisterDecorator.");
        }

        return _implementationNames[typeof(T)];
    }
}