ASP.NET MVC

ASP.NET是使用HTML蚁飒、CSS琼懊、JS和服務(wù)端腳本創(chuàng)建Web頁面和網(wǎng)站的開發(fā)框架哼丈。

ASP.NET支持三種開發(fā)模式

  • Web Pages:Web頁面
  • MVC:Model View Controller 模型-視圖-控制器
  • Web Forms:Web窗體

基于.NET平臺開發(fā)站點的框架實際上包含兩部分:可視化用戶界面WebForm和后臺Web組件ASP.NET削祈。兩者通過命名空間區(qū)分咙崎,System.Web.UI.*命名空間下的內(nèi)容可稱為WebForm褪猛,而System.Web.*命名空間下的內(nèi)容可稱為ASP.NET伊滋。

與WebForm一樣笑旺,ASP.NET MVC的類都在System.Web.Mvc命名空間下,也是基于ASP.NET平臺構(gòu)建的乌妙。

ASP.NET Web Form的技術(shù)堆棧

MVC 編程模型

MVC

MVC 是使用模型-視圖-控制器設(shè)計創(chuàng)建Web程序的模式

  • Model 模型,表示應(yīng)用程序核心泽艘,如數(shù)據(jù)庫記錄列表悉盆。
    模型是應(yīng)用程序中用于處理應(yīng)用程序數(shù)據(jù)邏輯的部分秋秤,模型對象負(fù)責(zé)在數(shù)據(jù)庫中存取數(shù)據(jù)灼卢。
    模型是定義應(yīng)用程序主題的現(xiàn)實對象鞋真、過程以及規(guī)則的表示海诲,稱為域(Domain)特幔。
    模型通常被稱為域模型(Domain Model)薄风,包含應(yīng)用程序域中要建立的C#對象遭赂,稱為域?qū)ο螅―omain Object)嵌牺。這些域?qū)ο髽?gòu)成了應(yīng)用程序的全部事物以及操作這些對象的方法。視圖和控制器以一致的方式將域暴露給客戶端僻弹。一個設(shè)計良好的MVC應(yīng)用程序蹋绽,必須從設(shè)計良好的模型開始,隨后添加控制器和視圖的焦點蚣抗。
  • View 視圖翰铡,顯示數(shù)據(jù)
    視圖是應(yīng)用程序中處理數(shù)據(jù)顯示的部分,視圖是依賴模型數(shù)據(jù)創(chuàng)建的迷捧。
  • Controller 控制器漠秋,處理輸入
    控制器是應(yīng)用程序中處理用戶交互的部分手趣,控制器負(fù)責(zé)從視圖讀取數(shù)據(jù)绿渣,控制用戶輸入,并向模型發(fā)送數(shù)據(jù)淀散。
MVC生命周期

MVC分層有助于管理復(fù)雜的應(yīng)用程序,同時也簡化分組開發(fā)郭膛。


MVC

MVC框架被構(gòu)建成一系列獨立的組件,這些組件滿足一個.NET接口棍现,或建立在一個出抽象類之上。你可以很容易地用一個自己的不同實現(xiàn)來替換這些組件朴肺。例如:路由系統(tǒng)西土、視圖引擎、控制器工廠等般甲。通常,MVC框架對每個組件提供三種選擇:

  • 使用組件現(xiàn)行的默認(rèn)實現(xiàn)
  • 派生默認(rèn)實現(xiàn)的一個子類以調(diào)整其行為
  • 用接口或抽象基類的一個新的實現(xiàn)來完全替換該組件

ASP.NET MVC 開發(fā)中有一個很重要的理念叫“約定大于配置”,ASP.NET MVC中存在許多潛規(guī)則:

  • 控制器存放controller目錄涮俄,命名以 Controller結(jié)尾彻亲。
  • 每個控制器都對應(yīng)View中一個目錄,視圖目錄名稱跟控制器同名野来。控制器中的方法名都對應(yīng)一個View視圖舀患,且視圖名字跟Action動作的名字相同。
  • 控制器必須是非靜態(tài)類低匙,且要實現(xiàn) IController接口。
  • 控制器類型可以放到其他項目中

ASP.NET MVC的請求處理流程


ASP.NET MVC

客戶端請求 => IIS => Runtime => Controller => Action => ViewResult(:ActionResult) => ExecuteResult() => RazorView(:IView).RenderView => Response

