ASP.NET是使用HTML、CSS、JS和服务端脚本创建Web页面和网站的开发框架。
ASP.NET支持三种开发模式
- Web Pages:Web页面
- MVC:Model View Controller 模型-视图-控制器
- Web Forms:Web窗体
基于.NET平台开发站点的框架实际上包含两部分:可视化用户界面WebForm和后台Web组件ASP.NET。两者通过命名空间区分,System.Web.UI.*
命名空间下的内容可称为WebForm,而System.Web.*
命名空间下的内容可称为ASP.NET。
与WebForm一样,ASP.NET MVC的类都在System.Web.Mvc
命名空间下,也是基于ASP.NET平台构建的。
MVC 编程模型
MVC 是使用模型-视图-控制器设计创建Web程序的模式
- Model 模型,表示应用程序核心,如数据库记录列表。
模型是应用程序中用于处理应用程序数据逻辑的部分,模型对象负责在数据库中存取数据。
模型是定义应用程序主题的现实对象、过程以及规则的表示,称为域(Domain)。
模型通常被称为域模型(Domain Model),包含应用程序域中要建立的C#对象,称为域对象(Domain Object)。这些域对象构成了应用程序的全部事物以及操作这些对象的方法。视图和控制器以一致的方式将域暴露给客户端。一个设计良好的MVC应用程序,必须从设计良好的模型开始,随后添加控制器和视图的焦点。 - View 视图,显示数据
视图是应用程序中处理数据显示的部分,视图是依赖模型数据创建的。 - Controller 控制器,处理输入
控制器是应用程序中处理用户交互的部分,控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
MVC分层有助于管理复杂的应用程序,同时也简化分组开发。
MVC框架被构建成一系列独立的组件,这些组件满足一个.NET接口,或建立在一个出抽象类之上。你可以很容易地用一个自己的不同实现来替换这些组件。例如:路由系统、视图引擎、控制器工厂等。通常,MVC框架对每个组件提供三种选择:
- 使用组件现行的默认实现
- 派生默认实现的一个子类以调整其行为
- 用接口或抽象基类的一个新的实现来完全替换该组件
ASP.NET MVC 开发中有一个很重要的理念叫“约定大于配置”,ASP.NET MVC中存在许多潜规则:
- 控制器存放
controller
目录,命名以Controller
结尾。 - 每个控制器都对应
View
中一个目录,视图目录名称跟控制器同名。控制器中的方法名都对应一个View
视图,且视图名字跟Action
动作的名字相同。 - 控制器必须是非静态类,且要实现
IController
接口。 - 控制器类型可以放到其他项目中
ASP.NET MVC的请求处理流程
客户端请求 => IIS => Runtime => Controller => Action => ViewResult(:ActionResult) => ExecuteResult() => RazorView(:IView).RenderView => Response
ASP.NET MVC 目录结构
- 应用程序信息
Properties
References
- 应用程序目录
App_Data
Content
存放静态文件,如CSS、ICON、IMG。
Controllers
负责处理用户输入和响应的控制器类,命名以Controller
结尾。
Models
包含表示应用程序模型的类,控制并操作应用程序的数据。
Views
用于存储与应用的显示相关的HTML界面文件。该目录包含每个控制器对应的一个目录。
Scripts
存储应用程序的JS文件 - 配置文件
Global.asax
packages.config
Web.config
ASP.NET MVC
路由
任何时候一个请求进来,URL都会与已注册的路由模板进行匹配。如果找到匹配项,就会确定处理该请求的合适控制器和操作方法。如果没有找到,请求会被拒绝,结果通常是一个404消息。
应用程序路由通常是在global.asax
文件中注册,并在应用程序启动时得到处理。
$ vim global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
}
URL路由引擎是一个把PostResolveRequestCache
事件串联起来的HTTP模块,在检查出请求的响应不在ASP.NET缓存中以后,就会出发该事件。
$ vim App_Start/RouteConfig.cs
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace Movie
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
}
框架所支持的路由必须添加到由ASP.NET MVC管理的路由对象的静态集合中,该集合就是RouteTable.Routes
。通常使用便捷的MapRoute()
方法来填充该集合。如果需要在路由上做一些MapRoute()
并不支持的设置,需要使用
var route = new Route(...);
RouteTable.Routes.Add("RouteName", route);
路由约束
routes.MapRoute(
"Product",
"{controller}/{id}/{locale}",
new {controller = "Product", action="Index", locale="en-us"},
// 路由约束
new {id = @"\d{8}", locale="[a-z]{2}-[a-z]{2}"}
)
从技术角度看,路由处理程序是一个实现IRouteHandler
接口的类
public interface IRouteHandler
{
IHttpHandler GetHttpHandler(RequestContext requestContext);
}
在System.Web.Routing
命名空间中定义的 RequestContext
类,封装了请求的HTTP上下文及任何可用的路由专用信息,比如Route对象本身、URL参数和约束条件。这些数据被分组到一个RouteData
对象。
public class RequestContext
{
public RequestContext(HttpContextBase httpContext, RouteData routeData);
// properties
public HttpContextBase HttpContext {get; set;}
public RouteData RouteData {get; set;}
}
阻止已定义的URL路由
- 为需要阻止的URL定义一个模式并保存到一个路由
- 将该路由链接到一个专门的路由处理程序
StopRoutingHandler
类
其结果就是在调用GetHttpHandler()
时会抛出一个NotSupported
异常。
$ vim /App_Start/RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
// 强制路由系统处理所有的请求
// routes.RouteExistingFiles = true;
// 指示路由系统忽略任何.axd请求
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces:new[] { "App.Controllers"}
);
}
IgnoreRoute
所作的就是将StopRoutingHandler
路由处理程序关联到围绕指定URL模式所构建的路由上。
控制器
控制器主要负责响应用户的输入,关注的是应用程序流、输入数据的处理以及对相关视图输出数据的提供。
控制器的特征
- 继承自
System.Web.Mvc.Controller
,实现IController
接口。 - 一个
Controller
可包含多个Action
,每个Action
都是一个方法,返回一个ActionResult
实例。
控制器本身是一个类,该类只要是public
公开的方法就会被是为是一个动作方法Action
,只要动作存在就可以接收客户端传来的请求,经过处理后返回响应的视图View
。
public string Text()
{
return "hello world";
}
在定义一个方法时会根据方法有无返回值来划分,但是控制器的本质是类,控制器的动作action
本质是方法。从数学集合的角度来看,控制器是类的子集,控制器的动作是方法的子集。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Movie.Controllers
{
public class TestController : Controller
{
// http://localhost:49184/test
public string Index()
{
return "hello";
}
// http://localhost:49184/test/welcome?name=superman&age=30
public string Welcome(string name, int age = 0)
{
// HttpServerUtility.HtmlEncode 来保护应用从malacious输入的(也就是JavaScript)
return HttpUtility.HtmlEncode("welcome "+name+" age is "+age);
}
}
}
控制器的基本条件
- 控制器必须为
public
公开类别 - 控制器名称以
Controller
结尾 - 控制器必须继承ASP.NET MVC内置
Controller
类或实现IController
接口 - 控制器内的所有动作必须为
public
公开方法,任何非public
方法均不会被视为动作方法。
控制器的运行原理
当控制器被MvcHandler
选中后,通过ActionInvoker
选定适当地Action
来运行。在Controller
中每个Action
可定义0到n个参数,ActionInvoker
会根据当前的RouteValue
与客户端传递来的数据,准备可传入Action
参数的数据,最后调用Controller
中被选中的Action
方法。
HTTP动词
ASP.NET MVC可让你将方法绑定到用于特定HTTP动词的操作,要将控制器方法与HTTP动词关联,可使用参数化的AcceptVerbs
特性,也可以直接使用特性。使用AcceptVerbs
特性,可以指定需要哪个HTTP动词来执行给定的方法。
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Update(Customer customer)
{
...
}
AcceptVerbs
特性会从HttpVerbs
枚举类型中提取,HttpVerbs
枚举由Flags
特性修饰。
public enum HttpVerbs
{
Get = 1,
Post = 2,
Put = 4,
Delete = 8,
Head = 0x10
}
可通过按位OR
(|)运算符将来自枚举类型的枚举值结合到一起,同时获得另一个HttpVerbs
值。
[AcceptVerbs(HttpVerbs.Post|HttpVerbs.Put)]
public ActionResult Edit(Customer customer)
{
...
}
控制器的方法类别
- 动作方法选定器
当通过ActionInvoker
选定Controller
控制器中的public
公开方法时,ASP.NET MVC的“动作方法选定器(Action Method Selector)”便会运行在动作方法上,以便ActionInvoker
选择对应的Action
。
- NonAction 属性
若控制器的动作方法上的特性为NonAction
,则表示该Action
是“公开方法”,即告知ActionInvoker
不要选择此Action
来运行。
NonAction
属性的作用:
保护Controller
中特定的公开方法不要发布到Web上
功能尚未开发完毕就要进行部署,但暂时不想将此方法删除。
可将public
修改为private
达到同样的保护效果
private ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
- HTTP动词限定属性
HTTP动词限定属性包括HttpGet
、HttpPost
、HttpDelete
、HttpPut
、HttpHead
、HttpOptions
、HttpPatch
,它们都是动作方法选定器的一部分。
// 当客户端浏览器发送的是HTTP GET请求时,ActionInvoker才会选定此Action。
[HttpGet]
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
[HttpPost]
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
- 操作过滤器
一个操作方法 一旦被选中就会立即执行,且返回一个结果,返回的结果也会随之执行。
ASP.NET MVC 提供5种操作过滤器:身份验证、授权、操作前后处理、结果前后处理、处理处理。此外还有一种是重写过滤器,它允许为全局或控制器的默认集合制定例外规则。
操作过滤器可直接使用操作方法或控制器的特性来编写,也可以作为在全局过滤器列表中注册的单独类来编写。如果打算将编写的操作过滤器作为特性来使用,就必须继承FilterAttribute
类或其子类,例如ActionFilterAtrribute
。当然不作为特性使用的全局操作过滤器对这个基类是没有要求的。注意的是,无论采用哪个路由,操作过滤器支持的过滤活动都由实现的接口所决定。
MvcHandler
从Controller
得到ActionResult
之后,开始运行ActionResult
提供的ExecuteResult
方法,并将运行结果响应到客户端,此时Controller
的任务完成。
控制器的动作过滤机制
- 授权过滤器
AuthorizationFilters
- 动作过滤器
ActionFilters
- 结果过滤器
ResultFilters
- 例外过滤器
ExceptionFilters
动作
Action本质是类中的公共方法可重载,要求参数不同。Action可通过路由规则传递数据。Action方法接收浏览器传递的参数。若希望方法只处理某种请求,可在方法前添加特性[HttpGet]
或[HttpPost]
,处理请求时会根据参数调用对应方法。
获取Request对象中的输入数据
ASP.NET中Request.Params
字典产生于4个不同字典的组合,即QueryString
、Form
、Cookies
、ServerVariables
。可使用Request
对象的Item
索引器属性。
POST数据接收方法包括:Request.Form
、Form.Collection
、同名参数、Model
。GET数据接收方式可直接通过Request.QueryString
获取。
public ActionResult Echo()
{
var data = Request["param"] ?? String.Empty;
}
public ActionResult Echo()
{
var data = Request.Params["param"] ?? String.Empty;
}
从路由中获取输入数据库
ASP.NET MVC会通过URL提供输入的参数,参数值由路由模块捕获以供应用程序使用。路由值不通过Request
对象向应用程序空开。
public ActionResult Echo()
{
var data = RouteData.Values["param"] ?? String.Empty;
}
路由数据是通过Controller
类的RouteData
属性公开的,对匹配项的搜索不区分大小写。RouteData.Values
字典是一个字符串/对象字典,大多数时候该字典只包含字符串。但是,若以编程方式填充该字典,那么它就可以包含其他类型的值。
public ActionResult Echo()
{
var data = RouteData.Values["data"] ?? (Request.Params["data"] ?? String.Empty);
}
ValueProvider字典
在Controller
类中ValueProvider
属性只会为从各种来源收集到的输入数据提供单个容器。默认情况下,ValueProvider
字典来自下列源的输入值填充:
- 子操作值
- 表单数据
Request.Form
- 路由数据
- 查询字符串
- 提交的文件
ValueProvider
字典提供了一个以GetValues()
方法为中心的自定义编程接口。
var result = ValueProvider.GetValue("param");
GetValue()
不会返回String
或Object
类型,而会返回ValueProviderResult
类型的一个实例。该类型有两个用于实际读取真实参数值的属性:RawValue
和AttemptedValue
,前者是Object
类型,包含来源所提供的原始值。AttemptedValue
属性却是一个字符串,代表了强制转换为String
类型的结果。
public ActionResult Echo()
{
var data = ValueProvider.GetValue("param").AttemptedValue ?? (ValueProvider.GetValue("param").AttemptedValue ?? String.Empty);
}
ValueProvider
比Request
和RouteData
在参数名称上的要求更高一些,如果参数大小写输入错误,会得到一个从GetValue
返回的null
对象。如果之后只是读取值而不检查为空的结果对象,就会导致一个异常。最后要注意的是,默认情况下,不能通过ValueProvider
字典获得对Cookies
的访问权。可通过定义一个实现IValueProvider
接口的类,值提供器列表可以以编程方式得到扩展。
参数传入的属性均通过模型绑定机制(Model Binding),从RequestContext
请求上下文中获取数据,并将数据传入对应的方法参数中,让Action
动作方法不再像ASP或ASP.NET Web Forms中使用的Request.Form
或Request.QueryString
等对象来获取客户端的数据。
产生操作结果
操作方法通常会返回一个ActionResult
类型对象,但它的类型并不是一个数据容器。确切地说,它是一个抽象类,提供通用编程界面,代表操作方法进一步的操作。
public abstract class ActionResult
{
protected ActionResult(){}
public abstract void ExecuteResult(ControllerContext context);
}
通过重写ExecuteResult()
方法,派生类会获得访问任何由操作方法的执行所产生的数据的权限,并触发一些后续操作。一般来说,这些后续操作与一些浏览器的响应生成有关。
预定义操作结果类型
Action
动作方法运行完毕后的回传值,通常是ActionResult
类别或其衍生类别Derived Class
。事实上,ActionResult
是一个抽象类,下列均是继承自ActionResult
。
-
ViewResult
用来回传一个View
-
RedirectResult
用来将网页重定向 -
Content
用来回传文件内容 -
FileResult
用来回传二进制文档等
执行操作结果的机制
public JavaScriptResult GetScript()
{
return JavaScript(js);
}
// JavaScript是Controller类的一个辅助器方法,充当JavaScriptResult对象工厂的角色
protected JavaScriptResult JavaScript(string js)
{
// JavaScriptResult类提供公共属性Script,它包含会写入输出流的脚本代码。
return new JavaScriptResult(){Script = script};
}
/**JavaScriptResult**/
public class JavaScriptResult:ActionResult
{
public String Script {get; set;}
public override void ExecuteResult(ControllerContext context)
{
if(context == null) throw new ArgumentNullException("context");
// Prepare the response
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = "application/x-javascript";
if(Script != null)
{
response.Write(Script);
}
}
}
控制器的动作结果
控制器动作结果ActionResult
是控制器动作返回的结果,即控制器返回给浏览器请求的响应内容。
ASP.NET MVC框架支持6种标准类型的动作结果
EmptyResult
表示无结果
ViewResult
表示返回HTML及标记
public ViewResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
JsonResult
表示返回JSON结果
public JsonResult TestJson()
{
var jsonResult = new JsonResult();
jsonResult.Data = new object[]
{
new {UserID = 10010,UserName = "IronMan"},
new {UserID = 10012,UserName = "AntMan"},
};
jsonResult.Data = new { UserID = 100, UserName = "SuperMane" };
jsonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
return jsonResult;
}
ContentResult
表示返回文本结果
public ContentResult Content()
{
return Content("Hello World");
}
RedirectResult
表示重定向到新的URL
public RedirectResult Goto(string url)
{
return Redirect(url);
}
RedirectToRouteResult
表示重定向到新的控制器动作上
public RedirectToRouteResult Reload()
{
return RedirectToAction("Index");
}
控制器的视图数据
View和Action之间前后台数据传递的方式
- 弱类型
ViewData[""]
- 动态型
dynamic
的ViewBag
- 动态类型
Model
- 临时存储
TempData[""]
- 后台
return View(data)
存入ViewData.Model
- 前台
Model
即WebViewPage.Model
使用强类型视图@model 类型
写在View
最顶部,@ViewData.Model
简写为@Model
、@Html.xxFor(x=>x.**)
。使用强类型的好处是提供智能提示,实现编译时错误检查,防止写错字段。
ViewBag.Message = "Your application description page.";
ViewData、TempData、ViewBag的区别
三者都是容器,能存储常量和变量,也能存储集合。
-
ViewData
和ViewBag
ViewBag
和ViewData
属性是同一份数据的不同表现形式,本质上都是ViewDataDictionary
,二者之间数据共享。
ViewData
为对象型,ViewBag
为Dynamic
动态对象,动态类型会在程序运行时动态解析,可为其指定任意属性,最终动态属性名作为数据字典的键名。动态类型dynamic
和对象型object
的区别是在dynamic
使用时会自动根据数据类型转换,object
则需要我们自己去强制转换。
ViewBag
只是在ViewData
上多了一层Dynamic
控制,可以说是访问ViewData
的另一种方式。理论上ViewBag
要比ViewData
慢一点儿。
-
ViewData
和TempData
ViewData
和TempData
属性均返回一个具有字典键值对结构的数据容器,TempData
存储临时数据,且设置的变量在被第一次读取后会被移除,也就是说TempData
设置的变量只能被读取一次。ViewData
和ViewBag
只对当前View
有用,TempData
则可以在不同的Action
中进行传值,类似Webform
中的Session
。TempData
的值在取了一次后会自动删除。TempData
用来在一次请求中同时执行的多个Action
方法之间共享数据。
视图
ASP.NET MVC 3引入Razor视图引擎(Razor view engine)。Razor视图模板使用.csshtml
文件扩展名,使用C#创建所要输出的HTML。
// 使用视图模板生成HTML返回给浏览器
// 控制器方法(ActionMethod 操作方法)一般返回一个ActionResult或从ActionResult所继承的类型
// 而非原始类型如字符串
public ActionResult Default()
{
return View();
}
# http://localhost:49184/Test/Welcome?message=hello&count=3
public ActionResult Welcome(string message, int count = 0)
{
ViewBag.Message = message;
ViewBag.Count= count;
return View();
}
# Welcome.cshtml
@{
ViewBag.Title = "Welcome";
Layout = "~/Views/Shared/_Layout.cshtml";
}
Welcome
@for (int i = 0; i < ViewBag.Count; i++)
{
- @ViewBag.Message
}
向浏览器输出HTML源码
# System.Web.IHtmlString
@Html.Raw("")
Razor布局的部分视图
ASP.NET MVC中的部分视图相当于WebForm中的User Control,由于页面中会有许多重用的地方,可进行封装重用。使用部分视图的好处是既可以简写代码,又可以使页面代码更加清晰更好维护。
- Razor中的
@Html.Partial()
和@{Html.RenderPartial();}
Partial可直接输出内容,在内部将HTML转换为MVCHtmlString
字符串,然后缓存起来,最后一次性输出到页面。显然,转换过程会降低效率,通常使用RenderPartial代替。
- Razor中的
@{Html.RenderPartial();}
与@{Html.RenderAction();}
RenderPartial不需要创建Controller的Action,而RenderAction需要在Controller中创建加载的Action。RenderAction会先去调用Controller的Action再呈现视图,所以这里会在发起一个链接。除了有HTML代码外,还需通过读取数据库来渲染,就必须使用RenderAction,因此它可以在Action里调用Model里的方法读取数据库,渲染视图后再呈现,而RenderPartial没有Action。
Razor中的
@{Html.RenderAction();}
和@Html.Action();
Action是直接输出和Partial一样存在一个转换的过程,但是不如RenderAction直接输出到当前HttpContext的效果高。Razor中的
@{Html.RenderPartial();}
和@RenderPage()
使用RenderPage来呈现部分,不能使用原来视图的Model和ViewData,只能通过参数来传递。而RenderPartial可使用原来视图的Model和ViewData。
模型
使用.NET Framework数据访问技术Entity Framework,定义和使用模型类。
Entity Framework, EF是支持代码优先(Code First)的开发模式,是相对于原始的CLR Object(POCO类)而言。
using System;
using System.Data.Entity;
namespace MovieManage.Models
{
//MovieDBContext类代表Entity Framework的电影数据库类
//负责在数据库中获取、存储、更新、处理Movie类的实例
//MovieDBContext继承自EF的DbContext基类
//为了能够引入DbContext和DbSet,需要添加using System.Data.Entity;
public class MovieDBContext : DbContext
{
public DbSet Movies { get; set; }
}
//Movie表示数据库中的电影,Movie对象的每个实例,将对应数据库表的一行,Movie类的每个属性将对应表的一列。
public class Movie
{
//属性
public int ID { get; set; }
public string Title { get; set; }
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public decimal Price { get; set; }
}
}
MovieDBContext
类负责处理连接到数据库,并将Movie
对象映射到数据表记录的任务中。那么如何指定它将连接到数据库呢?实际上,默认EF
将预设使用LocalDB
。
SQL Server Express LocalDB,简称LocalDB是SQLServer Express轻量级版本的数据库引擎。在用户模式下启动、执行。LocalDB运行在一个特殊的SQL Server Express的执行模式,所以运行使用MDF文件数据库。通常LocalDB的数据库文件都保存在web项目的App_Data目录下。
默认的EF命名为对象上下文类的一个连接字符串,连接字符串connectionString
的名称必须匹配DbContext
类的名称。
# web.config
CTRL+SHIFT+B
编译并生成代码后创建控制器
VS自动创建CRUD操作、视图被称为scaffolding
。
System.Data.SqlClient.SqlException:“在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误。未找到或无法访问服务器。请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接。 (provider: SQL Network Interfaces, error: 50 - 发生了 Local Database Runtime 错误。无法创建自动实例。有关错误详细信息,请参阅 Windows 应用程序事件日志。
)”
EF
EFEntity Framework
中会为每个管理的实体对象创建一个代理包装类对象,其中会跟踪实体对象的状态和每个属性的状态。
EF对象管理器
每个通过EF数据上下文操作的实体对象,都需要存在上下文的容器中,一旦通过上下文的某个方法操作了实体对象后,上下文就会给它加上一个状态标识。
调用上下文的SaveChange()
方法时,上下文context
会遍历容器中的所有对象,并检查它们的状态标识,并依照标识的值进行相应的SQL增删改查操作。
- 通常使用EF更新的方式,先查询出要修改的数据,然后再修改新值。实体对象被修改的属性在代理类对象里的对应属性状态会被修改记录下修改状态。等到调用
SaveChange()
方法时,EF会遍历其管理的每个实体对象,并根据其包装类对象的状态,生成增删改查的SQL语句并执行。
//实体上下文对象
EFEntities ctx= new EFEntities();
// 查询出要修改的数据
User user= ctx.Users.Find(id);
//设置修改后的值
user.UpdatedAt = DateTime.Now;
//更新到数据库
ctx.SaveChanges();
- 为了避免先查询数据库,可直接将被修改的实体对象添加到
EF
中管理,此时为附加状态Attached
,手动设置为未修改状态Unchanged
。若修改为Modified
那么将执行更新全部列,不过没有赋新值的列,执行生成的UPDATE SQL
语句时SET
的值仍旧是原来的值。同时,设置被修改的实体对象的包装类对象对应属性为修改状态。
User user = db.Users.Find(id);
if(user!=null){
// 将实体对象user添加到EF对象容器中,并获取伪包装类对象
DbEntityEntry entry = db.Entry(user);
// 将伪包装类对象的状态设置为Unchanged
entry.State = System.Data.EntityState.Unchanged;
// 对要修改的列Visited进行赋值
user.Visited = user.Visited + 1;
//设置被改变的属性
entry.Property(a=>a.Visited).IsModified = true;
//提交到数据库完成修改
int i = db.SaveChanges();//返回执行修改的行数
if(i>0){
//...
}
}
// 执行修改
[HttpPost]
public ActionResult Modify(BlogArticle model)
{
try
{
//将实体对象model加入EF对象容器中,并获取伪包装类对象entry。
DbEntityEntry entry = db.Entry(model);
//将包装类对象entry的状态设置为unchanged
entry.State = System.Data.EntityState.Unchanged;
//设置被修改的属性
entry.Property(a=>a.Title).IsModified = true;
entry.Property(a=>a.Author).IsModified = true;
entry.Property(a=>a.Category).IsModified = true;
//提交到数据库以完成修改
db.SaveChanges();
//更新成功后重定向
return RedirectToAction("Index","Home");
}
catch(Exception e)
{
return Content("修改失败..."+e.Message);
}
}
//执行删除
public ActionResult Delete(int id)
{
try
{
// 创建待删除对象
BlogArticle ba = new BlogArticle();
ba.Id = id;
//将待删除对象添加到EF对象管理容器
db.BlogArticles.Attach(ba);
//将对象包装类的状态标识设置为删除状态
db.BlogArticles.Remove(ba);
//更新到数据库
db.SaveChanges();
//跳转重定向
return RedirectToAction("Index","Home");
}
catch(Exception e)
{
return RedirectToAction("友好提示页面");
}
}
创建数据库上下文
协调为给定的数据模型的实体框架功能的主类是数据库上下文类。通过从派生来创建此类System.Data.Entity.DbContext
类。该类中可指定数据模型中包含哪些实体。
ashx
ashx
用于处理程序HttpHandler
,是.NET众多Web组件的一种,.ashx
是其扩展名。一个httpHandler
接受并处理一个HTTP请求,类似于Java中的Servlet,Java中需继承HttpServlet类。在.NET中需要实现IHttpHandler
接口,此接口有一个IsReusable
成员,一个待实现的方法ProcessRequest(HttpContextctx)
。程序在ProcessRequest()
方法中接收到HTTP请求。成员IsReusable
指定此IHttpHanlder
实例是否可被用来处理多个请求。
.ashx
文件用于编写web handler
,ashx
必须包含IsReuseable
属性,代表是否可复用。如果要在.ashx
文件中使用SESSION
必须实现IRequiresSessionState
接口。
.ashx
程序适合产生供浏览器处理的、无需回发处理的数据格式,例如用于生成动态图片、动态文本等内容。