65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Unity IoC 容器在 MVC 中实现依赖注入

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.70/5 (21投票s)

2014年7月15日

CPOL

3分钟阅读

viewsIcon

128350

在这篇文章中,我们将学习如何使用 Unity 容器实现依赖注入。

使用 Unity IoC 容器在 MVC 中实现依赖注入

如果您有软件设计经验,并且遵循(或至少尝试遵循)完美的設計模式和原则,那么依赖注入、解耦架构和控制反转 (IoC) 对您来说是非常常见的术语。您可能在各种编程语言中(使用或不使用任何 IoC 容器)拥有依赖注入实现的实践经验。在本文中,我们将讨论依赖注入和 IoC 容器之间的关系,然后我们将了解 IoC 容器和依赖注入如何并排工作。

什么是 IoC 容器?

DI 容器或 IoC 容器是一个软件框架,用于创建依赖项并在需要时自动注入它们。说实话,有些依赖项容器在需要解决依赖项时就像魔法一样工作。市场上有许多这样的框架可以简化我们的工作,这里有一些在 .NET 环境中工作的框架。因此,选择完全取决于您,它们各自都有自己的基准和性能,因此请根据您的项目和要求明智地选择。由于本文并非专门针对 IoC 容器,因此我不会过多讨论它。在本文中,我们将使用 Unity 作为 IoC 容器来解决依赖关系。

让我们实现带有依赖项的控制器

正如标题所示,我们将实现 MVC 架构以帮助理解这个概念,然后我们将创建两个控制器,每个控制器都有一些依赖项,并了解如何使用 IoC 容器来解决这些依赖项。以下是第一个控制器的代码。实现非常简单,我们创建了一个 `Ilogger` 接口,并且 `ILogger` 有两个实现:一个是 `ActualLogger`,它尚未完成,作为替代,我们实现了另一个类 `FakeLogger`,我们将在代码开发中使用它。

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

namespace MVCIoC.Controllers
{
    public interface ILogger
    {
        Boolean Log();
    }
    public class ActualLogger : ILogger
    {
        public Boolean Log()
        {
            throw new NotImplementedException();
        }
    }

    public class FakeLogger : ILogger
    {
        public Boolean Log()
        {
            return true;
        }
    }

    public class HomeController : Controller
    {
        public readonly ILogger logger = null;
        public HomeController(ILogger tmpLogger)
        {
            logger = tmpLogger;
        }
        public ActionResult Index()
        {
            //perform logging information
            logger.Log();
            return View();
        }
    }
}

所以,我们的想法是,如果我们的实际实现处于开发阶段,我们仍然可以使用某种替代方法继续我们的控制器开发,并且及时可以更改它而不会影响现有代码。因此,我们看到 Home 控制器有一个参数化构造函数。作为参数,它将采用依赖对象的实际实现。让我们创建一个另一个控制器;实现将与上面的完全相同。我只是想展示如何在多个控制器中解决依赖关系。

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

namespace MVCIoC.Controllers
{
    public interface IService
    {
        Boolean Call();
    }
    public class RealService : IService
    {
        //Real service is not developed yet.
        public bool Call()
        {
            throw new NotImplementedException();
        }
    }
    public class FakeService : IService
    {
        public bool Call()
        {
            return true;
        }
    }
    public class ValueController : Controller
    {
        public readonly IService service = null;

        public ValueController(IService tmpService)
        {
            service = tmpService;
        }

        public ActionResult Index()
        {
            //User service to implement business logic
            service.Call();
            return View();
        }
	}
}

好了,我们已经设置好了一切。现在我们将引用 Unity 框架。如果您使用的是 Visual Studio,则可以使用 NuGet 包管理器来引用。我以下面的引用为例

现在,我们将从 `DefaultControllerFactory` 类实现我们自己的控制器工厂类。您还可以实现其他选项。例如,`IController` 执行相同的操作。在这里,我们定义了 `ControllerFactory` 并从 `DefaultControllerFactory` 类派生。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.Practices.Unity;
using MVCIoC.Controllers;

namespace MVCIoC.Models
{
    public class ControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            try
            {
                if (controllerType == null)
                    throw new ArgumentNullException("controllerType");

                if (!typeof(IController).IsAssignableFrom(controllerType))
                    throw new ArgumentException(string.Format(
                        "Type requested is not a controller: {0}",
                        controllerType.Name),
                        "controllerType");

                return MvcUnityContainer.Container.Resolve(controllerType) as IController;
            }
            catch
            {
                return null;
            }

        }
    }

    public static class MvcUnityContainer
    {
        public static UnityContainer Container { get; set; }
    }

}

您可以看到我们正在 try 块中解决依赖关系,它将通过解决依赖关系来创建一个控制器对象。对于 Unity 容器,我们将创建一个 `Bootstrapper` 类来在一个文件中设置所有依赖项。请查看下面的代码。虽然实现 Bootstrapper 不是强制性的,但实现它始终是一个好习惯。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Mvc;
using MVCIoC.Controllers;

namespace MVCIoC.Models
{
    public class Bootstrapper
    {
        public static IUnityContainer Initialise()
        {
            var container = BuildUnityContainer();
            DependencyResolver.SetResolver(new UnityDependencyResolver(container));
            return container;
        }
        private static IUnityContainer BuildUnityContainer()
        {
            var container = new UnityContainer();
            container.RegisterType<IService, FakeService>();
            container.RegisterType<ILogger, FakeLogger>();
            MvcUnityContainer.Container = container;
            return container;
        }
    }  
}

请注意,我们已将 `FakeService` 和 `FakeLogger` 与其接口映射在一起,因为我们的目标是使用 `FakeService` 和 `FakeLogger` 的实现。一旦完成原始实现的开发,您只需在此处更改,无需触摸您的控制器代码。现在最终实现是注册。我们必须在 MVC 通道中注册我们的 IoC 容器。只需修改您的 `Application_Start()` 代码,如下所示。

		protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            //Initialize IoC container/Unity
            Bootstrapper.Initialise();
            //Register our custom controller factory
            ControllerBuilder.Current.SetControllerFactory(typeof(ControllerFactory));
        }

现在,是时候运行应用程序了。一旦我们调用 Home 控制器,您将看到 `FakeLogger` 的对象已注入到 Home 控制器的构造函数中。

总结

市场上有很多 IoC 容器。您可以选择其中任何一个,所有 IoC 容器的工作方式都大致相同。

© . All rights reserved.