ASP.NET MVC 目錄結(jié)構(gòu)

  • 應(yīng)用程序信息
    Properties
    References
  • 應(yīng)用程序目錄
    App_Data
    Content 存放靜態(tài)文件绞呈,如CSS佃声、ICON、IMG召嘶。
    Controllers 負(fù)責(zé)處理用戶輸入和響應(yīng)的控制器類,命名以Controller結(jié)尾铛只。
    Models 包含表示應(yīng)用程序模型的類,控制并操作應(yīng)用程序的數(shù)據(jù)蜕着。
    Views 用于存儲與應(yīng)用的顯示相關(guān)的HTML界面文件。該目錄包含每個控制器對應(yīng)的一個目錄韧骗。
    Scripts 存儲應(yīng)用程序的JS文件
  • 配置文件
    Global.asax
    packages.config
    Web.config

ASP.NET MVC

路由

任何時候一個請求進來,URL都會與已注冊的路由模板進行匹配政模。如果找到匹配項,就會確定處理該請求的合適控制器和操作方法习蓬。如果沒有找到,請求會被拒絕枫慷,結(jié)果通常是一個404消息。

路由

應(yīng)用程序路由通常是在global.asax文件中注冊,并在應(yīng)用程序啟動時得到處理足丢。

$ vim global.asax
protected void Application_Start()
{
  AreaRegistration.RegisterAllAreas();
}

URL路由引擎是一個把PostResolveRequestCache事件串聯(lián)起來的HTTP模塊,在檢查出請求的響應(yīng)不在ASP.NET緩存中以后,就會出發(fā)該事件袖订。

$ 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管理的路由對象的靜態(tài)集合中,該集合就是RouteTable.Routes吏口。通常使用便捷的MapRoute()方法來填充該集合。如果需要在路由上做一些MapRoute()并不支持的設(shè)置,需要使用

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}"}
)
路由
VS開啟反編譯

從技術(shù)角度看塘娶,路由處理程序是一個實現(xiàn)IRouteHandler接口的類

public interface IRouteHandler
{
  IHttpHandler GetHttpHandler(RequestContext requestContext);
}

System.Web.Routing命名空間中定義的 RequestContext
類痊夭,封裝了請求的HTTP上下文及任何可用的路由專用信息刁岸,比如Route對象本身、URL參數(shù)和約束條件她我。這些數(shù)據(jù)被分組到一個RouteData對象虹曙。

RequestContext
public class RequestContext
{
  public RequestContext(HttpContextBase httpContext, RouteData routeData);
  // properties
  public HttpContextBase HttpContext {get; set;}
  public RouteData RouteData {get; set;}
}

阻止已定義的URL路由

  1. 為需要阻止的URL定義一個模式并保存到一個路由
  2. 將該路由鏈接到一個專門的路由處理程序StopRoutingHandler

其結(jié)果就是在調(diào)用GetHttpHandler()時會拋出一個NotSupported異常番舆。

$ vim /App_Start/RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
    // 強制路由系統(tǒng)處理所有的請求
    // routes.RouteExistingFiles = true;

    // 指示路由系統(tǒng)忽略任何.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路由處理程序關(guān)聯(lián)到圍繞指定URL模式所構(gòu)建的路由上酝碳。

控制器

控制器

控制器主要負(fù)責(zé)響應(yīng)用戶的輸入,關(guān)注的是應(yīng)用程序流恨狈、輸入數(shù)據(jù)的處理以及對相關(guān)視圖輸出數(shù)據(jù)的提供疏哗。

控制器的特征

  • 繼承自System.Web.Mvc.Controller,實現(xiàn)IController接口禾怠。
  • 一個Controller可包含多個Action返奉,每個Action都是一個方法,返回一個ActionResult實例刃宵。

控制器本身是一個類衡瓶,該類只要是public公開的方法就會被是為是一個動作方法Action,只要動作存在就可以接收客戶端傳來的請求牲证,經(jīng)過處理后返回響應(yīng)的視圖View哮针。

public string Text()
{
    return "hello world";
}

在定義一個方法時會根據(jù)方法有無返回值來劃分,但是控制器的本質(zhì)是類坦袍,控制器的動作action本質(zhì)是方法十厢。從數(shù)學(xué)集合的角度來看,控制器是類的子集捂齐,控制器的動作是方法的子集蛮放。

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 來保護應(yīng)用從malacious輸入的(也就是JavaScript)
            return HttpUtility.HtmlEncode("welcome "+name+" age is "+age);
        }
    }
}

