How to mix decorators in autofac?

I would like to be able to mix and match decorators with Autofac.

For example, let's say I have an IRepository interface implemented by the Repository class.
I could have the following decorators: RepositoryLocalCache, RepositoryDistributedCache, RepositorySecurity, RepositoryLogging ... you get the idea.

Based on the configuration settings, I would like to decorate the basic implementation with the necessary decorators. It can be none, one or several decorators.

I am familiar with the syntax for registering a single decorator or chaining them in a fixed order, but how can I make this dynamic?

+4
source share
3 answers

As Stephen points out above, the methods RegisterDecoratorin Autofac are not really intended for this scenario and are rather inconvenient to use. They were created for some situations that are difficult to implement with regular Autofac registrations - a “native” way to make this much cleaner.

As an example IFoo, this is a service, and Impla specific (e.g., repository) implementation.

interface IFoo { }

class Impl : IFoo { }

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

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

First, register all components using their specific types:

var builder = new ContainerBuilder();

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

Lambda registration is fine too, just make sure they don't use As<IFoo>().

Now the shell that combines them to provide a fully configured service:

bool useA = true, useB = false;

builder.Register(c =>
{
    IFoo result = c.Resolve<Impl>();

    if (useA)
        result = c.Resolve<DecoratorA>(TypedParameter.From(result));

    if (useB)
        result = c.Resolve<DecoratorB>(TypedParameter.From(result));

    return result;
}).As<IFoo>();

useA useB .

, ( ) IFoo, .

using (var container = builder.Build())
{
    var foo = container.Resolve<IFoo>();

, , , , , , .

+12

Autofac. . , :

var builder = new ContainerBuilder();

builder.RegisterType<Repository>().Named<IRepository>("implementor");

builder.RegisterDecorator<IRepository>(
    (c, inner) => new RepositoryLocalCache(inner),
    fromKey: "implementor",
    toKey: "decorator1");

builder.RegisterDecorator<IRepository>(
    (c, inner) => new RepositoryDistributedCache(inner),
    fromKey: "decorator1",
    toKey: "decorator2");

builder.RegisterDecorator<IRepository>(
    (c, inner) => new RepositorySecurity(inner),
    fromKey: "decorator2",
    toKey: "decorator3");

builder.RegisterDecorator<IRepository>(
    (c, inner) => new RepositoryLogging(inner),
    fromKey: "decorator3",
    toKey: null);

Autofac (IRepository ) (toKey) fromKey). , Autofac .

- Autofac , , - . RepositoryDistributedCache if -block, , RepositorySecurity , .

, 'dummy' , :

int counter = 0;
Func<object> getCurrentKey => () => counter;
Func<object> getNextKey => () => ++counter;       
var builder = new ContainerBuilder();
builder.RegisterType<Repository>().Named<IRepository>(getCurrentKey());

if (config.UseRepositoryLocalCache) {
    builder.RegisterDecorator<IRepository>(
        (c, inner) => new RepositoryLocalCache(inner),
        fromKey: getCurrentKey(), toKey: getNextKey());
}

if (config.UseRepositoryDistributedCache) {
    builder.RegisterDecorator<IRepository>(
        (c, inner) => new RepositoryDistributedCache(inner),
        fromKey: getCurrentKey(), toKey: getNextKey());
}

if (config.UseRepositorySecurity) {    
    builder.RegisterDecorator<IRepository>(
        (c, inner) => new RepositorySecurity(inner),
        fromKey: getCurrentKey(), toKey: getNextKey());
}

if (config.UseRepositoryLogging) {    
    builder.RegisterDecorator<IRepository>(
        (c, inner) => new RepositoryLogging(inner),
        fromKey: getCurrentKey(), toKey: getNextKey());
}

// The keyless decorator that just passes the call through.
builder.RegisterDecorator<IRepository>(
    (c, inner) => new RepositoryPassThrough(inner),
    fromKey: getCurrentKey(), toKey: null);    

counter lambdas getNextKey getCurrentKey, . RepositoryPassThrough. . ; , .

, Autofac, . , API Autofac, (.. , ) . , , , . , .

+5

, :

, . @Steven , " ". RegisterDecorator, , "pass through".

:

builder.RegisterDecorated<EnquiryService, IEnquiryService>();

builder.RegisterDecorator<ServiceBusEnquiryServiceDecorator, IEnquiryService>();
builder.RegisterDecorator<EmailNotificationDecorator, IEnquiryService>();

:

  • .
  • , , , .
  • RegisterDecorated RegisterDecorator , . , 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)];
    }
}

, , . , , .

This concept formed the basis of my contribution to Orchard CMS, which added decorator capabilities: https://github.com/OrchardCMS/Orchard/pull/6233 .

0
source

Source: https://habr.com/ru/post/1538844/


All Articles