开发者

Dependency Injection / Constructor Injection Help

开发者 https://www.devze.com 2023-03-22 00:29 出处:网络
I have the following classes / interfaces: public interface IProjectRepository { IQueryably<Project> GetProjects();

I have the following classes / interfaces:

public interface IProjectRepository
{
    IQueryably<Project> GetProjects();
}

// Depends on my EF Context
public ProjectRepository : IProjectRepository
{
    private MyDbEntities context;

    public ProjectRepository(MyDbEntities context)
    {
        this.context = context;
    }

    public IQueryable<Project> GetProjects() 
    {
        return context.Projects;
    }
}

My controlle开发者_JS百科r:

 // Depends on IProjectRepository
 public class ProjectsController : Controller
 {
     private IProjectRepository projectRepository;

     public ProjectsController(IProjectRepository projectRepository)
     {
         this.projectRepository = projectRepository;
     }

     public ActionResult Index()
     {
         return View(projectRepository.GetProjects());
     }
 }

I need to set up my dependency injection so that it passes in ProjectRepository into my Controller AND it needs to pass in my Entity Framework context into the Project Repository. I need to Entity Context to be HTTP Request scoped.

I'm not sure where I am supposed to put all the mapping code to make the dependency injection work. I also don't understand how MVC will work without the default constructor.

Can someone help me put all the pieces together? I am using StructureMap but I could easily switch to something else because I have no idea what I'm doing.


If you are using MVC 3, to do things properly, you should make use of the built in dependency resolution bits. I would highly recommend you read through the series of blog posts from Brad Wilson (member of the ASP.NET MVC team).

As far as a StructureMap specific implementation, I found the following blog posts helpful.

StructureMap and ASP.NET MVC 3 – Getting Started
StructureMap, Model Binders and Dependency Injection in ASP.NET MVC 3
StructureMap, Action Filters and Dependency Injection in ASP.NET MVC 3
StructureMap, Global Action Filters and Dependency Injection in ASP.NET MVC 3

Anyway, here's some code. To start with, I would suggest that you install the StructureMap-MVC3 NuGet package.

I can't remember what exactly it creates in the way of files, but here's what's basically involved.

/App_Start/StructuremapMvc.cs - This hooks into the Application_Start and sets up your container (SmIoC.Initialize()) and then sets the MVC 3 DependencyResolver to a your SmDependencyResolver

using System.Web.Mvc;
using YourAppNamespace.Website.IoC;
using StructureMap;

[assembly: WebActivator.PreApplicationStartMethod(typeof(YourAppNamespace.App_Start.StructuremapMvc), "Start")]

namespace YourAppNamespace.Website.App_Start {
    public static class StructuremapMvc {
        public static void Start() {
            var container = SmIoC.Initialize();
            DependencyResolver.SetResolver(new SmDependencyResolver(container));
        }
    }
}

/IoC/SmDependencyResolver.cs - this is your MVC 3 IDependencyResolver implementation. It's used in the App_Start code above.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using StructureMap;

namespace YourAppNamespace.Website.IoC
{
    public class SmDependencyResolver : IDependencyResolver
    {
        private readonly IContainer _container;

        public SmDependencyResolver(IContainer container)
        {
            _container = container;
        }

        public object GetService(Type serviceType)
        {
            if (serviceType == null)
            {
                return null;
            }

            try
            {
                return _container.GetInstance(serviceType);
            }
            catch
            {
                return null;
            }
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return _container.GetAllInstances(serviceType).Cast<object>(); ;
        }
    }
}

/IoC/SmIoC.cs - this is where you setup your container... also used in the App_Start code.

namespace YourAppNamespace.Website.IoC
{
    public static class SmIoC
    {
        public static IContainer Initialize()
        {
            ObjectFactory.Initialize(x =>
                        {
                            x.For<IProjectRepository>().Use<ProjectRepository>();
                            //etc...
                        });

            return ObjectFactory.Container;
        }
    }
}

Now everything is hooked up... (I think ;-) but you still have one last thing to do. Inside your Global.asax, we need to make sure you dispose of everything that is HttpContext scoped.

protected void Application_EndRequest()
{
    ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}

So you should be able to achieve dependency injection through constructor injection, which is the correct way to go about doing things.


If you are set on using StructureMap, here is a tutorial on the setup that you will probably need.

Some other dependency injection frameworks come with custom controller factories which will do that for you. Ninject (open source dependency injection), for example has an extension that you can use that contains this behaviour. See here for example. And here to the extension.

You can use also Unity IOC which is another popular dependency injection framework with which, to my knowledge, you will have to create a custom controller factory (like with structuremap) to achieve this behaviour. See here for an example.

You can also research all other dependency injection frameworks to see what support you can get with each.

EDIT: I hope I am explaining this correctly but here is some background info.

MVC uses a controller factory that has the responsibilities of instantiating the respective controllers needed when a request is made. By default, it will initialize a controller by calling its parameterless constructor.

To create the infrastructure for the constructor parameter injection you need to create a custom factory that can resolve constructor parameters. That is where the dependency injection containers come in: essentially the DI container (if configured properly) knows how to resolve those dependency and your custom factory will leverage it to request the registered dependencies and pass the to the controller constructor.


All work pretty much the same. Historically, all have had setter injectors (set up a property that is then filled), but most have constructor injection now. In structure map, the easiest way to accomplish this is use the attribute: [StructureMap.DefaultConstructor].

Once you add the attribute, the objects you have placed in your "map" should inject without any extra work. If you can't use attributes, consider using the setter.

There is a file on the structure map site: http://structuremap.net/structuremap/ConstructorAndSetterInjection.htm


When using StructureMap I would generally have something like this in my controller:

private static IProjectRepository GetProjectRepository()
{
    var retVal = ObjectFactory.TryGetInstance<IProjectRepository>() 
                 ?? new ProjectRepository();
    return retVal;
}

If the TryGetInstance returns null (because nothing was set for that type) it will default to the concrete type you specify.

Now you have a bootstrapper somewhere like this:

public static class StructureMapBootStrapper
{
    public static void InitializeStructureMap()
    {
        ObjectFactory.Initialize(x =>
        {
            x.For<IProjectRepository>().Use<ProjectRepository>();
        }
    }
}

Now you call this bootstrapper in your Global.asax Application_Start event:

    protected void Application_Start()
    {
        StructureMapBootStrapper.InitializeStructureMap();
    }

Now in a test project, when you want to inject a mock repository you can just do this:

    [TestMethod]
    public void SomeControllerTest()
    {
        StructureMap.ObjectFactory.Inject(
           typeof(IProjectRepository),
           new MockProjectRepository());

        // ... do some test of your controller with the mock
    }
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号