UPDATE: Using Dependency Injection on ViewModelBuilder

On a previous piece of code, dependency injection should have been used on my ViewModelBuilder. Today, I provide a cleaner way to do my ViewModelBuilder.

May 8th, 2015 • MVC •
0 (0 votes)
Review architecture

A while back, I explained how to create a ViewModelBuilder using a DefaultViewModelFactory when creating ViewModels for my Views. The article where I explained this was called The Skinniest ASP.NET MVC Controller You've Ever Seen, Part 1.

Quite a mouth-full, right?

I've been using reflection for my ViewModel Factory. One location to build and return my ViewModels.

Here was the code in question (or "code smell").

Classes\DefaultViewModelFactory.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ThinController.Interfaces;
namespace ThinController.Classes
{
    public class DefaultViewModelFactory : IViewModelFactory
    {
        public TViewModel GetViewModel<TController, TViewModel>(TController controller)
        {
            var viewModels = GetSystemTypes(controller, typeof(TViewModel));
            var modelBuilders = GetSystemTypes(controller, typeof(IViewModelBuilder<TController, TViewModel>));
            var typeName = typeof(TViewModel).Name;
            var model = (from t in viewModels
                         where typeof(TViewModel).IsAssignableFrom(t)
                            && t.Name.Equals(typeName)
                         let strategy = (TViewModel)Activator.CreateInstance(t)
                         select strategy).FirstOrDefault();
            var modelBuilder = (from t in modelBuilders
                                where typeof(IViewModelBuilder<TController, TViewModel>)
                                    .IsAssignableFrom(t)
                                let strategy = (IViewModelBuilder<TController, TViewModel>)
                                    Activator.CreateInstance(t)
                                select strategy).FirstOrDefault();
            // Redirect and assist developers in adding their own modelbuilder/viewmodel
            if (modelBuilder == null)
                throw new Exception(
                    String.Format(
                        "Could not find a ModelBuilder with a {0} Controller/{1} ViewModel pairing. Please create one.",
                        typeof(TController).Name, typeof(TViewModel).Name));
            return modelBuilder.Build(controller, model);
        }
        public TViewModel GetViewModel<TController, TViewModel, TInput>(TController controller,              TInput data)         {             var viewModels = GetSystemTypes(controller, typeof(TViewModel));             var modelBuilders = GetSystemTypes(controller, typeof(IViewModelBuilder<TController,                  TViewModel, TInput>));             var typeName = typeof(TViewModel).Name;             var model = (from t in viewModels                          where typeof(TViewModel).IsAssignableFrom(t) && t.Name.Equals(typeName)                          let strategy = (TViewModel)Activator.CreateInstance(t)                          select strategy).FirstOrDefault();             var modelBuilder = (from t in modelBuilders                                 where typeof(IViewModelBuilder<TController, TViewModel, TInput>)                                     .IsAssignableFrom(t)                                 let strategy = (IViewModelBuilder<TController, TViewModel, TInput>)                                     Activator.CreateInstance(t)                                 select strategy).FirstOrDefault();             // Redirect and assist developers in adding their own modelbuilder/viewmodel             if (modelBuilder == null)                 throw new Exception(                     String.Format(                         "Could not find a ModelBuilder with a {0} Controller/{1} ViewModel/{2} TInput pairing. Please create one.",                         typeof(TController).Name, typeof(TViewModel).Name, typeof(TInput).Name));             return modelBuilder.Build(controller, model, data);         }         private IEnumerable<Type> GetSystemTypes<TController>(TController controller, Type type)         {             // First, get the types of the executing assembly             var currentAssemblyTypes = Assembly                 .GetExecutingAssembly()                 .GetTypes()                 .Where(e => type.IsAssignableFrom(e))                 .ToList();             // Then the calling assembly             currentAssemblyTypes.AddRange(                 Assembly                     .GetCallingAssembly()                     .GetTypes()                     .Where(e => type.IsAssignableFrom(e))                     .ToList()                 );             return currentAssemblyTypes.Distinct();         }     } }

Yes, it is a mess...and a "code smell!"

If you're wondering what I'm talking about when I say "Code Smell," please refer to the book Refactoring: Improving the Design Of Existing Code. This is one of the books that every .NET developer should own!

Enter Dependency Injection

I've always liked Ninject and it hasn't done me wrong yet. I finally looked the code over one last time and decided to refactor it so it wouldn't look like such a mess.

First, I used Nuget to grab Ninject (Install-Package Ninject) and the Ninject Extension Conventions (Install-package Ninject.extensions.conventions).

Once I had Ninject installed, I was ready to start refactoring the code.

Here is the refactored code:

ViewModelBuilder\ViewModelFactory.cs

public class DefaultViewModelFactoryIViewModelFactory
{
    public TViewModel GetViewModel<TController, TViewModel>(TController controller)
    {
        TViewModel model;
        IViewModelBuilder<TController, TViewModel> modelBuilder;
        using (var kernel = new StandardKernel())
        {
            kernel.Bind(x => x.FromThisAssembly()
                .SelectAllClasses()
                .BindAllInterfaces());
            model = kernel.Get<TViewModel>();
            modelBuilder = kernel.Get<IViewModelBuilder<TController, TViewModel>>();
        }
        // Redirect and assist developers in adding their own modelbuilder/viewmodel
        if (modelBuilder == null)
            throw new Exception(
                String.Format(
                    "Could not find a ModelBuilder with a {0} Controller/{1} ViewModel pairing. Please create one.",
                    typeof (TController).Name, typeof (TViewModel).Name));
        
        return modelBuilder.Build(controller, model);
    }
    public TViewModel GetViewModel<TController, TViewModel, TInput>(TController controller, TInput data)
    {
        TViewModel model;
        IViewModelBuilder<TController, TViewModel, TInput> modelBuilder;
        using (var kernel = new StandardKernel())
        {
            kernel.Bind(x => x.FromThisAssembly()
                .SelectAllClasses()
                .BindAllInterfaces());
            model = kernel.Get<TViewModel>();
            modelBuilder = kernel.Get<IViewModelBuilder<TController, TViewModel, TInput>>();
        }
        // Redirect and assist developers in adding their own modelbuilder/viewmodel
        if (modelBuilder == null)
            throw new Exception(
                String.Format(
                    "Could not find a ModelBuilder with a {0} Controller/{1} ViewModel/{2} TInput pairing. Please create one.",
                    typeof (TController).Name, typeof (TViewModel).Name, typeof (TInput).Name));
        // if (model == null) return null;
        return modelBuilder.Build(controller, model, data);
    }
}

Removing and optimizing code is one of the greatest feelings, isn't it?

This should give you an idea of how you can replace most reflection calls with dependency injection.

Since these ViewModels are contained and ONLY used for the Views, they are already compiled inside this assembly. We aren't going outside of the application for any injected ViewModels so why make it any more complicated.

The type of ViewModel and ViewModelBuilder are easy to instantiate using Ninject. The two methods in the class are required because one contains an input parameter and the other doesn't.

I did need a little bit of help with the Ninject and it's specific way of binding all of the classes to interfaces so that Ninject wouldn't scream about classes not being self-bindable.

This line took care of that:

   kernel.Bind(x => x.FromThisAssembly()
          .SelectAllClasses()
         .BindAllInterfaces()
);

After I added this in, everything functioned as before...

...only with less code and more manageable code!

Conclusion

Dependency Injection is becoming so important that the next version of ASP.NET MVC will have dependency injection baked right into ASP.NET MVC.

If you are still skeptical about dependency injection, I would seriously rethink about getting into dependency injection. I can describe it in two words:

They are Decoupled Factories!

The code above is proof of that.

Was there a better way to perform the code above? Did the bind need moved to another part of my application? Post your comments below.

Was this informative? Share it!

Looking to become a better developer?

Sign up to receive ReSharper Design Pattern Smart Templates, ASP.NET MVC Guidelines Checklist, and Newsletter Updates!

Picture of Jonathan Danylko

Jonathan Danylko is a freelance web architect and avid programmer who has been programming for over 20 years. He has developed various systems in numerous industries including e-commerce, biotechnology, real estate, health, insurance, and utility companies.

When asked what he likes to do in his spare time, he replies, "Programming."

comments powered by Disqus