3

Using an IOC-container like Unity,AutoFac or others you have to Register and Resolve the IInterface to get the instance. This you do in the app class the root of all.

After doing the Register/Resolve stuff I am creating my MainController and pass them ALL resolved Services like:

protected void Application_Start(object sender, EventArgs e)
{
    var builder = new ContainerBuilder();

    builder.Register<IUserService1, UserService1>();
    builder.Register<IUserService2, UserService2>();
    builder.Register<IUserService3, UserService3>();
            builder.Register<IAnotherService, AnotherService>();
    // And many more Services...

    _container = builder.Build();

    var userService1 = _container.Resolve<IUserService1>();
    var userService2 = _container.Resolve<IUserService2>();
    var userService3 = _container.Resolve<IUserService3>();
var anotherService = _container.Resolve<IAnotherService>();     

    var vm = new MainController(userService1,userService2,userService3,anotherService)
}

public class MainController
{    
    private UserController1 _userVM1;
    private UserController2 _userVM2;
    private UserController3 _userVM3;

    public MainController(IUserService1 userService1,IUserService2 userService2,IUserService3 userService3,anotherService)
    {    
        _userVM1 = new UserController1(userService1,anotherService);
        _userVM2 = new UserController2(userService2,...,...);
        _userVM3 = new UserController3(userService3,...,...,...);     
    }
} 

// Such a Controller class needs to be created 10 times... and what I do here is typical for all Controllers driving the GUI
public class UserController1
{
    private readonly IUserService1 _userService1; 

    public UserController1(IUserService1 userService1,IAnotherService anotherService)
    {
        _userService1 = userService1;           
        //Bind data to GUI
        UserData1Collection = ConvertModelIntoViewModelCollection(userService1,anotherService);
    }

    public ObservableCollection<UserData1> UserData1Collection { get; set; }

    private ObservableCollection<UserData1ViewModel> ConvertModelIntoViewModelCollection(IAnotherService anotherService)
    {      
        var userData1ViewModelCollection = new ObservableCollection<UserData1ViewModel>();
        _userService1.GetUserData1().ForEach(user =>
        {
            userData1ViewModelCollection.Add(new UserData1ViewModel(user, anotherService,...));
        });           
        return userData1ViewModelCollection; 
    }
}

Now the question:

There is a lot of falling through/passing trough services because I have to call services when for example properties of viewmodels change via lost_focus on gui controls.

Is that all right what I do? Do you see any disadvantage? Or how would you do it?

Update

That DI stuff is a massiv attack on my vicious habits :P

  1. Did you meant it that way Can?

  2. Btw. why should I do that controller factory? Why then not a ServiceFactory too... then we are back to the ServiceLocator...

  3. How do I get now that controller instances in my MainViewModel? via extending the Constructor of my MVM with many additional params? ending up with 30 params? ...

 

protected override void OnStartup(StartupEventArgs e)
{
    IContainerBuilder builder = new ContainerBuilder();

    // Firstly Register ALL existing Services            
    builder.Register<IAdminService, AdminService>();
    builder.Register<IDocumentService, DocumentService>();
    builder.Register<ILessonPlannerService, LessonPlannerService>();
    builder.Register<IMediator, Mediator>();
    builder.Register<IMainRepository, MainRepository>();           
    builder.Register<MainViewModel>();

    IContainer _container = builder.Build();

    // THEN Register ALL Controllers needing the previously registered Services
    IControllerFactory factory = new ControllerFactory(builder);
    IDailyPlanner controller1 = factory.Create<IDailyPlanner>();
    IWeeklyPlanner controller2 = factory.Create<IWeeklyPlanner>();
    SchoolclassAdministrationViewModel controller3 = factory.Create<SchoolclassAdministrationViewModel>();

    // THEN Register the mainViewModel(MainController) which should take ALL Services and ALL Controller... WOW thats a massive Ctor param count... is that pure? Did you mean it that way???
    MainViewModel mainViewModel = _container.Resolve<MainViewModel>();

    //MainWindow mainWindow = _container.Resolve<MainWindow>();
    //mainWindow.DataContext = mainViewModel;   
    //mainWindow.ShowDialog();   
} 

public class ControllerFactory : IControllerFactory
{
    private readonly IContainerBuilder _builder;
    private readonly IContainer _container;

    /// <summary>
    /// Takes the IOC container to register all Controllers
    /// </summary>
    public ControllerFactory(IContainerBuilder builder)
    {
        _builder = builder;

        _builder.Register<SchoolclassAdministrationViewModel>();
        _builder.Register<IDailyPlanner, LessonPlannerDailyViewModel>();
        _builder.Register<IWeeklyPlanner, LessonPlannerWeeklyViewModel>();
        _container = _builder.Build();
    }

