Reputation: 32428
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 new
ed 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
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
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
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
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