George Duckett
George Duckett

Reputation: 32428

Inversion of Control / Dependency Injection and conversion operators

I have a UserModel class used by many MVC apps that I'm trying to convert to use IoC:

public class UserModel
{
    public string Login { get; set; }

    [DisplayName("Display Name")]
    public string DisplayName { get; set; }

    public static explicit operator UserModel(string userLogin)
    {
        IKernel IoC; // Where do I get this from?
        var ActiveDirRepo = IoC.Get<IActiveDirectoryRepository>();

        var User = ActiveDirRepo.FindById<ActiveDirectoryUser>(userLogin.ToUpper());

        return new UserModel()
        {
            Login = userLogin,
            DisplayName = User == null ? userLogin.ToUpper() : User.Name
        };
    }
}

It has an explicit conversion operator from string to UserModel. Previously I just newed an ActiveDirectoryRepository and used it, but I want to start using IoC/DI.

The problem I've got now is, how do I reference my IoC container (IKernel)?

I can't inject it into UserModel from the outside, as the operator is what is creating it.

Upvotes: 1

Views: 755

Answers (4)

Rafal
Rafal

Reputation: 12619

You can use service locator pattern form Microsoft.Practices.ServiceLocation I would be supprised if ninject would not provide implementation. If it does not it's straight forward to implement.

Regardless that you can do what you want with this code I would strongly recommend you to not implement explicit conversion operator as you did. It is a bad practice.

Upvotes: 1

Adam Kewley
Adam Kewley

Reputation: 1234

Ideologically, your IoC container should only exist in the composite root of your application. With that in mind, it's a matter of figuring out your classes dependencies and injecting them via the constructor (if you cant do constructor DI then many IoC containers offer alternative methods of injection).

Here, you need access to IActiveDirectoryRepository in order to create a UserModel. However, your creation method, explicit operator UserModel is a static method with no access to instance variables. Unfortunately, that restricts your ability to use dependency injection to leverage IoC so here's some ideas:

  • Straightforward: Use the service locator pattern. You use a service locator to get an instance of IActiveDirectoryRepository. This would directly adapt into your current implementation nicely (swap the kernel lines to a service locator). However, there are drawbacks to this pattern; most notably, the fact you're internalizing your IActiveDirectoryRepository source which would make unit testing less effective.

  • Traditional: Make your UserModel creator into a factory that accepts IActiveDirectoryRepository via it's constructor. Then, any UserModel's created by the factory use the supplied IActiveDirectoryRepository.

    public sealed class UserFactory
    {
        private readonly IActiveDirecotryRepository repository;
        public UserFactory(IActiveDirectoryRepository repository)
        {
            this.repository = repository;
        }
    
        public UserModel CreateNewUser(string userLogin)
        {
            var User = repository.FindById<ActiveDirectoryUser>(userLogin.ToUpper());
            return new UserModel()
            {
                Login = userLogin,
                DisplayName = User == null ? userLogin.ToUpper() : User.Name
            } 
        }
    }
    
  • Fancy: Create a closure over IActiveDirectoryRepository and pass the creator around as first-class function.

    static Func<string, UserModel> CreateFactory(IActiveDirectoryRepository repository)
    {
        return (userLogin) => 
        {
            var User = repository.FindById<ActiveDirectoryUser>(userLogin.ToUpper());
            return new UserModel()
            {
                Login = userLogin,
                DisplayName = User == null ? userLogin.ToUpper() : User.Name
            }   
        }
    }
    

Upvotes: 1

Cyril Gandon
Cyril Gandon

Reputation: 17048

This kind of conversion are suppose to be simple, this is not suppose to need external dependencies. If you need to inject some sort of dependencies, then forgot the conversion, there is no solutions.

Simple enough, pass the IKernel to a build method :

public static UserModel Build(string userLogin, IKernel IoC)
{
    var ActiveDirRepo = IoC.Get<IActiveDirectoryRepository>();

    var User = ActiveDirRepo.FindById<ActiveDirectoryUser>(userLogin.ToUpper());

    return new UserModel()
    {
        Login = userLogin,
        DisplayName = User == null ? userLogin.ToUpper() : User.Name
    };
}

Then you can ask yourself if it make sense that this method belong to the IKernel interface :

interface IKernel
{
    UserModel Build(string userLogin);
}

Upvotes: 1

Dennis
Dennis

Reputation: 37770

There's no magic in IoC. Sometimes you still need a starting point, from which you can access application's IoC container, something like this:

var ioc = Application.Instance.Container.Get<IActiveDirectoryRepository>();

You don't need to access IoC container from everywhere, because objects, that were created via container already have their dependencies injected, but at least some root objects need to gain access to container.

Upvotes: 1

Related Questions