    /// <summary>
    /// Returns an Instance of a given Type
    /// </summary>
    public T Create<T>()
    {
        return _container.Resolve<T>();
    }
}

Update2:

Now I changed my code that the MainViewModel accepts the IControllerFactory as Parameter and added these two lines of code to the App class:

builder.Register<IControllerFactory, ControllerFactory>();
builder.Register<IContainerBuilder, ContainerBuilder>(); 

That way I dont need to pass all controllers in the MainViewModel Ctor instead the MainViewModel gets the controller instances from the Factory.

Is there anything better I can do here? Or is that an acceptable good solution? I have no experience at all with DI so I ask :)

Update3

OK I did some code refactoring and made comments for others so they understand whats the final solution:

protected override void OnStartup(StartupEventArgs e)
{
    IContainerBuilder builder = new ContainerBuilder();

    // Firstly Register ALL existing Services          
    builder.Register<IAdminService, AdminService>();
    builder.Register<IDocumentService, DocumentService>();
    builder.Register<ILessonPlannerService, LessonPlannerService>();
    builder.Register<IMediator, Mediator>();
    builder.Register<IMainRepository, MainRepository>();
    builder.Register<IControllerFactory, ControllerFactory>();              
    builder.Register<IDailyPlanner, LessonPlannerDailyViewModel>();
    builder.Register<IWeeklyPlanner, LessonPlannerWeeklyViewModel>();

    // Just for visual separation THEN register the MainController driving all other Controllers created via the IControllerFactory          
    builder.Register<MainViewModel>();

    // Build the container
    IContainer container = builder.Build();

    // THEN Register the MainController which should take ALL IServices and the IFactory
    MainViewModel mainViewModel = container.Resolve<MainViewModel>();

    // LATER in the mainViewModel`s Ctor you can create all 10 Controller instances with the IControllerFactory like this
    // _dailyPlannerController = controllerFactory.Create<IDailyPlanner>();

    MainWindow mainWindow = new MainWindow();
    mainWindow.DataContext = mainViewModel;   
    mainWindow.ShowDialog();   
}

public class ControllerFactory : IControllerFactory
{
    private readonly IContainer _container;

    /// <summary>
    /// Takes the IOC container to resolve all Controllers
    /// </summary>
    public ControllerFactory(IContainer container)
    {
        _container = container; 
    }

    /// <summary>
    /// Returns an Instance of a given Type
    /// </summary>
    public T Create<T>()
    {
        return _container.Resolve<T>();
    }
}

Thank you very much for your time, @Can. I have learned a lot!

msfanboy
  • 5,273
  • 13
  • 69
  • 120

1 Answers1

9

It seems to me that you have misunderstood how to use an IoC container. Instead of creating instances of your services and passing them as parameters, you need to ask the container to resolve them for you.

For example, you can refactor your code as follows to make use of IoC properly:

protected void Application_Start(object sender, EventArgs e)
{
    var builder = new ContainerBuilder();

    builder.Register<IUserService1, UserService1>();
    builder.Register<IUserService2, UserService2>();
    builder.Register<IUserService3, UserService3>();
    builder.Register<IAnotherService, AnotherService>();

    builder.Register<MainController, MainController>();
    // And many more Services...

    _container = builder.Build();

    //let the container inject all the required dependencies into MainController..
    var vm = _container.Resolve<MainController>();
}

The container in this case should control the lifecycle of your MainController object and make sure that all the dependencies (properties and constructor parameters that need to be initialized) are injected and populated.

What will happen is that the container will understand that to create an instance of MainController, it needs IUserService1, IUserService2 and so forth, and in turn will look if it can create any instances of those, by looking at the other types registered with the container. This will be done in a recursive manner to build up a dependency tree until all the dependencies of a class can be satisified. The resulting MainController you get will already have all the dependencies injected in it.

Ideally, you should call Resolve() in as little places as possible in order to structure your application in a way that there is only one root. For an in depth view into Dependency Injection, I strongly recommend the book Dependency Injection in .NET by Mark Seeman, which is in my opinion one of the best introduction to DI one can have.

UPDATE:

The reason why I suggested to use a ControllerFactory was because you had a lot of UserController classes in your MainController, and passing all those as a dependency you would end up with 10+ constructor parameters, not to mention that having to add more when you create new controllers. If your viewmodels only have dependency on one controller, then it doesn't make sense to use the factory in that way and you can have a direct dependency on the required controller.

As for ServiceFactory, it is not needed, because each of your classes are not likely to require ALL the service classes available, just some of them. In that case it is better to specify them explicitly for each service in the constructor.

