搭建大型MVC实际项目框架-利用Ninject
如何利用Ninject搭建大型MVC实际项目
前言:
学习MVC也有一段时间了,想抽点时间来总结、分享下,但总是被耽搁了,这几天在看一本书《Pro ASP.NET MVC 3》,网上也有中文版《精通ASP.NET MVC3》的书,里面有一个实际项目的搭建,看到后觉得挺有意思的,原来可以这么整~~,于是我自己动手搭建了整个基础框架,项目的整体架构如下图所示:(ps:有部分人看到就一个简单的项目就搞这么多接口,觉得头都大了,我还是建议您认真读完全文,说不定你会从中受益)
如图1
整个架构分三层:
1、Web层,这里主要是Controllers(控制器),Views(视图)
2、Model层,这里主要是Entities(实体)和Repositories(仓储),封装对数据库的操作
3、Service层(服务层),这里主要是调用Models层,实现相关业务逻辑
至于为什么这么分,可能有部分人会认为整复杂了,这个我们在后面具体代码中会进行说明
下面进入实战环节:
首先第一步我们先建一个解决方案,起个通俗易懂的名称(MvcTest)~~,然后“右键”添加一个MVC4.0项目(MvcTest),这一步很简单,我就不贴图了。(选择空的项目)然后我们重复上面步骤,“右键”添加一个类库(MvcTest.Models),同样添加一个类库(MvcTest.Services),到此基本的项目添加完毕(如图1所示)
在Models类库中,我们添加几个文件夹,Entities,Repositories,Repositories,“右键”添加一个类ProductDbContext:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MvcTest.Models.Entities;
namespace MvcTest.Models
{
public class ProductDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
}
}
然后在Entities中添加一个实体类Product:(ps:这里我不得不吐槽下我插入代码,每次都插入到顶部去了,不知道是我不会用还是怎么回事)
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MvcTest.Models.Entities
{
public class Product
{
[Key]
public int ProductId { get; set; }
public string ProductName { get; set; }
}
}
然后在IRepositories中添加一个泛型接口:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MvcTest.Models.Entities;
namespace MvcTest.Models.IRepositories
{
public interface IDataRepository<T> where T : class
{
T[] GetAll();
}
}
然后我们添加一个实现该接口的类ProductRepository:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MvcTest.Models.Entities;
using MvcTest.Models.IRepositories;
namespace MvcTest.Models.Repositories
{
public class ProductRepository : IDataRepository<Product>
{
private ProductDbContext productDbContext = new ProductDbContext();
public Product[] GetAll()
{
return productDbContext.Products.ToArray();
}
}
}
到此该类库的设计已经完成,通过上面的代码我们可以知道,这个类库主要是封装对数据库的操作,在三层中叫(DAL)数据访问层。
下一步我们来完成Services类库的搭建,首先我们新建2个文件夹,IServices和Services,同样和上面步骤一样,在IServices中添加一个接口IDataService:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MvcTest.Models.IRepositories;
using MvcTest.Models.Entities;
namespace MvcTest.Services.IServices
{
public interface IDataService<T> where T : class
{
T[] GetAll();
}
}
然后在Services添加一个实现该接口的类ProductService:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MvcTest.Models.Entities;
using MvcTest.Models.IRepositories;
using MvcTest.Services.IServices;
namespace MvcTest.Services.Services
{
public class ProductService : IDataService<Product>
{
private IDataRepository<Product> productRepository;
public ProductService(IDataRepository<Product> productRepository)
{
this.productRepository = productRepository;
}
public Product[] GetAll()
{
return productRepository.GetAll();
}
}
}
到此该类库的搭建也已经完成,在这里我有几点要说明下:
1、库直接的引用关系:
由于Services层要操作Models层,所有要在添加Models的引用
2、对于上面2个类库,有部分人认为这2个类库的代码完全一样,要实现的功能也一样,我在这里说下我的看法,Models层只是封装对数据库的基本操作(CRUD(增、删、查、改)),而Services层是业务逻辑层,在我这个示例中因为只是作为演示,所以就只是简单的设计,但实际中业务逻辑是复杂的,往往要通过调用Models层不同的方法来进行组合。
下面进入我们最后一个环节,Web层的搭建,这个也是整个项目的重点所在,通过图1我们可以看到Web层和新建的没什么大区别,但仔细一看的话,其中有几个重要的改变。
1、NinjectManage这个文件夹中有一个NinjectControllerFactory类,他继承自DefaultControllerFactory(表示默认情况下已注册的控制器工厂),这个基类是用来生成我们的Controller实例的,我们要做的就是重写里面的方法GetControllerInstance(通过Ninject获取Controller实例)。
2、我们的Web层中引用了Ninject这个IOC组件,简单的引用方法就是右键项目,选择管理NuGet程序包,搜索Ninject,然后在线安装这个组件。
图2
图3
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Ninject;
using Ninject.Modules;
using MvcTest.Models.IRepositories;
using MvcTest.Models.Repositories;
using MvcTest.Services.IServices;
using MvcTest.Services.Services;
using MvcTest.Models.Entities;
namespace MvcTest.NinjectManage
{
public class NinjectControllerFactory : DefaultControllerFactory
{
private IKernel ninjectKernel;
public NinjectControllerFactory()
{
ninjectKernel = new StandardKernel(new MyModule());
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType);
}
}
public class MyModule : NinjectModule
{
public override void Load()
{
Bind<IDataRepository<Product>>().To<ProductRepository>();
Bind<IDataService<Product>>().To<ProductService>();
}
}
}
代码中就是通过Ninject这个Ioc组件进行解耦的,网上也有很多Ioc组件,大家可以去尝试,至于Ninject的具体使用,根据上面代码,我来做一些解释,首先我定义一个MyModule继承自NinjectModule,然后override一下Load方法,在Load里面我们通过Bind方法来表示IDataRepository<Product>和ProductRepository的关系,然后在NinjectControllerFactory 的构造器中我们把MyModule实例化到IKernel 。我们在之前的Services层中ProductService中有这样一段代码:
private IDataRepository<Product> productRepository;
public ProductService(IDataRepository<Product> productRepository)
{
this.productRepository = productRepository;
}
在这个构造函数中,我们和Models层进行交互的只是IDataRepository这个接口,并没有和具体的实现类(可以自由更换)进行挂钩,这种方法叫“构造器注入”(也可以通过属性注入),这样做的体现出这个架构的好处了,我们可以自由替换业务实现类和数据访问实现类。
最后一个步骤就是新建一个HomeController进行测试:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcTest.Models;
using MvcTest.Models.Entities;
using MvcTest.Services.IServices;
namespace MvcTest.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
private IDataService<Product> productService;
public HomeController(IDataService<Product> productService)
{
this.productService = productService;
}
public ActionResult Index()
{
Product[] products = productService.GetAll();
return View(products);
}
}
}
我们在这个控制器中,也是通过“构造器注入”来调用Services层。
最后一点需要注意的是,在Global.asax中:
图4
这一步的作用是我们不是使用默认的DefaultControllerFactory来进行Controller实例的获取,而是我们继承该类的子类NinjectControllerFactory
结尾:
至此,整个项目的搭建基本完成,如果大家有什么问题,可以在下方留言,如果您觉得好,请不要吝啬您右小脚轻轻一点“推荐”,您的鼓励是我前进的动力,谢谢!
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。