thoughts on programming and computer related stuff RSS 2.0
# Friday, June 12, 2009
This blog post is about changing the DefaultControllerFactory to a custom one to allow constructor dependency injection. In this post I use Structuremap, CommonServiceLocator and I use a slightly changed version of the CommonServiceLocator.StructureMapAdatpter.

The DefaultControllerFactory needs a constructor without any input parameters. I want to get rid of dependencies in my code, so I put all my interface dependencies in the constructor and want to use structuremap to handle this for me. I have used this in a semi-large project, but in this blog post I will use an example with only one dependency and only one page.

Lets get started:

I start by creating a new ASP.NET MVC project. I then delete all controllers and views except for my home controller and my index view.

Then I add a Tools folder and a PersonRepository class in my new folder. This only has a default constructor and a get method that returns a Person, a simple object with only a name Property. I also extract an interface from PersonRepository called IPersonRepository.

In my homecontroller I create a constructor that takes a Ipersonrepository as a parameter. This is my HomeController now:
using System.Web.Mvc;
using DIControllerFactoryExample.Tools;

namespace DIControllerFactoryExample.Controllers
{

    public class HomeController : Controller
    {
        private readonly IPersonRepository personRepository;

        public HomeController(IPersonRepository personRepository)
        {
            this.personRepository = personRepository;
        }

        public ActionResult Index()
        {
            var personid = 1;
            ViewData.Model = personRepository.Get(personid);

            return View();
        }
    }
}
I compile, run and get a: "No parameterless constructor defined for this object." exception. Just as planned.
Now, the fun begins. Getting this to work.

I start by adding a solution folder called Lib, and adding StructureMap.dll and Microsoft.Practices.ServiceLocation.dll and reference them in the solution. These can be found at the locations in the top of this blog post.

Then I add 3 new files to my Tools folder.
StructureMapServiceLocator.cs, DefaultStructureMapRegistry.cs, CommonServiceLocatorControllerFactory.cs

These files look like this:
using StructureMap.Configuration.DSL;

namespace DIControllerFactoryExample.Tools
{
    public class DefaultStructureMapRegistry : Registry
    {
        public DefaultStructureMapRegistry()
        {
            ForRequestedType<IPersonRepository>().TheDefaultIsConcreteType
                <PersonRepository>();
        }
    }
}
using System;
using System.Collections.Generic;
using Microsoft.Practices.ServiceLocation;
using StructureMap;

namespace DIControllerFactoryExample.Tools
{
    public class StructureMapServiceLocator : ServiceLocatorImplBase
    {
        private IContainer container;

        public StructureMapServiceLocator(IContainer container)
        {
            this.container = container;
        }

        protected override object DoGetInstance(Type serviceType, string key)
        {
            return string.IsNullOrEmpty(key) ? container.GetInstance(serviceType) : container.GetInstance(serviceType, key);
        }

        protected override IEnumerable<object> DoGetAllInstances(Type serviceType)
        {
            foreach (object obj in container.GetAllInstances(serviceType))
            {
                yield return obj;
            }
        }
    }
}
using System;
using System.Web.Mvc;
using Microsoft.Practices.ServiceLocation;

namespace DIControllerFactoryExample.Tools
{
    public class StructureMapServiceLocatorControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(Type controllerType)
        {
            var controller = ServiceLocator.Current.GetInstance(controllerType) as Controller;
            return controller;
        }
    }
}

I also add the following to the Application_Start method in my Global.asax.cs:
var registry = new DefaultStructureMapRegistry();
var container = new Container(registry);
ServiceLocator.SetLocatorProvider(() => new StructureMapServiceLocator(container));
var locator = new StructureMapServiceLocatorControllerFactory();
ControllerBuilder.Current.SetControllerFactory(locator);

I compile, reload and everything works. That's is. Good luck;)

EDIT: Did a small fix in the code i global.asax, check out this post

The full Visual Studio solution can be downloaded here:

DIControllerFactoryExample.zip (463.5 KB)
Friday, June 12, 2009 11:18:17 AM (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
.NET | ASP.NET | ASP.NET MVC | Patterns
# Monday, September 01, 2008

Third party applications are great, and its very often smart to use open source or buy modules you need before making your own. However a lot of third party applications do create some problems. One is that prices and functionality can change and new, better and cheaper products come to surface.

 

Therefore, you do not want to be to dependent on these applications, so if you find another tool that does the same thing even better, you should be able to switch it out easy. A good way to do this is to create a interface and a factory pattern, and refer to the interface in your code and let the factory class decide what tool to use.  You can also use this pattern to ease mocking of objects. And let the factory return a mock object if you are unit testing.


Example in C#:

using System;

namespace Logger
{
    // An interface is created
    interface iLog
    {
        bool Log(string s);
    }
    //An implementation using enterprise libraries logging
    class EntLog : iLog 
    {
        public bool Log(string s)
        {
            //Implement logic to save to log
            return false;
        }
    }

    //An implementation using log4net to save logs.
    class log4net : iLog
    {
        public bool Log(string s)
        {
            //Implement logic to save to log
            return false;
        }
    }

    // A factory class
    class LogFactory
    {
        // Creates an object, of the right type.
        public static iLog CreateLog(){
            return new log4net();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            // Get the log object to use.
            iLog logger = LogFactory.CreateLog();
            //Print the type of logger used
            Console.Out.WriteLine(logger.GetType());
            // Log a message and write the result
            Console.Out.WriteLine(logger.Log("Something is happening"));
            Console.ReadKey();
        }
    }
}

To change this to use entLog instead of log4net, all you have to do is change the object created in the logfactory.  To add a new logger, all you have to do is create a new class to implement Ilog and change the creator to return the new logger. The creator could also have a constructor that takes an input, so that you can decide in the class using the log what kind of log you want to use.

 

Monday, September 01, 2008 11:33:16 AM (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
.NET | Patterns
Navigation
Archive
<September 2010>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
gaute
Sign In
Statistics
Total Posts: 17
This Year: 0
This Month: 0
This Week: 0
Comments: 1
Themes
Pick a theme:
All Content © 2010, gaute
DasBlog theme 'Business' created by Christoph De Baene (delarou)