You should also register all your instances in one place (or in small installer classes) instead of within constructor for different classes.

Here's a question that is more specific to MVVM that should get you going on how to structure your classes and dependencies: How can I combine MVVM and Dependency Injection in a WPF app?

Community
  • 1
  • 1
Can Gencer
  • 8,822
  • 5
  • 33
  • 52
  • @msfanboy, I would recommend that you create a class ControllerFactory or something similar that is responsible for creating all your controllers. Then you simply ask that class for your controller. ControllerFactory in turn can resolve the requested controller from the container. Any controller you get will already have all the services in it populated (if you registered them properly). This is a well used pattern in ASP.NET MVC world for integration with DI containers. – Can Gencer Jun 24 '11 at 20:37
  • Ok Can one step more now I am eager to know it all. HOW does the ControlloerFactory know the container and should there be a dependency between them? Yes I heard of Mark Seemans book, he already advised it to me. I will not have time next 6 months but after it I am eager to read it for sure! – msfanboy Jun 24 '11 at 20:44
  • @msfanboy, You just pass it as a parameter in the constructor. You can simply new your class manually with the container as a parameter and then register the ControllerFactory interface against that instance. This will almost be the only place in your project where you pass the container as a parameter to one of your classes. – Can Gencer Jun 24 '11 at 20:49
  • @Can "This will almost be the only place in your project where you pass the container..." Did you say that because its a well known bad practice to pass the container around? I will try tomorrow your recommendations and post the result here :) – msfanboy Jun 24 '11 at 21:46
  • @Can 1.)Not sure wether I understand you right or you me. Maybe I should have chosen consistent Controller/ViewModel names like the MainViewModel is actually working as a Controller. 2.) Right not all Controller (you wrote classes) need all available services that would be actually a bad separation of responsibility or mixed UI.3.)But this I already do in the App class StartUp method ?? - Was that now a general hint ? - 4.)thanks I will watch them. ONE question is still not answered, HOW do you pass the controllers from the App class to the mainViewModel if not using its constructor ? – msfanboy Jun 25 '11 at 08:04
  • @msfanboy, 2.) yes that is what I wrote. Controller should only have in the constructor the services they need, hence you don't need a service factory. 3.) You also registered some components inside the ControllerFactory 4.) It should be enough that you only pass the ControllerFactory to the maincontroller, which assume is the main root of your application. – Can Gencer Jun 25 '11 at 08:19
  • @Can I did an UPDATE2 I guess then we have it :) – msfanboy Jun 25 '11 at 08:21
  • @msfanboy, that looks fine, except I don't think you need to pass IContainerBuilder to the factory, better to register your controllers elsewhere (a minor point, but still). – Can Gencer Jun 25 '11 at 08:48
  • @Can But you said in an former post I should => "Then you simply ask that class for your controller. ControllerFactory in turn can resolve the requested controller from the container" from the container... HOW can my ControllerFactory resolve the controller from the container if my Factory does not have access to it as it is not passed ? Now you say I dont need to pass my container"-Builder" , thats paradox! ;-) – msfanboy Jun 25 '11 at 09:02
  • @Can you mean I should make an own RegisterController/GetController concept in the ControllerFactory without using the DI container? Just like I did with the ServiceLocator before only difference is the Factory now is not static and its an abstract Factory using an interface. – msfanboy Jun 25 '11 at 09:07
  • @msfanboy, what I meant is that instead of passing the builder, you pass the container that you registered the services and controllers with as an instance. Otherwise you will endup with having two different containers and it won't work. Everything should be registered against the same container. – Can Gencer Jun 25 '11 at 09:16
  • @Can forgot to say just see my UPDATE3 :) – msfanboy Jun 25 '11 at 10:28
  • @msfanboy, looks like a good solution. I would also add a generic constraint to the Create method of the Factory such as where T : ControllerBase so that it is not abused for resolving other types. – Can Gencer Jun 25 '11 at 10:40
  • @Can good idea but not all Controller inherit from a base class which is just giving the INotifyPropertyChanged Interface. But when development continues maybe there will be a base class for communication between controllers instead of a mediator, must see and think about it, but in general your idea is fine! – msfanboy Jun 25 '11 at 11:12
  • @Can just for fun I tried your advise: did a public class ControlloerFactory : where T : ControllerBase but then this line wount work anymore: builder.Register(); now ControllerFactory wants a type... – msfanboy Jun 25 '11 at 17:20
  • @msfanboy I meant the Create method. i.e. public T Create() where T : ControllerBase – Can Gencer Jun 25 '11 at 18:44
  • hell am I stupid. Of course yours work! :) did not know that a method can have a where constraint so I assumed you meant the class constraint. – msfanboy Jun 28 '11 at 09:33