什么叫上下文?

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

  • 当前用户是谁?
  • 刚才提供操作的数据库连接实例从哪里拿到?
  • 这个方法从哪个 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. 使用Kinect2作为Oculus游戏应用的输入设备

    注: 文章写于2015年8月, 眼下VR游戏Demo已经完结, 所以把上一次预研的一些经验分享出来, 希望对大家有所帮助 背景 初接触Oculus时, 从网上下载了一大堆的Demo来体验, 可是, 操 ...

  2. Linux非阻塞IO(三)非阻塞IO中缓冲区Buffer的实现

    本文我们来实现回射服务器的Buffer.   Buffer的实现   上节提到了非阻塞IO必须具备Buffer.再次将Buffer的设计描述一下: 这里必须补充一点,writeIndex指向空闲空间的 ...

  3. POJ 2375 Cow Ski Area (强连通分量)

    题目地址:POJ 2375 对每一个点向与之相邻并h小于该点的点加有向边. 然后强连通缩点.问题就转化成了最少加几条边使得图为强连通图,取入度为0和出度为0的点数的较大者就可以.注意,当强连通分量仅仅 ...

  4. BufferedReader 使用 readLine() 读取 UTF-8 格式的文本第一行第一个字符是空字符的解决办法

    BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(ksmgVo.getFiledata( ...

  5. 浅析Java语言中两种异常的差别

    Java提供了两类主要的异常:runtime exception和checked exception.所有的checked exception是从java.lang.Exception类衍生出来的,而 ...

  6. centos源码安装lnmp

    参考博客:http://blog.csdn.net/yanzi1225627/article/details/49123659 服务器环境为:CentOS6.6 64位(虚拟机) 一.安装前准备 创建 ...

  7. Python Numpy 数组的初始化和基本操作

    一.基础: Numpy的主要数据类型是ndarray,即多维数组.它有以下几个属性: ndarray.ndim:数组的维数 ndarray.shape:数组每一维的大小 ndarray.size:数组 ...

  8. C----------输入一组整数,求出这组数字子序列和中的最大值,只要求出最大子序列的和,不必求出最大值对应的序列。

    © 版权声明:本文为博主原创文章,转载请注明出处 代码: #include <stdio.h> #include <stdlib.h> #define GET_ARRAY_LE ...

  9. hbase的数据模型

    hbase类似bigTable是一个分布式的数据库,它是一个稀疏的,长期存储的,多维的,排序的映射表,这张表的索引是行关键字,列关键字,时间戳.hbase中的数据都是字符串,没有类型.         ...

  10. Codeforces 14D Two Paths 树的直径

    题目链接:点击打开链接 题意:给定一棵树 找2条点不反复的路径,使得两路径的长度乘积最大 思路: 1.为了保证点不反复,在图中删去一条边,枚举这条删边 2.这样得到了2个树,在各自的树中找最长链.即树 ...