控制器的基本條件

  1. 控制器必須為public公開類別
  2. 控制器名稱以Controller結(jié)尾
  3. 控制器必須繼承ASP.NET MVC內(nèi)置Controller類或?qū)崿F(xiàn)IController接口
  4. 控制器內(nèi)的所有動作必須為 public 公開方法,任何非public方法均不會被視為動作方法奠宜。

控制器的運行原理

當(dāng)控制器被MvcHandler選中后包颁,通過ActionInvoker選定適當(dāng)?shù)?code>Action來運行。在Controller中每個Action可定義0到n個參數(shù)压真,ActionInvoker會根據(jù)當(dāng)前的RouteValue與客戶端傳遞來的數(shù)據(jù)娩嚼,準(zhǔn)備可傳入Action參數(shù)的數(shù)據(jù),最后調(diào)用Controller中被選中的Action方法滴肿。

HTTP動詞

ASP.NET MVC可讓你將方法綁定到用于特定HTTP動詞的操作岳悟,要將控制器方法與HTTP動詞關(guān)聯(lián),可使用參數(shù)化的AcceptVerbs特性,也可以直接使用特性贵少。使用AcceptVerbs特性呵俏,可以指定需要哪個HTTP動詞來執(zhí)行給定的方法。

[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(|)運算符將來自枚舉類型的枚舉值結(jié)合到一起,同時獲得另一個HttpVerbs值宽气。

[AcceptVerbs(HttpVerbs.Post|HttpVerbs.Put)]
public ActionResult Edit(Customer customer)
{
  ...
}

控制器的方法類別

  • 動作方法選定器

當(dāng)通過ActionInvoker選定Controller控制器中的public公開方法時随常,ASP.NET MVC的“動作方法選定器(Action Method Selector)”便會運行在動作方法上潜沦,以便ActionInvoker選擇對應(yīng)的Action萄涯。

  1. NonAction 屬性

若控制器的動作方法上的特性為NonAction,則表示該Action是“公開方法”唆鸡,即告知ActionInvoker不要選擇此Action來運行涝影。

NonAction屬性

NonAction屬性的作用:

保護Controller中特定的公開方法不要發(fā)布到Web上

功能尚未開發(fā)完畢就要進行部署,但暫時不想將此方法刪除争占。

可將public修改為private達到同樣的保護效果

private ActionResult Contact()
{
    ViewBag.Message = "Your contact page.";

    return View();
}
  1. HTTP動詞限定屬性

HTTP動詞限定屬性包括HttpGet燃逻、HttpPostHttpDelete臂痕、HttpPut伯襟、HttpHeadHttpOptions握童、HttpPatch姆怪,它們都是動作方法選定器的一部分。

// 當(dāng)客戶端瀏覽器發(fā)送的是HTTP GET請求時各聘,ActionInvoker才會選定此Action鳍侣。
[HttpGet]
public ActionResult Contact()
{
    ViewBag.Message = "Your contact page.";

    return View();
}
HttpGet
[HttpPost]
public ActionResult Contact()
{
    ViewBag.Message = "Your contact page.";

    return View();
}
HttpPost 404
  • 操作過濾器

一個操作方法 一旦被選中就會立即執(zhí)行党巾,且返回一個結(jié)果,返回的結(jié)果也會隨之執(zhí)行溪掀。

ASP.NET MVC 提供5種操作過濾器:身份驗證、授權(quán)步鉴、操作前后處理揪胃、結(jié)果前后處理、處理處理氛琢。此外還有一種是重寫過濾器喊递,它允許為全局或控制器的默認(rèn)集合制定例外規(guī)則。

操作過濾器可直接使用操作方法或控制器的特性來編寫艺沼,也可以作為在全局過濾器列表中注冊的單獨類來編寫册舞。如果打算將編寫的操作過濾器作為特性來使用,就必須繼承FilterAttribute類或其子類障般,例如ActionFilterAtrribute调鲸。當(dāng)然不作為特性使用的全局操作過濾器對這個基類是沒有要求的盛杰。注意的是,無論采用哪個路由藐石,操作過濾器支持的過濾活動都由實現(xiàn)的接口所決定即供。

MvcHandlerController得到ActionResult之后,開始運行ActionResult提供的ExecuteResult方法于微,并將運行結(jié)果響應(yīng)到客戶端逗嫡,此時Controller的任務(wù)完成。

控制器的動作過濾機制

  • 授權(quán)過濾器 AuthorizationFilters
  • 動作過濾器 ActionFilters
  • 結(jié)果過濾器 ResultFilters
  • 例外過濾器 ExceptionFilters

動作

Action本質(zhì)是類中的公共方法可重載株依,要求參數(shù)不同驱证。Action可通過路由規(guī)則傳遞數(shù)據(jù)。Action方法接收瀏覽器傳遞的參數(shù)恋腕。若希望方法只處理某種請求抹锄,可在方法前添加特性[HttpGet][HttpPost],處理請求時會根據(jù)參數(shù)調(diào)用對應(yīng)方法荠藤。

獲取Request對象中的輸入數(shù)據(jù)

ASP.NET中Request.Params字典產(chǎn)生于4個不同字典的組合伙单,即QueryStringForm哈肖、Cookies吻育、ServerVariables∮倬可使用Request對象的Item索引器屬性布疼。

POST數(shù)據(jù)接收方法包括:Request.FormForm.Collection庄吼、同名參數(shù)缎除、Model。GET數(shù)據(jù)接收方式可直接通過Request.QueryString獲取总寻。

public ActionResult Echo()
{
  var data = Request["param"] ?? String.Empty;
}
public ActionResult Echo()
{
  var data = Request.Params["param"] ?? String.Empty;
}

從路由中獲取輸入數(shù)據(jù)庫

ASP.NET MVC會通過URL提供輸入的參數(shù)器罐,參數(shù)值由路由模塊捕獲以供應(yīng)用程序使用。路由值不通過Request對象向應(yīng)用程序空開渐行。

public ActionResult Echo()
{
  var data = RouteData.Values["param"] ?? String.Empty;
}

路由數(shù)據(jù)是通過Controller類的RouteData屬性公開的轰坊,對匹配項的搜索不區(qū)分大小寫。RouteData.Values字典是一個字符串/對象字典祟印,大多數(shù)時候該字典只包含字符串肴沫。但是,若以編程方式填充該字典蕴忆,那么它就可以包含其他類型的值颤芬。

public ActionResult Echo()
{
  var data = RouteData.Values["data"] ?? (Request.Params["data"] ?? String.Empty);
}

ValueProvider字典

Controller類中ValueProvider屬性只會為從各種來源收集到的輸入數(shù)據(jù)提供單個容器。默認(rèn)情況下,ValueProvider字典來自下列源的輸入值填充:

  • 子操作值
  • 表單數(shù)據(jù) Request.Form
  • 路由數(shù)據(jù)
  • 查詢字符串
  • 提交的文件

ValueProvider字典提供了一個以GetValues()方法為中心的自定義編程接口站蝠。

var result = ValueProvider.GetValue("param");

GetValue()不會返回StringObject類型汰具,而會返回ValueProviderResult類型的一個實例。該類型有兩個用于實際讀取真實參數(shù)值的屬性:RawValueAttemptedValue菱魔,前者是Object類型留荔,包含來源所提供的原始值。AttemptedValue屬性卻是一個字符串澜倦,代表了強制轉(zhuǎn)換為String類型的結(jié)果聚蝶。

public ActionResult Echo()
{
  var data = ValueProvider.GetValue("param").AttemptedValue ?? (ValueProvider.GetValue("param").AttemptedValue ?? String.Empty);
}

ValueProviderRequestRouteData在參數(shù)名稱上的要求更高一些,如果參數(shù)大小寫輸入錯誤藻治,會得到一個從GetValue返回的null對象碘勉。如果之后只是讀取值而不檢查為空的結(jié)果對象,就會導(dǎo)致一個異常栋艳。最后要注意的是恰聘,默認(rèn)情況下句各,不能通過ValueProvider字典獲得對Cookies的訪問權(quán)吸占。可通過定義一個實現(xiàn)IValueProvider接口的類凿宾,值提供器列表可以以編程方式得到擴展矾屯。

參數(shù)傳入的屬性均通過模型綁定機制(Model Binding),從RequestContext請求上下文中獲取數(shù)據(jù)初厚,并將數(shù)據(jù)傳入對應(yīng)的方法參數(shù)中件蚕,讓Action動作方法不再像ASP或ASP.NET Web Forms中使用的Request.FormRequest.QueryString等對象來獲取客戶端的數(shù)據(jù)。

產(chǎn)生操作結(jié)果

操作方法通常會返回一個ActionResult類型對象产禾,但它的類型并不是一個數(shù)據(jù)容器排作。確切地說,它是一個抽象類亚情,提供通用編程界面妄痪,代表操作方法進一步的操作。

public abstract class ActionResult
{
  protected ActionResult(){}
  public abstract void ExecuteResult(ControllerContext context);
}

通過重寫ExecuteResult()方法楞件,派生類會獲得訪問任何由操作方法的執(zhí)行所產(chǎn)生的數(shù)據(jù)的權(quán)限衫生,并觸發(fā)一些后續(xù)操作。一般來說土浸,這些后續(xù)操作與一些瀏覽器的響應(yīng)生成有關(guān)罪针。

預(yù)定義操作結(jié)果類型

Action動作方法運行完畢后的回傳值,通常是ActionResult類別或其衍生類別Derived Class黄伊。事實上泪酱,ActionResult是一個抽象類,下列均是繼承自ActionResult

  • ViewResult用來回傳一個View
  • RedirectResult用來將網(wǎng)頁重定向
  • Content用來回傳文件內(nèi)容
  • FileResult用來回傳二進制文檔等

執(zhí)行操作結(jié)果的機制

public JavaScriptResult GetScript()
{
  return JavaScript(js);
}
// JavaScript是Controller類的一個輔助器方法墓阀,充當(dāng)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);
    }
  }
}

