依赖注入/构造函数注入帮助

时间:2011-07-20 20:40:25

标签: c# asp.net-mvc dependency-injection structuremap

我有以下类/接口:

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;
    }
}

我的控制器:

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

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

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

我需要设置我的依赖注入,以便它将ProjectRepository传递到我的Controller AND ,它需要将我的Entity Framework上下文传递到Project Repository。我需要将实体上下文作为HTTP请求范围。

我不确定我应该放置所有映射代码以使依赖注入工作。我也不明白MVC如何在没有默认构造函数的情况下工作。

有人能帮我把所有的东西放在一起吗?我正在使用StructureMap,但我可以轻松切换到别的东西,因为我不知道我在做什么。

4 个答案:

答案 0 :(得分:4)

如果您正在使用MVC 3,要正确地执行操作,您应该使用内置的依赖性解析位。我强烈建议您阅读series of blog posts from Brad Wilson(ASP.NET MVC团队成员)。

就StructureMap的具体实现而言,我发现以下博客文章很有帮助。

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

无论如何,这里有一些代码。首先,我建议您安装StructureMap-MVC3 NuGet package

我不记得它究竟是以文件的方式创造了什么,但这是基本上所涉及的内容。

/App_Start/StructuremapMvc.cs - 这会挂钩到Application_Start并设置容器(SmIoC.Initialize()),然后将MVC 3 DependencyResolver设置为您的SmDependencyResolver < / p>

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 - 这是您的MVC 3 IDependencyResolver实现。它在上面的App_Start代码中使用。

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 - 这是您设置容器的地方......也用于App_Start代码。

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

            return ObjectFactory.Container;
        }
    }
}

现在一切都搞定了......(我想;-)但你还有最后一件事要做。在Global.asax内,我们需要确保处理HttpContext范围内的所有内容。

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

所以你应该能够通过构造函数注入实现依赖注入,这是正确的做事方式。

答案 1 :(得分:0)

如果您已开始使用StructureMap,here是您可能需要的设置教程。

其他一些依赖注入框架带有自定义控制器工厂,可以为您完成。 Ninject(开源依赖注入),例如,您可以使用包含此行为的扩展。例如,请参阅here。并here到扩展程序。

你也可以使用Unity IOC这是另一个流行的依赖注入框架,据我所知,你必须创建一个自定义控制器工厂(如使用structuremap)来实现这种行为。有关示例,请参阅here

您还可以研究所有其他依赖注入框架,以了解每种依赖注入框架可以获得哪些支持。

编辑: 我希望我正确解释这个,但这里有一些背景信息。

MVC使用控制器工厂,该工厂负责在发出请求时实例化所需的相应控制器。默认情况下,它将通过调用其无参数构造函数来初始化控制器。

要为构造函数参数注入创建基础结构,您需要创建一个可以解析构造函数参数的自定义工厂。这就是依赖注入容器的用武之地:本质上DI容器(如果配置正确)知道如何解决这些依赖关系,并且您的自定义工厂将利用它来请求已注册的依赖项并将其传递给控制器​​构造函数。

答案 2 :(得分:-1)

所有工作都差不多。从历史上看,所有人都有setter注入器(设置一个然后填充的属性),但现在大多数都有构造函数注入。在结构图中,最简单的方法是使用属性:[StructureMap.DefaultConstructor]。

添加属性后,放置在“地图”中的对象应该无需任何额外工作即可注入。如果您不能使用属性,请考虑使用setter。

结构地图网站上有一个文件: http://structuremap.net/structuremap/ConstructorAndSetterInjection.htm

答案 3 :(得分:-3)

使用StructureMap时,我的控制器通常会有这样的东西:

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

如果TryGetInstance返回null(因为没有为该类型设置任何内容),它将默认为您指定的具体类型。

现在你有一个像这样的引导程序:

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

现在,您可以在Global.asax Application_Start事件中调用此引导程序:

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

现在在一个测试项目中,当你想注入一个模拟存储库时,你可以这样做:

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

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