什么叫上下文?

在你设计一个方法的时候,无法直接从方法参数或实例成员(字段或属性)获得的所有信息都是上下文。例如:

  • 当前用户是谁?
  • 刚才提供操作的数据库连接实例从哪里拿到?
  • 这个方法从哪个 View 或者哪个 Controller 调用的?

当然,在方法体中获得上下文最终还是要靠方法参数或实例成员。

在MVC中有大量的上下文信息,例如:

  • ControllerContext
  • ViewContext
  • ModelBindingContext
  • ExceptionContext
  • ActionExcutingContext
  • ActionExcutedContext
  • AuthorizationContext
  • ResultExcutingContext
  • ResultExcutedContext

这些上下文通过单一的参数提供了丰富的运行时信息。

实体上下文放到哪里?

除了MVC的上下文外,还有一个重要的上下文就是 ADO.NET EF的实体上下文,通常派生自System.Data.Objects.ObjectContext,都是由IDE自动生成的。这个上下文承载了数据库连接,需要通过IDisposable来释放连接。多数情况下,我们这样使用:

using(MyEntities context = new MyEntities())
{
…… 在这里写入代码
}

如果在一次页面生命周期内只使用一次实体上下文这样处理是非常合适的,但是事实上不都是这样。更多的时候可能需要临时对实体进行一个小的访问,例如获得一个当前用户的显示名,通过这种方式访问就代价太大了。

我们知道,这个上下文可以存放到HttpContext里。在HttpContext的所有容器中,只有Items是最合适的,因为这个属性的存续期在后台页面对象释放后就结束了。当然,被释放时也不会执行IDisposable的Dispose方法。我们仍然需要在Global.asax中捕捉EndRequest事件。但是奇妙的是:在ASP.NET MVC Application中不能使用event方式来捕捉,只能手工写Application_EndRequest方法

什么是一次Model、二次Model和Form Model?

Model一共分为三种:

  • 直接数据库实体映射实例,如Product(产品)
  • 为View的呈现提供服务的包装对象,如ProductInfo(产品信息)
  • 为Post回传提供服务的包装对象,如ProductForm(产品属性值)

第一种类型Model的特点是非常浓缩,几乎没有冗余,通过复杂的关系进行组合,通常需要通过多个不同类型的实例进行组合来表达一个完整的有意义的场景。例如,一个产品信息可能包含产品名称、产品类别、该产品所有的规格型号以及每种规格型号的参数、单价等。虽然ADO.NET EF提供了获取组合属性的能力,但不能处理多层次,并且不能对加载过程进行控制。所以,需要专门定义一些Model对这一组Model进行包装。如果把原始的模型称做“一次Model”,则可以把这个包装对象称做“二次Model”。

页面上收集到的Form信息,通过三种方式传递到Controller(以登录为例):

  • 每个信息项一个参数:public ActionResult Login(string userName, string password){…}
  • 一个单一的名值对参数:public ActionResult Login(FormCollection formCollection){…}
  • 一个单一的包装对象:public ActionResult Login(LoginInfo info){…}

第一种方式不利于重构。当需要加入一个参数时,必须修改Action的签名。而且也无法令Controller把值传递到View。第二种方式不利于设计时纠错,因为FormCollection中的值不是强类型的。所以,我们通常都会采用第三种方式。虽然ADO.NET EF对象可以直接作为Form Model,并且有BindAttribute对属性与Form值进行定制化的绑定,但是不够灵活,如果一个Form组合跨多个一次Model类型,则根本无法处理。所以我们有必要专门定义一个Model给View使用。我们不妨称之为“Form Model”。

业务逻辑放到什么地方?

MVC是一种“古老”的设计模式,提供了非常自然的分层方式,这也是为什么利于单元测试的原因。除了MVC这些“主层”以外,BLL可以算是一个“亚层”。那么,我们把BLL放到什么地方最合适?

BLL需要完全可见Model层,同时也需要一些上下文信息。例如,我们至少从我们刚才描述的论题中发现,需要从HttpContext的Items中获得实体上下文。有些时候,我们还需要将用户的一些登录信息缓存到HttpContext中,如果用户的登录信息非常复杂的话,仅仅依靠HttpContext.User.Identity.Name每次去抓取未必很合算。我的习惯是把和这个用户相关的信息组合到一个大的Model对象中,并把这个对象的实例 存放到HttpContext.Cache中。如果有任何变化,释放这个Cache项即可。

所以,对于业务逻辑的位置你可以有两个选择:

  • 放到 Model 下,再建立一个“上下文提供器接口”,由 Model 借助上下文来独立处理。
  • 放到 Controller 下,直接使用 Controller 提供的上下文来进行处理。

第一种方式不依赖Controller,解耦彻底,非常灵活,更易于测试。但是需要付出一定的成本,调用栈会稍深一点,还需要劳神处理到Controller与BLLContext间的关系。第二种方式解耦不够彻底,但非常简捷,比较适合 Controller 与 Model 不必彻底解耦的小型项目。有意思的是:ASP.NET MVC Application模板所生成的AccountController采用的就是第二种方式。

ADO.NET EF仅影响ASP.NET MVC的Model层。在Model层中除了EDMX自动生成的一次Model外,我们还需要建立大量的二次Model和Form Model。当然,从提升内聚度考虑,所有的业务逻辑方法都在这些Model中定义,特别是,可以利用partial类和扩展方法这两种手段加入业务逻辑。