控制器的動作結(jié)果

控制器動作結(jié)果ActionResult是控制器動作返回的結(jié)果岂津,即控制器返回給瀏覽器請求的響應(yīng)內(nèi)容虱黄。

ASP.NET MVC框架支持6種標(biāo)準(zhǔn)類型的動作結(jié)果

EmptyResult表示無結(jié)果

ViewResult表示返回HTML及標(biāo)記

public ViewResult About()
{
    ViewBag.Message = "Your application description page.";

    return View();
}

JsonResult表示返回JSON結(jié)果

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表示返回文本結(jié)果

public ContentResult Content()
{
    return Content("Hello World");
}

RedirectResult表示重定向到新的URL

public RedirectResult Goto(string url)
{
    return Redirect(url);
}

RedirectToRouteResult表示重定向到新的控制器動作上

public RedirectToRouteResult Reload()
{
    return RedirectToAction("Index");
}

控制器的視圖數(shù)據(jù)

View和Action之間前后臺數(shù)據(jù)傳遞的方式

  • 弱類型 ViewData[""]
  • 動態(tài)型dynamicViewBag
  • 動態(tài)類型 Model
  • 臨時存儲 TempData[""]
  • 后臺 return View(data) 存入 ViewData.Model
  • 前臺 ModelWebViewPage.Model

