ASP.NET MVC 控制器激活(二)

前言

在之前的篇幅中,用文字和图像来表示了控制器的激活过程,描述的角度都是从框架默认实现的角度去进行描述的,这样也使得大家都可以清楚的知道激活的过程以及其中涉及到的对象模型,今天的篇幅就是在激活的过程中,框架提供了哪些可注入点,站在一个使用者的角度来进行描述。

激活控制器-注入点入口

如上图,这是上个篇幅中描述的控制器激活过程图,这里引用过来是怕有的朋友忘记了前面的所说和没看过前面篇幅的朋友。

就从默认控制器工厂的实现来看,在CreateController()方法中,通过GetControllerType()方法来获取控制器类型(Type),然后传递到GetControllerInstance()方法中,通过其中的实现来完成根据控制器类型(Type)到IController的生成。而在后续的注入点也是在GetControllerInstance()方法实现中来进行注入的,GetControllerInstance()方法即是整个控制器激活过程的入口点。

IoC示例

既然说到了动态注入,想必就要用到IoC框架了,在MVC学前篇中提到过Ninject的使用,下面这个示例便是依赖于Ninject的来做的演示:

     /// <summary>
     /// 产品实体类
     /// </summary>
     public class Product
     {
         public string ID { get; set; }
         public string Name { get; set; }
     }

定义一个数据实体类没什么好说的,

     /// <summary>
     /// 抽象数据提取库
     /// </summary>
     public interface IDataStandard
     {
         List<Product> GetProducts();
     }
     /// <summary>
     /// 默认实现--数据提取库
     /// </summary>
     public class DataProvide : IDataStandard
     {
 
         public List<Product> GetProducts()
         {
             List<Product> products = new List<Product>() 
             {
                 new Product(){ ID="",Name="name1"},
                 new Product(){ID="",Name="name2"},
                 new Product(){ID="",Name="name3"}
             };
             return products;
         }
     }

这里定义的一个是抽象的数据提取库,和一个默认的实现作为演示用于提供数据用的。

     /// <summary>
     /// 抽象数据调用
     /// </summary>
     public interface IDataCall
     {
         void WriterToMonitor();
     }
 
     /// <summary>
     /// 默认的数据调用实现
     /// </summary>
     public class DefultDataCall:IDataCall
     {
         private IDataStandard _DataStandard;
 
         public DefultDataCall(IDataStandard dataStandard)//使用构造函数方式注入 通过Ninject框架实现
         {
             _DataStandard = dataStandard;
         }
 
         public void WriterToMonitor()
         {
             foreach(var data in _DataStandard.GetProducts())
             {
                 Console.WriteLine("Prodcut ID:" + data.ID + " Name:" + data.Name);
             }
         }
     }

这里定义的是抽象的数据调用和默认的实现,我们现在要做的就是通过IoC框架来让调用客户端对数据调用和数据提取解耦,

 class Program
     {
         static void Main(string[] args)
         {
             IKernel ninject = new StandardKernel();
             ninject.Bind<IDataStandard>().To<DataProvide>();
             IDataCall dataCall = ninject.Get(typeof(DefultDataCall)) as IDataCall;
             if (dataCall != null)
             {
                 dataCall.WriterToMonitor();
             }
 
             Console.ReadLine();
         }
     }

运行这段代码:

很简单明了的一个示例,在MVC的项目中也是这样运行的。

MVC项目中的运用

在上面的章节里说过,入口点在protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType);方法中,我们只需要实现一个默认的控制器工厂类型,并且重写一下这个方法,因为我们已经可以在重写的方法中获取到控制器的类型了,有了它就可以按照IoC示例中的那样来进行其它对象到控制器的一个动态注入。

我们先要定义一个控制器,并且要让它对上述示例中的抽象提取库依赖,采取构造函数式注入(依赖)。

看一下示例:

     public class IoCDemoController : Controller
     {
         //
         // GET: /IoCDemo/
 
         private IDataStandard _DataStandard;
 
         public IoCDemoController(IDataStandard dataStandard)
         {
             _DataStandard = dataStandard;
         }
 
         public ActionResult Index()
         {
             return View(_DataStandard.GetProducts());
         }
     }

在Index方法上右键,点击添加视图:

点击添加,并且在视图中输入如下代码:

@model IEnumerable<ConsoleApplication2.Product>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>
@foreach (var item in Model)
{
   <h3>ID: @item.ID Name:@item.Name</h3>
}

再把Global.asax文件中的路由设置修改一下:

 routes.MapRoute(
                 "Default", // 路由名称
                 "{controller}/{action}/{id}", // 带有参数的 URL
                 new { controller = "IoCDemo", action = "Index", id = UrlParameter.Optional } // 参数默认值
             );

这个时候准备工作都做好,可是控制器中所用的数据哪里来呢?从我们默认实现的控制器工厂中来:

     public class NinjectControllerFactory :DefaultControllerFactory
     {
         private IKernel _NinjectKernel;
 
         public NinjectControllerFactory()
         {
             _NinjectKernel = new StandardKernel();
             _NinjectKernel.Bind<IDataStandard>().To<DataProvide>();
         }
 
         protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
         {
             IController controller = _NinjectKernel.Get(controllerType) as IController;
             if (controller != null) 
             {
                 return controller;
             }
             return null;
         }
 
     }

按照上面章节中的样式,在NinjectControllerFactory中事先绑定数据类型,等到系统执行需要用到控制器的时候会通过Ninject框架来讲数据动态的注入到控制器中。

最后还要设置一项:

在Global.asax文件中的Application_Start()方法中要把我们默认的实现的控制器工厂设置到MVC框架中,

 protected void Application_Start()
         {
             AreaRegistration.RegisterAllAreas();
 
             RegisterGlobalFilters(GlobalFilters.Filters);
             RegisterRoutes(RouteTable.Routes);
             ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
         }

添加上代码的最后一句。现在我们就可以来看一下最终效果了。

本篇就讲到这里,会在下个篇幅中继续讲解其他的注入点。

ASP.NET MVC 学习