ASP.NET MVC + ADO.NET EF 项目实战(一):应用程序布局设计的更多相关文章

  1. 快读《ASP.NET Core技术内幕与项目实战》WebApi3.1:WebApi最佳实践

    本节内容,涉及到6.1-6.6(P155-182),以WebApi说明为主.主要NuGet包:无 一.创建WebApi的最佳实践,综合了RPC和Restful两种风格的特点 1 //定义Person类 ...

  2. ASP.NET MVC 5 with EF 6 上传文件

        参考   ASP.NET MVC 5 with EF 6 - Working With Files Rename, Resize, Upload Image (ASP.NET MVC) ASP ...

  3. Asp.net mvc web api 在项目中的实际应用

    Asp.net mvc web api 在项目中的实际应用 前言:以下只是记录本人在项目中的应用,而web api在数据传输方面有多种实现方式,具体可根据实际情况而定! 1:数据传输前的加密,以下用到 ...

  4. 简读《ASP.NET Core技术内幕与项目实战》之3:配置

    特别说明:1.本系列内容主要基于杨中科老师的书籍<ASP.NET Core技术内幕与项目实战>及配套的B站视频视频教程,同时会增加极少部分的小知识点2.本系列教程主要目的是提炼知识点,追求 ...

  5. 快读《ASP.NET Core技术内幕与项目实战》EFCore2.5:集合查询原理揭秘(IQueryable和IEnumerable)

    本节内容,涉及4.6(P116-P130).主要NuGet包:如前述章节 一.LINQ和EFCore的集合查询扩展方法的区别 1.LINQ和EFCore中的集合查询扩展方法,虽然命名和使用完全一样,都 ...

  6. 《ASP.NET Core技术内幕与项目实战》精简集-目录

    本系列是杨中科2022年最新作品<ASP.NET Core技术内幕与项目实战>及B站配套视频(强插点赞)的精简集,是一个读书笔记.总结和提炼了主要知识点,遵守代码优先原则,以利于快速复习和 ...

  7. ASP.NET MVC开发:Web项目开发必备知识点

    最近加班加点完成一个Web项目,使用Asp.net MVC开发.很久以前接触的Asp.net开发还是Aspx形式,什么Razor引擎,什么MVC还是这次开发才明白,可以算是新手. 对新手而言,那进行A ...

  8. 解析ASP.NET Mvc开发之EF延迟加载

    目录: 1)从明源动力到创新工场这一路走来 2)解析ASP.NET WebForm和Mvc开发的区别 3)解析ASP.NET Mvc开发之查询数据实例 ------------------------ ...

  9. ASP.NET MVC 入门2、项目的目录结构与核心的DLL

    我们新建一个ASP.NET MVC的Web Application后,默认的情况下,项目的目录结构如下: App_Data :这个目录跟我们一般的ASP.NET website是一样的,用于存放数据. ...

随机推荐

  1. testng执行报错:org.testng.TestNGException: Cannot find class in classpath

    org.testng.TestNGException: Cannot find class in classpath 解决办法:project->clean 再次执行正常运行  

  2. 百度地图 Android SDK - 标注(Marker)的基本使用

    标注(Marker)是开发人员最常使用的地图覆盖物志一.今天就来向大家介绍一些标注(Marker)的最基本用法! 实现目标: 1.构建基础地图页面: 2.在地图的中心点处加入 Marker: 3.实现 ...

  3. UI自动化测试篇 :ReportNG替代TestNG自带html版测试报告初探

    转载http://www.cnblogs.com/chengtch/p/6071322.html “1.1.4版本的ReportNG是最后一个版本,之后不再做维护.作为一个简单的测试报告插件,它是创造 ...

  4. Android Socket通信编程

    安卓客户端通过socket与服务器端通讯一般可以按照以下几个步骤:(1).通过IP地址和端口实例化Socket,请求连接服务器:socket = new Socket(HOST, PORT); //h ...

  5. Beautiful Soup 4.4.0 基本使用方法

    Beautiful Soup 4.4.0 基本使用方法Beautiful Soup 安装 pip install  beautifulsoup4 标准库有html.parser解析器但速度不是很快一般 ...

  6. Unity里面的自动寻路(一)

    来自:http://www.narkii.com/club/forum.php?mod=viewthread&tid=269146&highlight=Unity%E9%87%8C%E ...

  7. SurfaceView实现拍照预览

    一.布局代码 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andro ...

  8. linux ln 命令使用参数详解(ln -s 软链接)(转)

    这是linux中一个非常重要命令,请大家一定要熟悉.它的功能是为某一个文件在另外一个位置建立一个同不的链接,这个命令最常用的参数是-s,具体用法是:ln -s 源文件 目标文件. 当 我们需要在不同的 ...

  9. Nginx 限制单个IP的并发连接数及对每个连接速度(限速)

    使用Nginx限制单个IP的并发连接数能够减少一些采集程序或者DDOS的攻击. 再lnmp的nginx配置中已经添加了部分代码,但是是注释掉的,可以编辑/usr/local/nginx/conf/ng ...

  10. JDBC技术总结(三)

    1. 数据库连接池 JDBC部分的前两个总结主要总结了一下JDBC的基本操作,而且有个共同点,就是应用程序都是直接获取数据库连接的.这会有个弊端:用户每次请求都需要向数据库获得连接,而数据库创建连接通 ...