使用強類型視圖@model 類型寫在View最頂部,@ViewData.Model簡寫為@Model吮成、@Html.xxFor(x=>x.**)橱乱。使用強類型的好處是提供智能提示,實現(xiàn)編譯時錯誤檢查粱甫,防止寫錯字段泳叠。

ViewBag.Message = "Your application description page.";

ViewData、TempData茶宵、ViewBag的區(qū)別

三者都是容器危纫,能存儲常量和變量,也能存儲集合乌庶。

  • ViewDataViewBag

ViewBagViewData屬性是同一份數(shù)據(jù)的不同表現(xiàn)形式种蝶,本質(zhì)上都是ViewDataDictionary,二者之間數(shù)據(jù)共享瞒大。

ViewData為對象型螃征,ViewBagDynamic動態(tài)對象,動態(tài)類型會在程序運行時動態(tài)解析透敌,可為其指定任意屬性盯滚,最終動態(tài)屬性名作為數(shù)據(jù)字典的鍵名。動態(tài)類型dynamic和對象型object的區(qū)別是在dynamic使用時會自動根據(jù)數(shù)據(jù)類型轉(zhuǎn)換酗电,object則需要我們自己去強制轉(zhuǎn)換魄藕。

ViewBag只是在ViewData上多了一層Dynamic控制,可以說是訪問ViewData的另一種方式撵术。理論上ViewBag要比ViewData慢一點兒背率。

  • ViewDataTempData

ViewDataTempData屬性均返回一個具有字典鍵值對結(jié)構(gòu)的數(shù)據(jù)容器,TempData存儲臨時數(shù)據(jù)荷荤,且設(shè)置的變量在被第一次讀取后會被移除退渗,也就是說TempData設(shè)置的變量只能被讀取一次。ViewDataViewBag只對當(dāng)前View有用蕴纳,TempData則可以在不同的Action中進行傳值会油,類似Webform中的SessionTempData的值在取了一次后會自動刪除古毛。TempData用來在一次請求中同時執(zhí)行的多個Action方法之間共享數(shù)據(jù)翻翩。

