8

I have two classes that take a ILastActivityUpdator as a constructor parameter: UserService and AnonymousUserService.

public AnonymousUserService(ILastActivityUpdator lastActivityUpdator)
{
    if (lastActivityUpdator == null)
    {
        throw new ArgumentNullException("lastActivityUpdator");
    }
    this.lastActivityUpdator = lastActivityUpdator;
}

And similar as above for UserService:

public UserService(ILastActivityUpdator lastActivityUpdator)
{
    if (lastActivityUpdator == null)
    {
        throw new ArgumentNullException("lastActivityUpdator");
    }
    this.lastActivityUpdator = lastActivityUpdator;
}

ILastActivityUpdator interface has one method: UpdateLastActivity(int userId). There are two implementations of the interface, a LastActivityUpdator and a decorator called AnonymousUserLastActivityUpdator which inherits from LastActivityUpdator and adds some extra functionality to the method, like so:

public class AnonymousUserLastActivityUpdator 
    : LastActivityUpdator, IAnonymousUserLastActivityUpdator
{
    public AnonymousUserLastActivityUpdator()
    { }

    public override void UpdateLastActivity(int userId)
    {
        base.UpdateLastActivity(userId);

        // Extra functionality
    }
}

I now want use Autofac to wire up the AnonymousUserService with the AnonymousUserLastActivityUpdator and the UserService with the LastActivityUpdator.

What I tried is to add an interface for the decorator that derives from the base interface like so:

public interface IAnonymousUserLastActivityUpdator : ILastActivityUpdator
{ }

Then I thought I could use the IAnonymousUserLastActivityUpdator in the AnonymousUserService constructor and everything would be autowired properly.

Unfortunately it just always uses the first implementation, being IAnonymousUserLastActivityUpdator since it is registered earlier (alphabetical order).

How can I accomplish that the AnonymousUserService gets the AnonymousUserLastActivityUpdator injected and the UserService the LastActivityUpdator?

user2609980
  • 10,264
  • 15
  • 74
  • 143
  • [This question is actually an FAQ on the Autofac doc site.](http://docs.autofac.org/en/latest/faq/select-by-context.html) – Travis Illig Jun 15 '15 at 13:51

2 Answers2

7

Autofac is nicely documented and it looks like you can find what you are after here. From what I can tell, if you have registered your updators with

builder.RegisterType<LastActivityUpdator>();
builder.RegisterType<AnonymousUserLastActivityUpdator>();

then you should be able to register your services with

builder.Register(c => new UserService(c.Resolve<LastActivityUpdator>()));
builder.Register(c => new AnonymousUserService(c.Resolve<AnonymousUserLastActivityUpdator>()));

or

builder.RegisterType<UserService>().WithParameter(
    (p, c) => p.ParameterType == typeof(ILastActivityUpdator),
    (p, c) => c.Resolve<LastActivityUpdator>());

builder.RegisterType<AnonymousUserService>().WithParameter(
    (p, c) => p.ParameterType == typeof(ILastActivityUpdator),
    (p, c) => c.Resolve<AnonymousUserLastActivityUpdator>());

Then when you resolve UserService or AnonymousUserService from the container, they will get the correct dependencies.

As an aside, if an interface is injected into a class, then that class should function correctly with all implementations of that interface (LSP). From the class names, it looks like AnonymousUserService only works with AnonymousUserLastActivityUpdator and not any implementation of ILastActivityUpdator. If that is the case, then it might be appropriate to introduce a different abstraction (like IAnonymousUserLastActivityUpdator) as you suggested.

Matt Cole
  • 2,491
  • 17
  • 21
  • Hi Matt, thanks a lot. It only functions with a `IAnonymousUserLastActivityUpdator` so you are right that it should depend on that. I think my mistake was letting the `IAnonymousUserLastActivityUpdator` derive from `ILastActivityUpdator`, therefore the first matching was selected. I will try not letting it inherit this evening. Thanks. – user2609980 Jun 15 '15 at 09:49
  • That sounds correct however you shouldn't change you design based on your IoC. If it makes sense for `IAnonymousUserLastActivityUpdator` to inherit from `ILastActivityUpdator` then it should, and you can set up your bindings as above. Good luck with it :) – Matt Cole Jun 15 '15 at 10:04
  • the `builder.Register(c => new UserService(c => c.Resolve()));` syntax is not correct. "A local variable named c cannot be declared in this scope because it would give a different meaning to c, which is already used in `parent or current' scope to denote something else. – user2609980 Jun 17 '15 at 20:46
  • 1
    There shouldn't have been a second lambda. I've corrected it – Matt Cole Jun 18 '15 at 09:16
  • Matt, I tried implementing your suggestion after refactoring to a decorator pattern, and I always get the `AnonymousUserLastActivityUpdator` injected, even when I want it to be a `LastActivityUpdator`. Maybe you can take a look at http://stackoverflow.com/questions/30926589/how-to-use-autofac-to-inject-decorator-in-one-constructor-and-decorated-class-in. Thanks. – user2609980 Jun 18 '15 at 22:17
  • 1
    It seems to be working for me. I've put together a quick [example](https://gist.github.com/mattwcole/65a8d5a1adb8179932ba) – Matt Cole Jun 18 '15 at 22:37
1

As stated in the previous response, in this case you're breaking the Liskov principle. In fact, your consumer classes depend on diferent interfaces implementations. Even if the interface are excatly the same one, the functionality it's not. And you need to reflect that:

  1. in each consumer class, depend on the right interface
  2. register each implementation as the appropriate interface

That you can derive one implementation from the other is completely irrelevant from the DI point of view: it doesn't matter if the implementations are the same class, or are completely independent, or one derives from the other, as in this case.

For more details, check Autofac's Services vs Components docs. You'll see that all the above options are possible.

Taking this into account:

  1. In each consumer class constructor, specify the right interface dependency:
    public AnonymousUserService(IAnonymousUserLastActivityUpdator lastActivityUpdator)
    public UserService(ILastActivityUpdator lastActivityUpdator)
  1. And register each implementation as the right interface:
    builder.RegisterType<LastActivityUpdator>()
      .As<ILastActivityUpdator>();
    builder.RegisterType<AnonymousUserLastActivityUpdator>()
      .As<IAnonymousUserLastActivityUpdator>;

In this way, each consumer class will automatically get injected the right implementation.

Hans Kesting
  • 38,117
  • 9
  • 79
  • 111
JotaBe
  • 38,030
  • 8
  • 98
  • 117