DependencyResolutionException Circular component dependency detected: How to use Autofac to inject decorator?

3.5k views Asked by At

I have an interface called ILastActivityUpdator as follows:

public interface ILastActivityUpdator
{
    void UpdateLastActivity(int userId);
}

Besides that I have two classes that implement this interface, being LastActivityUpdator and a decorator AnonymousUserLastActivityUpdator. The latter one takes an ICookieProvider as extra argument and uses this in the method implementation. I.e.,

public LastActivityUpdator(IUnitOfWork unitOfWork) 
    : base(unitOfWork)
{ }

public void UpdateLastActivity(int userId)
{
    // Do some stuff.
}

And the decorator:

public AnonymousUserLastActivityUpdator(
    IUnitOfWork unitOfWork,
    ICookieProvider cookieProvider,
    ILastActivityUpdator lastActivityUpdator) 
    : base(unitOfWork)
{
    this.lastActivityUpdator = lastActivityUpdator;
    this.cookieProvider = cookieProvider;
}

public void UpdateLastActivity(int userId)
{
    lastActivityUpdator.UpdateLastActivity(userId);

    cookieProvider.SetCookie(); 
}

I want to inject the AnonymousUserLastActivityUpdator in AnonymousUserService and the LastActivityUpdator in UserService.

To do this, my RegisterDependencies method in IoCConfig contains the following registerings taken from the Autofac FAQ Decorator section.

builder.RegisterType() .Keyed(AuthenticatedStatus.Anonymous) .InstancePerRequest(); builder.RegisterType() .Keyed(AuthenticatedStatus.Authenticated) .InstancePerRequest();

builder.RegisterType<LastActivityUpdator>()
    .Named<ILastActivityUpdator>("lastActivityUpdator");
builder.RegisterType<AnonymousUserLastActivityUpdator>()
    .Named<ILastActivityUpdator>("lastActivityUpdator");

// Then register the decorator. The decorator uses the
// named registrations to get the items to wrap.
builder.RegisterDecorator<ILastActivityUpdator>(
    (c, p, inner) => new AnonymousUserLastActivityUpdator(
        c.Resolve<IOogstplannerUnitOfWork>(), 
        c.Resolve<ICookieProvider>(),
        inner),
    fromKey: "lastActivityUpdator");

Unfortunately, when I run the code I get the error:

Autofac.Core.DependencyResolutionException Circular component dependency detected:
Services.AnonymousUserService ->
Services.AnonymousUserService ->
Services.AnonymousUserLastActivityUpdator ->
Services.AnonymousUserLastActivityUpdator ->
Services.AnonymousUserLastActivityUpdator.

Furthermore, I don't see why magically the right implementations would be injected. Does anyone know how to fix this?

1

There are 1 answers

2
Cyril Durand On BEST ANSWER

You have this error because the C# compiler doesn't know which overload of the RegisterDecorator method to take.

If you force it :

builder.RegisterDecorator<ILastActivityUpdator>(
    new Func<IComponentContext, ILastActivityUpdator, ILastActivityUpdator>(
        (c, inner) => new AnonymousUserLastActivityUpdator(inner)
    ),
    fromKey: "lastActivityUpdator");

You will have a new error :

AnonymousUserLastActivityUpdator does not contain a constructor that takes 1 arguments

and this error is caused by this line :

new AnonymousUserLastActivityUpdator(inner)

to resolve this error you have to change the way you create instance of AnonymousUserLastActivityUpdator

for example :

// only the type being decorated should be registered here
builder.RegisterType<LastActivityUpdator>()
       .Named<ILastActivityUpdator>("lastActivityUpdator");

builder.RegisterDecorator<ILastActivityUpdator>(
    (c, inner) => new AnonymousUserLastActivityUpdator(c.Resolve<IUnitOfWork>(), 
                                                       c.Resolve<ICookieProvider>(), 
                                                       inner),
    fromKey: "lastActivityUpdator")
       .As<ILastActivityUpdator>(); 

or even better :

builder.RegisterType<LastActivityUpdator>()
       .Named<ILastActivityUpdator>("lastActivityUpdator");
builder.RegisterType<AnonymousUserLastActivityUpdator>()
       .Named<ILastActivityUpdator>("anonymousLastActivityUpdator");

builder.RegisterDecorator<ILastActivityUpdator>(
    (c, inner) => c.ResolveNamed<ILastActivyUpdator>("anonymousLastActivityUpdator", 
                                                     TypedParameter.From(inner)),
    fromKey: "lastActivityUpdator")
       .As<ILastActivityUpdator>();