視圖

ASP.NET MVC 3引入Razor視圖引擎(Razor view engine)都许。Razor視圖模板使用.csshtml文件擴展名,使用C#創(chuàng)建所要輸出的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";
}

<h2>Welcome</h2>
<ul>
    @for (int i = 0; i < ViewBag.Count; i++)
    {
        <li>@ViewBag.Message</li>
    }
</ul>

向瀏覽器輸出HTML源碼

# System.Web.IHtmlString
@Html.Raw("<i class="fa fa-th"></i>")

Razor布局的部分視圖

ASP.NET MVC中的部分視圖相當(dāng)于WebForm中的User Control胶征,由于頁面中會有許多重用的地方,可進行封裝重用桨仿。使用部分視圖的好處是既可以簡寫代碼睛低,又可以使頁面代碼更加清晰更好維護。

  • Razor中的@Html.Partial()@{Html.RenderPartial();}

Partial可直接輸出內(nèi)容服傍,在內(nèi)部將HTML轉(zhuǎn)換為MVCHtmlString字符串钱雷,然后緩存起來,最后一次性輸出到頁面吹零。顯然罩抗,轉(zhuǎn)換過程會降低效率,通常使用RenderPartial代替灿椅。

  • Razor中的@{Html.RenderPartial();}@{Html.RenderAction();}

RenderPartial不需要創(chuàng)建Controller的Action套蒂,而RenderAction需要在Controller中創(chuàng)建加載的Action。RenderAction會先去調(diào)用Controller的Action再呈現(xiàn)視圖茫蛹,所以這里會在發(fā)起一個鏈接操刀。除了有HTML代碼外,還需通過讀取數(shù)據(jù)庫來渲染麻惶,就必須使用RenderAction馍刮,因此它可以在Action里調(diào)用Model里的方法讀取數(shù)據(jù)庫,渲染視圖后再呈現(xiàn)窃蹋,而RenderPartial沒有Action。

  • Razor中的@{Html.RenderAction();}@Html.Action();
    Action是直接輸出和Partial一樣存在一個轉(zhuǎn)換的過程静稻,但是不如RenderAction直接輸出到當(dāng)前HttpContext的效果高警没。

  • Razor中的@{Html.RenderPartial();}@RenderPage()

使用RenderPage來呈現(xiàn)部分,不能使用原來視圖的Model和ViewData振湾,只能通過參數(shù)來傳遞杀迹。而RenderPartial可使用原來視圖的Model和ViewData。

模型

使用.NET Framework數(shù)據(jù)訪問技術(shù)Entity Framework押搪,定義和使用模型類树酪。

Entity Framework, EF是支持代碼優(yōu)先(Code First)的開發(fā)模式,是相對于原始的CLR Object(POCO類)而言大州。

using System;
using System.Data.Entity;

namespace MovieManage.Models
{
    //MovieDBContext類代表Entity Framework的電影數(shù)據(jù)庫類
    //負(fù)責(zé)在數(shù)據(jù)庫中獲取续语、存儲、更新厦画、處理Movie類的實例
    //MovieDBContext繼承自EF的DbContext基類
    //為了能夠引入DbContext和DbSet疮茄,需要添加using System.Data.Entity;
    public class MovieDBContext : DbContext
    {
        public DbSet<Movie> Movies { get; set; }
    }
    //Movie表示數(shù)據(jù)庫中的電影滥朱,Movie對象的每個實例,將對應(yīng)數(shù)據(jù)庫表的一行力试,Movie類的每個屬性將對應(yīng)表的一列徙邻。
    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; }
    }
}
image.png

MovieDBContext類負(fù)責(zé)處理連接到數(shù)據(jù)庫,并將Movie對象映射到數(shù)據(jù)表記錄的任務(wù)中畸裳。那么如何指定它將連接到數(shù)據(jù)庫呢缰犁?實際上,默認(rèn)EF將預(yù)設(shè)使用LocalDB怖糊。

SQL Server Express LocalDB民鼓,簡稱LocalDB是SQLServer Express輕量級版本的數(shù)據(jù)庫引擎。在用戶模式下啟動蓬抄、執(zhí)行丰嘉。LocalDB運行在一個特殊的SQL Server Express的執(zhí)行模式,所以運行使用MDF文件數(shù)據(jù)庫嚷缭。通常LocalDB的數(shù)據(jù)庫文件都保存在web項目的App_Data目錄下饮亏。

