asp.net mvc 简单项目框架的搭建过程(一)对Bll层和Dal层进行充分解耦
学习asp.net 已经有近三个月的时间了,在asp.net mvc上花的时间最多,但个人真是有些菜,不得不说,asp.net mvc的水真的还是蛮深的。目前在公司实习,也见过公司几个项目的代码了。对项目的代码始终停留在一知半解的地步,能改一些简单的bug,但关于项目的来龙去脉始终云里雾里。对于asp.net mvc的架构始终看不懂。因此,照着传智博客的学习视频,学了一下简单的架构搭建。真个架构的搭建我看了将近两遍视频,才稍稍有些头绪,今天在这里记录一下,一方面加深理解,一方面如果以后忘记了,还能快速的想起来,当然如果我的这篇简陋的随笔能有幸被有需要的人看见,并对他们产生一些帮助,我心里肯定也是非常欢欣的。
好了,闲言少叙,那我就开始写了。
本篇博客我主要想讲如何使用 asp.net mvc + EF + spring.net 搭建一个简单的项目框架,我也并不是对前前后后所有的内容都理解,有的地方是囫囵吞枣,水平太菜,见谅。
在开始搭框架之前,先做一些准备工作:
(1)新建解决方案:
新建一个解决方案,然后在解决方案下间五个文件夹,分别为 Common、Bll、Dal、Model、UI.如下图所示:
(说明:我的项目命名为IotPf.Proj,因为之前做过一个物联网云平台的项目,这里打算用.net重做实践一下,练练手)
(2)在Model层中新建类库 Model,然后添加EF数据库实体
添加实体的操作我就不啰嗦了,我以前的博客有讲到。结果如下:
(3)分别在Bll里新建类库 XXX.Bll 和 XXX.IBll,在Dal里新建XXX.Dal和XXX.IDal,如图所示:
(4)在UI下面新建应用程序IotPf.UI
好了,准备工作已经做好了,下面进入正题。
1.首先在IofPf.Dal中新建UsersDal.cs文件,这个里面写对于User表的增删改查等系列数据库操作。如下入图所示:
下面列出在UserDal中写的关于增删改查几个最常用操作的最基本写法,然后对其进行调用(当然只是那种最简单的调用,不涉及如和进行解耦的操作):
看代码:
public class UserDal
{
IotPfModel context = new IotPfModel();
//使用lambda表示式查询
public IQueryable<Users> GetUsersByLambda(Expression<Func<Users,bool>> wherelambda)
{
return context.Users.Where(wherelambda).AsQueryable(); } //分页查询
#region 增删改
public Users AddUser(Users user)
{
context.Users.Add(user);
context.SaveChanges();
return user;
} public bool DeleteUser(Users user)
{
context.Entry(user).State = System.Data.Entity.EntityState.Deleted;
return context.SaveChanges() > ;
} public bool UpdateUser(Users user)
{
context.Entry(user).State = System.Data.Entity.EntityState.Modified;
return context.SaveChanges() > ;
}
#endregion
}
UserDal
上面的代码写了增删改查四个方法,其中查询的方法是根据lambda表达式自定义查询条件。
上面的代存在一下几个方面的问题:(以很通俗化的语言描述,不写菜鸟看不懂的话)
(1)首先,UserDal中对数据库进行操作首先要做的就是实例化上下文。目前,我们使用的是单一的EF操作数据库,所以实例化的就是EF的上下文,但是,操作数据的方式不止EF这一种,如果有一天我们的项目突然要求要用NH(另一种操作数据库的方式,但我没学过)操作数据库,那么如果我们像这样写的话,麻烦就大了。我们的项目肯定不会只有一个XXXDal,有很多个的,如果上下文变了,那我们就要改好多个XXXDal里的代码,这就牵一发而动全身了,相当麻烦,这就是第一个问题。
解决方法:可以把实例化上下文的操作单独的放在一个类中,在类中定义一个 GetDalContext()的方法,然后所有的XXXDal都可以调用这个方法,如果上下文变了,那么只需要改这一个文件就可以了,这就灵活一些了。
具体做法:
在IotPf.Dal中新建DbContextFactory.cs文件,然后编写代码如下:
public class DbContextFactory
{
public static DbContext GetCurrentDbContext()
{
return new IotPfModel();
}
}
优化后的写法如下:
public static DbContext GetCurrentDbContext()
{
DbContext context = CallContext.GetData("DbContext") as DbContext;
if (context == null)
{
context = new IotPfModel();
CallContext.SetData("DbContext", context);
} return context;
}
(别问我为啥这么优化,尴尬的告诉你,本菜鸡不知道啊!)
(2)其次,UserDal中的这几个方法是最最常用的方法,几乎每个XXXDal中都会用到这四个方法,那我们就在每个XXXDal中都写一遍吗?这显然是不合理的,这是无用功;
解决方法:常用的公共的方法,可以封装到一个基类中,所有需要用到的类都继承这个基类就可以了;
具体做法:
在IotPf.Dal下新建一个BaseDal.cs,然后编辑代码如下:
public class BaseDal<T> where T : class ,new()
{
//实例化上下文
DbContext context = DbContextFactory.GetCurrentDbContext();
//使用lambda条件进行查询
public IQueryable<T> GetEntityByLambda(Expression<Func<T,bool>> wherelambda)
{
return context.Set<T>().Where(wherelambda).AsQueryable();
} //增删改
#region 增删改
public T AddEntity(T entity)
{
context.Set<T>().Add(entity);
context.SaveChanges();
return entity;
}
public bool DeleteEntity(T entity)
{
context.Entry<T>(entity).State = EntityState.Deleted;
return context.SaveChanges() > ;
}
public bool UpdateEntity(T entity)
{
context.Entry<T>(entity).State = EntityState.Modified;
return context.SaveChanges() > ;
}
#endregion
}
然后,在UserDal中的代码就可以注释掉了,直接让UserDal继承BaseDal就可以了,其他的XXXDal也是如此:
public class UserDal:BaseDal<Users>
2.在IofPf.Bll中编写业务逻辑层代码
在业务逻辑层中新建UserService.cs文件,然后书写一个添加数据的方法:
public class UserService
{
UserDal userDal = new UserDal();//这个地方问题严重
public Users AddUser(Users user)
{
userDal.AddEntity(user);
return user;
}
}
好了,问题又来了,又到了一个要解耦的地方。
UserDal userDal = new UserDal();//这个地方问题严重
着重要讲的是这一句:
(1)首先,Bll层(UserService)中直接用了Dal层中的类(UserDal),这使得两层之间的联系很紧,耦合度太高,只要UserDal有任何变化,那么Bll层就要进行相应的改动。
改进1:在Dal层和Bll层之间添加接口层IDal,以后调用XXXDal的时候,使用IXXXDal进行调用,这样就用接口层把两个层隔离开来了:
具体做法:
在IotPf.IDal下新建接口文件IUserDal.cs,然后让UserDal继承IUserDal,当然,BaseDal的接口也是同理,下面给出代码如下:
IBaseDal:
public interface IBaseDal<T> where T : class,new()
{
IQueryable<T> GetEntityByLambda(Expression<Func<T, bool>> wherelambda); T AddEntity(T entity); bool DeleteEntity(T entity); bool UpdateEntity(T entity);
}
IUserDal:
public interface IUserDal:IBaseDal<Users>
{ }
UserDal和BaseDal都要继承IUserDal和IBaseDal才行哦,不然调不动的呢。
使用接口后则调用过程变为这样:
IUserDal userDal = new UserDal();
其实像这样写问题仍然蛮严重的。为啥呢?且看第二个问题:
(2)我们肯定不止一个文件会用到UserDal吧,实际上很多个文件都会用到UserDal。但是,如果有一天,我的Dal要由EF写的UserDal换为NH写的NHUserDal,那可怎么整呢,我难道要在每个用到UserDal的文件里分别去把new UserDal() 换为 new NHUserDal()?,显然不啊,和上面那个上下文的类似,我们新建一个类,把new UserDal的操作单独取出来,放在一个单独的方法中,这样只需要改一个地方就可以了。
具体做法:
在IotPf.Factory下新建AbstractFactory.cs,然后在里面添加如下代码:
public class AbstractFactory
{
public static IUserDal GetCurrentUserDal()
{
return new UserDal();
}
}
然后调用过程进一步改进为如下:
IUserDal userDal = AbstractFactory.GetCurrentUserDal();
这样就要灵活多了。
这种做法其实就是工厂的概念啦。
但是,这样仍然不完美的啦,是不是很恼火,怎么这么麻烦,还能不能愉快的写代码了。哈哈哈,莫慌,麻烦是麻烦,但这种思想还是很牛逼的,好处多多哇。下面看问题:
(3)我接下来说 AbstractFactory.cs里面的不足之处。什么不足之处呢?
现在我只写了一个GetCurrentUserDal() 对吧,这改起来倒是方便了,但是做项目不可能只有一个UserDal啊,以后肯定会有 ADal啊,BDal,CDal等各种Dal,一旦EF变为NH,那在这个工厂里不是又要改一堆名字了吗?这也太烦了啊。那怎么弄呢?看下面:
我们可以把所使用的数据库操作方式(EF还是NH还是Ado.net)写进一个配置文件里面。假设我现在有两种方式操作数据库,EF和NH,一种操作数据库的方法写在项目EFDal下,另一种写在NHDal项目下,然后让两个项目下各个操作方法的名字保持一致。如果要用EF操作,那就在配置文件中引用 EFDal,如果用NH操作,那就在配置文件中引用NHDal,这样就可以切换自如了,是不是很方便呢,哈哈,确实挺溜的呀。
具体做法:
首先,在Ui下的web.config文件中此处添加如下代码:
代码:
<add key="DalAssemblyName" value="IotPf.Dal"/>
然后在 AbstractFactory.cs下添加如下代码:
//配置文件设置
public static string AssemblyName = System.Configuration.ConfigurationManager.AppSettings["DalAssemblyName"];
public static IUserDal GetUserDal()
{
//配置文件设置 ,移到方法外面,会被多个GetXXXDal使用
// string AssemblyName = System.Configuration.ConfigurationManager.AppSettings["DalAssemblyName"];
return Assembly.Load(AssemblyName).CreateInstance(AssemblyName + ".UserDal") as IUserDal;
}
这里因为使用了配置文件,所以不要忘记添加下面这个引用哦:
这就是所谓的 抽象工厂的概念啦。
就像我现在用的是IotPf.Dal,那我在配置文件里的value那个地方就写IotPf.Dal,以后如果我需要用IotPf.XXXDal,那我就把value改为IotPf.XXXDal就好了。
3.下面我们来讲一下DbSession
DbSession是啥呢?说起DbSession那就要从上面的UserService接着讲啦。下面要说一下 上下文的 context.SaveChanges()了。
我们应该都知道的,如果我们操作上下文对数据库进行了 增、删、改的操作,那么操作结束后需要有一个 context.SaveChanges()的操作,用来把对实体的修改保存到数据库中。目前,我们这个操作是写在UserDal中的。但是,这样做有一个问题,什么问题呢,我们先看一下下面这段代码:
IUserDal userDal = AbstractFactory.GetUserDal();
public Users AddUser(Users user)
{
userDal.AddEntity(user);
userDal.DeleteEntity(user);
userDal.UpdateEntity(user);
return user;
}
这段代码中调用了三个UserDal中的方法,分别对数据库进行了三次不同的操作,每次操作之后相应的都会执行一次context.SaveChanges()的操作,也就是说与数据库交互了三次。有没有觉得三次有点多了呢,明明可以更少的呀,以后如果代码量大了,可能会交互更多次,那性能就很差了呀。这里呀,我们明明可以只交互一次就搞定的呀,怎么做呢,我们可以在把SaveChanges()的操作从UserDal中迁移到UserService中的呀,在UserDal中的每个方法先不写context.SaveChanges()的操作,而是在UserService中把增删改这单个操作一起完成之后再统一进行提交,那岂不是更好吗。就像下面这样:
(此代码仅说明统一提交的意思,不是实际程序中的写法,看这个的同学不要粘这段代码)
IUserDal userDal = AbstractFactory.GetUserDal();
public Users AddUser(Users user)
{
userDal.AddEntity(user);
userDal.DeleteEntity(user);
userDal.UpdateEntity(user);
context.SaveChanges();//统一提交
return user;
}
下面具体来讲做法:
首先,在IotPf.Factory下新建类 DbSession.cs,然后在里面编写如下代码:
public class DbSession
{
//声明UserDal
public IUserDal UserDal
{
get
{
return AbstractFactory.GetUserDal();
}
}
//保存实体
public int SaveChanges()
{
return DbContextFactory.GetCurrentDbContext().SaveChanges();
}
}
看到没,声明UserDal的操作现在搬到DbSession中了。
然后,UserService.cs现在变成下面这种写法:
public class UserService
{ //IUserDal userDal = AbstractFactory.GetUserDal();
DbSession dbSession = new DbSession();
public Users AddUser(Users user)
{
dbSession.UserDal.AddEntity(user);
dbSession.UserDal.DeleteEntity(user);
dbSession.UserDal.UpdateEntity(user);
dbSession.SaveChanges();
return user;
}
}
保存操作已经不再由Dal层控制了,而是提交到Bll层,由Bll层控制。当然,不要忘记回到Dal层,把原来代码中的SaveChanges操作去掉:去掉后代码如下:
public class BaseDal<T> where T : class , new()
{
//实例化上下文
DbContext context = DbContextFactory.GetCurrentDbContext();
//使用lambda条件进行查询
public IQueryable<T> GetEntityByLambda(Expression<Func<T,bool>> wherelambda)
{
return context.Set<T>().Where(wherelambda).AsQueryable();
} //增删改
#region 增删改
public T AddEntity(T entity)
{
context.Set<T>().Add(entity);
// context.SaveChanges();
return entity;
}
public bool DeleteEntity(T entity)
{
context.Entry<T>(entity).State = EntityState.Deleted;
// return context.SaveChanges() > 0;
return true;
}
public bool UpdateEntity(T entity)
{
context.Entry<T>(entity).State = EntityState.Modified;
// return context.SaveChanges() > 0;
return true;
}
#endregion
}
现在是不是就好多了,以后与数据库交互的次数就大大减少了,这样性能应该能提高不少吧,哈哈。
紧接着,我们需要将DbSession和Bll层进行隔离,也就是这个操作:
现在应该已经轻车熟路了,先新建接口层 IDbSession.cs ,然后新建DbSessionFactory.cs,下面给出代码:
IDbSession.cs
public interface IDbSession
{
IUserDal UserDal { get; }
int SaveChanges();
}
DbSessionFactory.cs:
public static IDbSession GetCurrentDbSession()
{
IDbSession dbSession = CallContext.GetData("DbSession") as IDbSession;
if (dbSession == null)
{
dbSession = new DbSession();
CallContext.SetData("DbSession", dbSession);
}
return dbSession;
}
现在UserService.cs中的代码就变成这样:
IDbSession dbSession = DbSessionFactory.GetCurrentDbSession();
public Users AddUser(Users user)
{
dbSession.UserDal.AddEntity(user);
dbSession.UserDal.DeleteEntity(user);
dbSession.UserDal.UpdateEntity(user);
dbSession.SaveChanges();
return user;
}
写到这里,数据访问层Dal和业务逻辑成Bll之间的解耦基本已经说完了,我感觉应该还是讲的比较清楚的了哈。
这个框架搭建的前半部分就已经讲完了,后面关于业务逻辑层Bll和展示层UI之间的系列操作,我留到下一篇讲啦,哈哈,一下子写完我自己hold不住,你估计也看不下去了吧。
下面总结一下哈:
本篇内容扯了那么长,我也真是够有尿性的了,总结起来其实也就几个内容而已啦:
(1)使用接口层进行层与层之间的隔离;
(2)对重复多次使用的方法进行基类封装;
(3)使用工厂的概念,对数据库操作方法进行配置,便于应对数据库的更换,提高框架灵活性;
(4)使用DbSession将数据库保存操作的权限有Dal层提交到业务逻辑层,减少与数据库交互次数,提高代码性能;
下一篇将介绍spring.net的使用,虽然我自己也不咋会 ,但还是把自己理解的写出来吧,以写代练,加深理解,大神们不要见笑啊。
我的eMail:3074596466@qq.com
完成框架代码就不往上贴了哈,有需要的同学可以给我发邮件索取。
明天就是国庆节啦,提前祝大家国庆节快乐,祝祖国繁荣昌盛!
asp.net mvc 简单项目框架的搭建过程(一)对Bll层和Dal层进行充分解耦的更多相关文章
- asp.net mvc 简单项目框架的搭建(二)—— Spring.Net在Mvc中的简单应用
摘要:上篇写了如何搭建一个简单项目框架的上部分,讲了关于Dal和Bll之间解耦的相关知识,这篇来把后i面的部分说一说. 上篇讲到DbSession,现在接着往下讲. 首先,还是把一些类似的操作完善一下 ...
- ASP.NET MVC企业级项目框架
ASP.NET MVC企业级项目框架 MVC项目搭建笔记---- 项目框架采用ASP.NET MVC+Entity Framwork+Spring.Net等技术搭建,搭建过程内容比较多,结合了抽象工厂 ...
- ASP.NET MVC 简单介绍①
ASP.NET MVC 简单介绍① 只做了重要描述,内容出自菜鸟教程网站内容. 目录 1布局 2HTML 帮助器 3.Razor 语法 4.添加样式 5.Layout 6. Controllers ...
- angular2+typescript在asp.net MVC Web项目上的实现
网上现在还没有关于angular2+typescript在asp.net mvc web项目上的实现的系统介绍,这里我也只是探索到了一个简单的方式,还有很多问题没能解决.但是能有个好的开头也值得记录一 ...
- 使用Visual Studio 2015 开发ASP.NET MVC 5 项目部署到Mono/Jexus
最新的Mono 4.4已经支持运行asp.net mvc5项目,有的同学听了这句话就兴高采烈的拿起Visual Studio 2015创建了一个mvc 5的项目,然后部署到Mono上,浏览下发现一堆错 ...
- 用开源 ASP.NET MVC 程序 Bonobo Git Server 搭建 Git 服务器(转)
用开源 ASP.NET MVC 程序 Bonobo Git Server 搭建 Git 服务器 现在不用Git,都不好意思说自己是程序员. 当你想用Git,而源代码服务器是Windows系统时,你 ...
- 项目一:项目第二天 Jquery ztree使用展示菜单数据 2、 基础设置需求分析 3、 搭建项目框架环境--ssh(复习) 4、 SpringData-JPA持久层入门案例(重点) 5、 Easyui menubutton菜单按钮使用 6、 Easyui messager消息框使用
1. Jquery ztree使用展示菜单数据 2. 基础设置需求分析 3. 搭建项目框架环境--ssh(复习) 4. SpringData-JPA持久层入门案例(重点) 5. Easyui menu ...
- ASP.NET MVC 5项目
图文详解远程部署ASP.NET MVC 5项目 话外篇: 由于感觉自己的机器比较慢,配置不好,所以最近想把之前的项目部署到实验室的服务器上,但是由于常不在实验室,所以在想能不能远程部署.因此今天专 ...
- asp.net mvc 4 项目升级到 asp.net mvc5
一.开始 1.打开或新建asp.net mvc 4项目 2.修改 global.asax文件 原: WebApiConfig.Register(GlobalConfiguration.Configur ...
随机推荐
- Feature Preprocessing on Kaggle
刚入手data science, 想着自己玩一玩kaggle,玩了新手Titanic和House Price的 项目, 觉得基本的baseline还是可以写出来,但是具体到一些细节,以至于到能拿到的出 ...
- solr+jieba结巴分词
为什么选择结巴分词 分词效率高 词料库构建时使用的是jieba (python) 结巴分词Java版本 下载 git clone https://github.com/huaban/jieba-ana ...
- 毕业样本=[威尔士大学毕业证书]UWIC原件一模一样证书
威尔士大学毕业证[微/Q:2544033233◆WeChat:CC6669834]UC毕业证书/联系人Alice[查看点击百度快照查看][留信网学历认证&博士&硕士&海归&am ...
- Python+Appium 获取 toast 文本值方法的封装
获取toast内容方法封装如下: def get_Toast(self,message): #查找toast值 ''' method explain:查找toast的值,与find_Toast实现方法 ...
- 自学java难吗?一个JAVA学习者应该具备的素质
无论是在校的学生也好,还是转行的也好,如今学JAVA开发的人越来越多,造成了如今新手越来越多,有人说前端饱和了,JAVA饱和了,JAVA才刚开始以一种好的势头发展就饱和了.我也是无语,一般说饱和的人, ...
- 企业移动应用和Smobiler
www.smobiler.com 什么是企业移动应用? 能够通过一种方式来为客户.合作伙伴和员工交付信息和服务,从而帮助其增加收入,提高业务敏捷性和生产力的移动端产品,我们称之为企业移 ...
- 包装类及 LeetCode 每日一题
1.包装类与创建对象 Java 为8大数据类型都提供了相应的包装类,并提供属性和方法,更方便的操作基本数据类型.包装类位于java.lang包中. 对于这几种类型的基本数据,都有相似的方法实现基本数据 ...
- Java之品优购课程讲义_day06(7)
商品录入[SKU 商品信息]5.1 需求分析 基于上一步我们完成的规格选择,根据选择的规格录入商品的 SKU 信息,当用户选择相应的规格,下面的 SKU 列表就会自动生成,如下图:实现思路:实现思路: ...
- Spring Boot 入门(六):集成 treetable 和 zTree 实现树形图
本篇文章是接着Spring Boot 入门(五):集成 AOP 进行日志管理写的,主要集成了树形图,在部门列表或者权限列表中,树形图经常被用上.主要是根据相应的 API 凭借 html 字符串 1.t ...
- Oracle数据库升级注意事项
1 备份配置参数 数据库升级前的配置参数要备份,如PGA大小 这样数据库升级后还可以升级前的配置,而不至于使用安装升级时的默认配置 2 检查版本兼容 确认数据库升级后是否对生产环境上的代码有影响,如果 ...