简体   繁体   English

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

[英]Dependency Injection / Constructor Injection Help

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 controller:我的 controller:

 // 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.我需要设置我的依赖注入,以便它在 ProjectRepository 中传递到我的 Controller并且它需要在我的实体框架上下文中传递到项目存储库中。 I need to Entity Context to be HTTP Request scoped.我需要实体上下文是 HTTP 请求范围。

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.我也不明白没有默认构造函数的 MVC 将如何工作。

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

If you are using MVC 3, to do things properly, you should make use of the built in dependency resolution bits.如果您使用的是 MVC 3,为了正确地做事,您应该使用内置的依赖解析位。 I would highly recommend you read through the series of blog posts from Brad Wilson (member of the ASP.NET MVC team).我强烈建议您阅读Brad Wilson(ASP.NET MVC 团队成员)的系列博客文章

As far as a StructureMap specific implementation, I found the following blog posts helpful.至于特定于 StructureMap 的实现,我发现以下博客文章很有帮助。

StructureMap and ASP.NET MVC 3 – Getting Started StructureMap 和 ASP.NET MVC 3 – 入门
StructureMap, Model Binders and Dependency Injection in ASP.NET MVC 3 StructureMap、Model ASP.NET MVC 3 中的绑定器和依赖注入
StructureMap, Action Filters and Dependency Injection in ASP.NET MVC 3 ASP.NET MVC 3 中的 StructureMap、动作过滤器和依赖注入
StructureMap, Global Action Filters and Dependency Injection in ASP.NET MVC 3 ASP.NET MVC 3 中的 StructureMap、全局操作过滤器和依赖注入

Anyway, here's some code.无论如何,这里有一些代码。 To start with, I would suggest that you install the StructureMap-MVC3 NuGet package .首先,我建议您安装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 /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 - this is your MVC 3 IDependencyResolver implementation. /IoC/SmDependencyResolver.cs - 这是您的 MVC 3 IDependencyResolver 实现。 It's used in the App_Start code above.它在上面的 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 - this is where you setup your container... also used in the App_Start code. /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;
        }
    }
}

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.在您的Global.asax中,我们需要确保您处理 HttpContext 范围内的所有内容。

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.所以你应该可以通过构造函数注入来实现依赖注入,这是go关于处事的正确方法。

If you are set on using StructureMap, here is a tutorial on the setup that you will probably need.如果您准备使用 StructureMap, 这里有一个您可能需要的设置教程。

Some other dependency injection frameworks come with custom controller factories which will do that for you.其他一些依赖注入框架带有自定义的 controller 工厂,它们将为您完成这些工作。 Ninject (open source dependency injection), for example has an extension that you can use that contains this behaviour.例如, Ninject (开源依赖注入)有一个您可以使用的扩展,其中包含此行为。 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.您还可以使用Unity IOC ,它是另一个流行的依赖注入框架,据我所知,您必须创建一个自定义 controller 工厂(如结构图)来实现此行为。 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. MVC 使用 controller 工厂,该工厂负责在发出请求时实例化所需的相应控制器。 By default, it will initialize a controller by calling its parameterless constructor.默认情况下,它将通过调用其无参数构造函数来初始化 controller。

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.这就是依赖注入容器的用武之地:本质上,DI 容器(如果配置正确)知道如何解决这些依赖关系,您的自定义工厂将利用它来请求注册的依赖关系并将其传递给 controller 构造函数。

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].在结构 map 中,最简单的方法是使用属性:[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.如果不能使用属性,请考虑使用 setter。

There is a file on the structure map site: http://structuremap.net/structuremap/ConstructorAndSetterInjection.htm结构上有一个文件 map 站点: http://structuremap.net/structuremap/ConstructorAndSetterInjection.htm

When using StructureMap I would generally have something like this in my controller:使用 StructureMap 时,我的 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.如果 TryGetInstance 返回 null(因为没有为该类型设置任何内容),它将默认为您指定的具体类型。

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:现在您在 Global.asax Application_Start 事件中调用此引导程序:

    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
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM