【问题标题】:Dependency Injection / Constructor Injection Help依赖注入/构造函数注入帮助
【发布时间】:2011-07-22 01:43:40
【问题描述】:

我有以下类/接口:

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 传递到我的控制器中并且它需要将我的实体框架上下文传递到项目存储库中。我需要将实体上下文设置为 HTTP 请求范围。

我不确定应该将所有映射代码放在哪里以使依赖注入工作。我也不明白如果没有默认构造函数,MVC 将如何工作。

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

【问题讨论】:

  • 你运行的是什么版本的 MVC?
  • 如果您使用的是 ASP.NET MVC 3,您应该真正利用它内置在 DependencyResolver 中的优势。有关更多信息,请参阅我的答案。
  • 我注意到 DependencyResolver 有问题。 MVC3的IDependencyResolver接口有个大问题:没有释放方法。这意味着如果您打算将它与 Windsor 一起使用,则可能存在内存泄漏。在此处查看我的博客文章:mikehadlow.blogspot.com/2011/02/…

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


【解决方案1】:

如果您使用的是 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

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

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

【讨论】:

  • 我注意到的一件事是,如果我的 IIS Express 端口仍在运行,那么容器就不会遇到断点。 /App_Start/StructuremapMvc.cs 是我放置了第一个断点,并惊讶于它没有被击中......所以只是一个观察。
【解决方案2】:

如果您准备使用 StructureMap,here 是您可能需要的设置教程。

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

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

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

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

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

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

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

    • 错误:没有为此对象定义无参数构造函数。
    • 我仍然不知道应该将配置/设置代码放在哪里。
    【解决方案4】:

    当使用 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
        }
    

    【讨论】:

    • 谢谢。这就是我一直在寻找的。如何将它们粘合在一起。不过,现在我在使用 ProjectRepository 时遇到了麻烦。如何设置它以获取我的实体上下文 HTTP 请求范围的默认类型?
    • 根据 HTTP 请求确定上下文范围:dotnetslackers.com/articles/ado_net/… 使用延迟加载方案的建议也很合理。
    • -1 抱歉,但这绝对不是使用 ASP.NET MVC 3 进行依赖注入的正确方法。
    • 这是服务定位器模式的示例,而不是依赖注入模式blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx
    • 是的,老实说我现在也不这样做了。
    猜你喜欢
    • 2011-02-02
    • 1970-01-01
    • 2019-04-20
    • 2013-01-30
    • 2012-02-12
    • 2018-06-17
    • 1970-01-01
    • 2023-04-04
    相关资源
    最近更新 更多