默認(rèn)的EF命名為對象上下文類的一個連接字符串,連接字符串connectionString的名稱必須匹配DbContext類的名稱阅爽。

# web.config
  <connectionStrings>
    <add name="MovieDBContext" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\Movies.mdf;Integrated Security=True" providerName="System.Data.SqlClient"/>
  </connectionStrings>

CTRL+SHIFT+B 編譯并生成代碼后創(chuàng)建控制器

image.png

VS自動創(chuàng)建CRUD操作路幸、視圖被稱為scaffolding

System.Data.SqlClient.SqlException:“在與 SQL Server 建立連接時出現(xiàn)與網(wǎng)絡(luò)相關(guān)的或特定于實例的錯誤付翁。未找到或無法訪問服務(wù)器简肴。請驗證實例名稱是否正確并且 SQL Server 已配置為允許遠(yuǎn)程連接。 (provider: SQL Network Interfaces, error: 50 - 發(fā)生了 Local Database Runtime 錯誤百侧。無法創(chuàng)建自動實例砰识。有關(guān)錯誤詳細(xì)信息,請參閱 Windows 應(yīng)用程序事件日志佣渴。
)”

EF

EFEntity Framework中會為每個管理的實體對象創(chuàng)建一個代理包裝類對象辫狼,其中會跟蹤實體對象的狀態(tài)和每個屬性的狀態(tài)。

EF對象管理器

每個通過EF數(shù)據(jù)上下文操作的實體對象辛润,都需要存在上下文的容器中膨处,一旦通過上下文的某個方法操作了實體對象后,上下文就會給它加上一個狀態(tài)標(biāo)識砂竖。

調(diào)用上下文的SaveChange()方法時真椿,上下文context會遍歷容器中的所有對象,并檢查它們的狀態(tài)標(biāo)識乎澄,并依照標(biāo)識的值進行相應(yīng)的SQL增刪改查操作突硝。

  1. 通常使用EF更新的方式,先查詢出要修改的數(shù)據(jù)三圆,然后再修改新值狞换。實體對象被修改的屬性在代理類對象里的對應(yīng)屬性狀態(tài)會被修改記錄下修改狀態(tài)避咆。等到調(diào)用SaveChange()方法時,EF會遍歷其管理的每個實體對象修噪,并根據(jù)其包裝類對象的狀態(tài)查库,生成增刪改查的SQL語句并執(zhí)行。
//實體上下文對象
EFEntities ctx= new EFEntities();
// 查詢出要修改的數(shù)據(jù)
User user= ctx.Users.Find(id);
//設(shè)置修改后的值
user.UpdatedAt = DateTime.Now;
//更新到數(shù)據(jù)庫
ctx.SaveChanges();
  1. 為了避免先查詢數(shù)據(jù)庫黄琼,可直接將被修改的實體對象添加到EF中管理樊销,此時為附加狀態(tài)Attached,手動設(shè)置為未修改狀態(tài)Unchanged脏款。若修改為Modified那么將執(zhí)行更新全部列围苫,不過沒有賦新值的列,執(zhí)行生成的UPDATE SQL語句時SET的值仍舊是原來的值撤师。同時剂府,設(shè)置被修改的實體對象的包裝類對象對應(yīng)屬性為修改狀態(tài)。
User user = db.Users.Find(id);
if(user!=null){
  // 將實體對象user添加到EF對象容器中剃盾,并獲取偽包裝類對象
  DbEntityEntry<User> entry = db.Entry<User>(user);
  // 將偽包裝類對象的狀態(tài)設(shè)置為Unchanged
  entry.State = System.Data.EntityState.Unchanged;
  // 對要修改的列Visited進行賦值
  user.Visited = user.Visited + 1;
  //設(shè)置被改變的屬性
  entry.Property(a=>a.Visited).IsModified = true;
  //提交到數(shù)據(jù)庫完成修改
  int i = db.SaveChanges();//返回執(zhí)行修改的行數(shù)
  if(i>0){
    //...
  }
}
// 執(zhí)行修改
[HttpPost]

