MVC项目中使用Entity framework和Ninject 实现松耦合架构

关于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);
        }
结果:

这样就实现了Controller层和具体业务层(PersonManager1)的解耦。如果下次要修改该业务,我只要新增IPerson接口的实现类,如: PersonManager2, PersonManager3(面向对象设计的开闭原则:对于扩展开放,对于修改封闭),然后在Ninject绑定的地方修改成:
Bind(typeof(IPerson<>)).To(typeof(PersonManager2<>));
即可,不需要求修改Controller层的任何代码。






猜你喜欢

转载自blog.csdn.net/sudazf/article/details/47073153
今日推荐