Property / method injection using Autofac in filter attributes

749 views Asked by At

Trying to use autofac for dependency injection by property.

The instance is always null and there is no dependency injected. Below is class where the property needs to be injected.

public class UserAccount
{
    public IAccountService AccountService { get; set; }

    public string Message()
    {
        return AccountService.Message();
    }
}

I have tried three different ways to inject the property but none was successful

Method 1 :

builder.Register(c => {
                var result = new UserAccount();
                var dep = c.Resolve<IAccountService>();
                result.SetDependency(dep);
                return result;
            });

Method 2 :

builder.RegisterType<UserAccount>().PropertiesAutowired();

Method 3 :

builder.Register(c => new UserAccount { AccountService = c.Resolve<IAccountService>()});

PS : Method injection of above is welcomed.

2

There are 2 answers

5
Steven On

You should prevent letting your container create data-centric objects, such as your UserAccount entity. This leads to complicated scenarios, such as the one you are in now.

In general, your DI Container should resolve only components—those are the classes in your system that contain the application's behavior, without having any interesting state. Those types of classes are typically long lived, or at least, longer lived than data-centric objects.

Data-centric objects, like entities, can best be created by hand. Not doing so would either lead to entities with big constructors, which easily causes the constructor over-injection code smell. As remedy, you might fall back on using Property Injection, but this causes a code smell of its own, caused Temporal Coupling.

Instead, a better solution is to:

  1. Create entities by hand, opposed to using a DI Container
  2. Supply dependencies to the entity using Method Injection, opposed to using Property Injection

With Method Injection, your UserAccount would as follows:

// This answer assumes that this class is an domain entity.
public class UserAccount
{
    public Guid Id { get; set; }
    public byte[] PasswordHash { get; set; }

    public string Message(IAccountService accountService)
    {
        if (accountService == null)
            throw new ArgumentNullException(nameof(accountService));

        return accountService.Message();
    }
}

This does move the responsibility of supplying the dependency from the Composition Root to the entity's direct consumer, though. But as discussed above, this is intentional, as the Composition Root in general, and a DI Container in particular, should not be responsible of creating entities and other data-centric, short-lived objects.

This does mean, however, that UserAccount's direct consumer should inject that dependency, and with that, know about existence of the dependency. But as that consumer would be a behavior-centric class, the typical solution is to use Constructor Injection at that stage:

public class UserService : IUserService
{
    private readonly IAccountService accountService;
    private readonly IUserAccountRepository repo;

    public UserService(IAccountService accountService, IUserAccountRepository repo)
    {
        this.accountService = accountService;
        this.repo = repo
    }

    public void DoSomething(Guid id)
    {
        UserAccount entity = this.repo.GetById(id);
        var message = entity.Message(this.accountService);
    }
}
0
Phillip Ngan On

Using method 3, you need to register AccountService, i.e.

        builder.RegisterType<AccountService>().As<IAccountService>();
        builder.Register(c => new UserAccount { AccountService = c.Resolve<IAccountService>()});

And when you use UserAccount, make sure it is created using Autofac.