public ActionResult Modify(BlogArticle model)
{
  try
  {
    //將實體對象model加入EF對象容器中腺占,并獲取偽包裝類對象entry。
    DbEntityEntry<BlogArticle> entry =  db.Entry<BlogArticle>(model);
    //將包裝類對象entry的狀態(tài)設(shè)置為unchanged
    entry.State = System.Data.EntityState.Unchanged;
    //設(shè)置被修改的屬性
    entry.Property(a=>a.Title).IsModified = true;
    entry.Property(a=>a.Author).IsModified = true;
    entry.Property(a=>a.Category).IsModified = true;
    //提交到數(shù)據(jù)庫以完成修改
    db.SaveChanges();
    //更新成功后重定向
    return RedirectToAction("Index","Home");
  }
  catch(Exception e)
  {
    return Content("修改失敗..."+e.Message);
   }
}
//執(zhí)行刪除
public ActionResult Delete(int id)
{
  try
  {
    // 創(chuàng)建待刪除對象
    BlogArticle ba = new BlogArticle();
    ba.Id = id;
    //將待刪除對象添加到EF對象管理容器
    db.BlogArticles.Attach(ba);
    //將對象包裝類的狀態(tài)標(biāo)識設(shè)置為刪除狀態(tài)
    db.BlogArticles.Remove(ba);
    //更新到數(shù)據(jù)庫
    db.SaveChanges();
    //跳轉(zhuǎn)重定向
    return RedirectToAction("Index","Home");
  }
  catch(Exception e)
  {
    return RedirectToAction("友好提示頁面");
  }
}

創(chuàng)建數(shù)據(jù)庫上下文
協(xié)調(diào)為給定的數(shù)據(jù)模型的實體框架功能的主類是數(shù)據(jù)庫上下文類痒谴。通過從派生來創(chuàng)建此類System.Data.Entity.DbContext類衰伯。該類中可指定數(shù)據(jù)模型中包含哪些實體。

ashx

ashx用于處理程序HttpHandler积蔚,是.NET眾多Web組件的一種意鲸,.ashx是其擴展名。一個httpHandler接受并處理一個HTTP請求尽爆,類似于Java中的Servlet怎顾,Java中需繼承HttpServlet類。在.NET中需要實現(xiàn)IHttpHandler接口教翩,此接口有一個IsReusable成員杆勇,一個待實現(xiàn)的方法ProcessRequest(HttpContextctx)。程序在ProcessRequest()方法中接收到HTTP請求饱亿。成員IsReusable指定此IHttpHanlder實例是否可被用來處理多個請求。

.ashx文件用于編寫web handler闰靴,ashx必須包含IsReuseable屬性彪笼,代表是否可復(fù)用。如果要在.ashx文件中使用SESSION必須實現(xiàn)IRequiresSessionState接口蚂且。

.ashx程序適合產(chǎn)生供瀏覽器處理的配猫、無需回發(fā)處理的數(shù)據(jù)格式,例如用于生成動態(tài)圖片杏死、動態(tài)文本等內(nèi)容泵肄。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捆交,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子腐巢,更是在濱河造成了極大的恐慌品追,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冯丙,死亡現(xiàn)場離奇詭異肉瓦,居然都是意外死亡,警方通過查閱死者的電腦和手機胃惜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門泞莉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人船殉,你說我怎么就攤上這事鲫趁。” “怎么了利虫?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵挨厚,是天一觀的道長。 經(jīng)常有香客問我列吼,道長幽崩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任寞钥,我火速辦了婚禮慌申,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘理郑。我一直安慰自己蹄溉,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布您炉。 她就那樣靜靜地躺著柒爵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赚爵。 梳的紋絲不亂的頭發(fā)上棉胀,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機與錄音冀膝,去河邊找鬼唁奢。 笑死,一個胖子當(dāng)著我的面吹牛窝剖,可吹牛的內(nèi)容都是我干的麻掸。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼赐纱,長吁一口氣:“原來是場噩夢啊……” “哼脊奋!你這毒婦竟也來了熬北?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤诚隙,失蹤者是張志新(化名)和其女友劉穎讶隐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體最楷,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡整份,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了籽孙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烈评。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖犯建,靈堂內(nèi)的尸體忽然破棺而出讲冠,到底是詐尸還是另有隱情,我是刑警寧澤适瓦,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布竿开,位于F島的核電站,受9級特大地震影響玻熙,放射性物質(zhì)發(fā)生泄漏否彩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一嗦随、第九天 我趴在偏房一處隱蔽的房頂上張望列荔。 院中可真熱鬧,春花似錦枚尼、人聲如沸贴浙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽崎溃。三九已至,卻和暖如春盯质,著一層夾襖步出監(jiān)牢的瞬間袁串,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工呼巷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留般婆,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓朵逝,卻偏偏與公主長得像,于是被迫代替她去往敵國和親乡范。 傳聞我的和親對象是個殘疾皇子配名,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容