关于Entity framework和Ninject是什么,此处省略一万个字。
这里记录下传统代码架构和使用Ioc工具后的松耦合架构:
以一个简单的示例说明:
新建一个空解决方案,添加MVC项目(这里用的是MVC4),为了方便演示,直接选择Internet应用程序:
再添加业务逻辑层(BLL)和数据访问层(DAL)两个类库项目(也可以放在MVC的model下,因为MVC的model层就类似三层架构中的BLL+DAL+Model) 如下:
然后在数据库里添加一个persons表,随便手动输入几条测试数据:
在DAL项目下新建一个文件夹,加入一个实体数据库模型edmx文件,把上面的表加进来(这里为了方便演示,就不用code first了):
修改HomeController中Index方法对应的视图文件为:
@model IEnumerable<DAL.EF_Model.PersonInfo> @{ ViewBag.Title = "Index"; } <h2>Index</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table> <tr> <th> @Html.DisplayNameFor(model => model.Name) </th> <th> @Html.DisplayNameFor(model => model.Age) </th> <th> @Html.DisplayNameFor(model => model.Sex) </th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Name) </td> <td> @Html.DisplayFor(modelItem => item.Age) </td> <td> @Html.DisplayFor(modelItem => item.Sex) </td> </tr> } </table>
然后,要做一个简单的功能,就是查出满足条件的人员名单,一般有如下几种做法:
一.传统做法:
在BLL中加入代码:
public class PersonBL<T> where T:class,new() { public IEnumerable<T> GetPersons(Expression<Func<T,bool>> perdicate) { return new PersonDA<T>().GetPersons(perdicate); } }在DAL中加入代码:
public class PersonDA<T> where T:class,new() { private MyDbEntities dbContext = new MyDbEntities(); public IEnumerable<T> GetPersons(System.Linq.Expressions.Expression<Func<T, bool>> perdicate) { return dbContext.Set<T>().Where(perdicate).Select(p => p).ToList(); } }最后在HomeController修改Index方法为:
public ActionResult Index() { IEnumerable<PersonInfo> table = new PersonBL<PersonInfo>().GetPersons(person => person.Sex == "男"); return View(table); }最后运行结果:
这种做法实现了controller层和BL,DA层的解耦,但是并没有面向接口,也就是说所有的数据库操作都在DA层进行。关于面向接口编程的好处,直接百度,此处省略一万字。
二.第二种面向接口做法:
在解决方案下新增一个接口类库项目,并新增一个公共接口文件IRepository(名字随便取,最好以i开头表示interface):
接口代码为(这里只实现了部分增删改查,针对不同情况可自行补充):
public interface IRepository<T> where T : class,new() { //增C void Create(T entity); void Create(IEnumerable<T> entities); //查R T Get(Expression<Func<T, bool>> predicate); IEnumerable<T> Fetch(Expression<Func<T, bool>> predicate); //改U void Update(T entity); void Update(IEnumerable<T> entities); //删D void Delete(T entity); void Delete(IEnumerable<T> entities); void Delete(Expression<Func<T, bool>> predicate); }然后新增一个继承该接口的person接口:
public interface IPerson<T>:IRepository<T> where T:class,new() { //TODO //此处添加其它接口方法 }
最后在BLL下新增一个继承IPerson接口的实现类PersonManager1:
public class PersonManager1<T> : IPerson<T> where T : class,new() { private MyDbEntities dbContext = new MyDbEntities(); public PersonManager1() { //dbContext.Database.Connection.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["mycon"].ConnectionString; } private DbSet<T> DbTable { get { return dbContext.Set<T>(); } } #region IRepository<T> 成员 #region 常规CRUD public void Create(T entity) { DbTable.Add(entity); dbContext.SaveChanges(); } public void Create(IEnumerable<T> entities) { if (entities.ToList().Count > 0) { DbTable.AddRange(entities); dbContext.SaveChanges(); } } public T Get(System.Linq.Expressions.Expression<Func<T, bool>> predicate) { return Fetch(predicate).FirstOrDefault(); } public IEnumerable<T> Fetch(System.Linq.Expressions.Expression<Func<T, bool>> predicate) { return DbTable.Where(predicate); } public void Update(T entity) { DbTable.Attach(entity); dbContext.Entry(entity).State = EntityState.Modified; dbContext.SaveChanges(); } public void Update(IEnumerable<T> entities) { foreach (var item in entities) { DbTable.Attach(item); dbContext.Entry(item).State = EntityState.Modified; } dbContext.SaveChanges(); } public void Delete(T entity) { DbTable.Attach(entity); dbContext.Entry(entity).State = EntityState.Deleted; dbContext.SaveChanges(); } public void Delete(IEnumerable<T> entities) { foreach (var item in entities) { DbTable.Attach(item); dbContext.Entry(item).State = EntityState.Deleted; } dbContext.SaveChanges(); } public void Delete(Expression<Func<T, bool>> predicate) { var delEntities = Fetch(predicate).ToList();//让其立即查询 foreach (var item in delEntities) { Delete(item); } } #endregion #endregion }最后将HomerController的Index方法修为:
public ActionResult Index() { //IEnumerable<Persons> table = new PersonBL<Persons>().GetPersons(person => person.Sex == "男"); IPerson<Persons> persons = new PersonManager1<Persons>(); IEnumerable<Persons> table=persons.Fetch(person => person.Sex == "女"); return View(table); }查询结果:
这种做法实现了面向接口方式,但是带来一个问题就是和Controller层耦合了,也就是说如果以后我修改了PersonManager1类,那么相应的Controller层也要对应修改。为了实现和Controller层解耦,就要用到DI,也就是Ioc工具,这类工具有很多,如AutoFac,Spring.Net,这里使用一个轻量级的名叫:Ninject
这个工具直接在NuGet下可以下载:
安装到当前项目下,新建一个Factory项目,并增加一个NInjectFactory类:
Ninject可以从构造函数注入或者直接绑定:
1)构造函数注入:
//构造函数注入(不支持Get取得绑定的类型,不推荐) public class NinjectControlFactory : DefaultControllerFactory { private IKernel ninectKernel; public NinjectControlFactory() { ninectKernel = new StandardKernel(); AddBindings(); } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { return controllerType == null ? null : (IController)ninectKernel.Get(controllerType); } private void AddBindings() { //ninectKernel.Bind<IProduct>().To<ProductNameA>(); //不带泛型约束注入 ninectKernel.Bind(typeof(IPerson<>)).To(typeof(PersonManager1<>));////带泛型约束注入 } }还需要注册绑定:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); //构造函数注入(不推荐) ControllerBuilder.Current.SetControllerFactory(new NinjectControlFactory()); }
修改Controller中的方法:
private IPerson<Persons> _iPerson; public HomeController(IPerson<Persons> iPerson) { _iPerson = iPerson; } public ActionResult Index() { //IEnumerable<Persons> table = new PersonBL<Persons>().GetPersons(person => person.Sex == "男"); //IPerson<Persons> persons = new PersonManager1<Persons>(); //IEnumerable<Persons> table=persons.Fetch(person => person.Sex == "女"); IEnumerable<Persons> table = _iPerson.Fetch(person=>person.Age>=40); return View(table); }
最后直接运行即可:
但是这种方式我不太喜欢,破坏构造函数,而且不能动态Get到具体类型,不是很好,下面使用另一种方式。
2)重写NinjectModule的Load方式:
//属性、字段、参数式注入(支持Get取得绑定的类型) public class MyBindModule : NinjectModule { public override void Load() { Bind(typeof(IPerson<>)).To(typeof(PersonManager1<>));//带泛型约束注入 } }然后在controller中:
private IPerson<Persons> _iPerson; public HomeController() { IKernel kernal = new StandardKernel(new MyBindModule()); _iPerson = kernal.Get<IPerson<Persons>>(); } public ActionResult Index() { //IEnumerable<Persons> table = new PersonBL<Persons>().GetPersons(person => person.Sex == "男"); //IPerson<Persons> persons = new PersonManager1<Persons>(); //IEnumerable<Persons> table=persons.Fetch(person => person.Sex == "女"); IEnumerable<Persons> table = _iPerson.Fetch(person=>person.Age<=30); return View(table); }结果:
Bind(typeof(IPerson<>)).To(typeof(PersonManager2<>));即可,不需要求修改Controller层的任何代码。