2011-07-20 94 views
3

我有以下类/接口:依赖注入/构造器注入帮助

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,但我可以轻松切换到其他的东西,因为我不知道自己在做什么。

+0

您运行的是哪个版本的MVC? – Charlino

+0

ASP.net MVC 3。 。 – Dismissile

+0

如果您使用的是ASP。NET MVC 3,你应该真正利用它的内置'DependencyResolver'。查看我的答案获取更多信息。 – Charlino

回答

-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 
    } 
+0

谢谢。这是我正在寻找的。如何将它们粘合在一起。现在我在ProjectRepository中遇到了问题。我如何设置它以获取我的实体上下文HTTP请求作用域的默认类型? – Dismissile

+0

根据HTTP请求确定您的上下文范围: http://dotnetslackers.com/articles/ado_net/Managing-Entity-Framework-ObjectContext-lifespan-and-scope-in​​-n-layered-ASP-NET-applications.aspx 使用延迟加载方案的建议也很好。 –

+3

-1对不起,但是这绝对不是用ASP.NET MVC 3进行依赖注入的正确方法。 – Charlino

-1

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

一旦你添加了属性,你放置在你的“地图”中的对象应该注入而不需要额外的工作。如果你不能使用属性,请考虑使用setter。

有结构上的地图站点中的文件: http://structuremap.net/structuremap/ConstructorAndSetterInjection.htm

+0

错误:没有为此对象定义的无参数构造函数。 – Dismissile

+0

我仍然不知道应该在哪里放置配置/设置代码。 – Dismissile

0

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

其他一些依赖注入框架附带自定义控制器工厂,它们将为您执行此操作。 Ninject(开放源代码依赖注入),例如有一个可用于包含此行为的扩展。例如,请参阅here。并here到扩展名。

你也可以使用Unity IOC,这是另一个流行的依赖注入框架,据我所知,你将不得不创建一个自定义控制器工厂(如结构图)来实现这种行为。一个例子见here

你也可以研究所有其他的依赖注入框架,看看你可以得到哪些支持。

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

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

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

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

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

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

+0

我注意到的一件事是如果我的IIS Express端口已经运行,那么容器不会中断。 /App_Start/StructuremapMvc.cs是我把第一个中断点,并感到惊讶,它没有被击中......所以只是一个观察。 –