天天看點

MVC筆記

1.MVC概念

--Model:用于存儲資料的元件

--View:根據Model資料進行内容展示的元件

--Controller:接受并處理使用者指令(操作Model),選擇一個View并輸出内容。

Controller對View進行引用,但是View不知道Controller的存在。Controller和View都是單向引用Model

MVC變種:Observer模式,MVP模式。

2.mvc路由機制

MVC中重要的路由處理,預設情況是在Global.asax檔案中,我們也可以把這塊内容獨立出來。

MVC筆記
MVC筆記

代碼

1 public  class MyMvcAppliation:HttpApplication

2     {

3         public static void RegisterRoutes(RouteCollection routes)

4         {

5             routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

6

7             routes.MapRoute(

8                 "Default",                                              // Route name

9                 "{controller}/{action}/{id}",                           // URL with parameters

10                 new { controller = "Home", action = "Index", id = "" },  // Parameter defaults

11                 new string[] { "GuestBook.MVC.Controller" }

12             );

13           

14         }

15         protected void Application_Start()

16         {

17             ControllerBuilder.Current.DefaultNamespaces.Add("GuestBook.MVC.Controller");

18             RegisterRoutes(RouteTable.Routes);

19         }

3. 把Controller類和業務邏輯分離,這裡可以采用Repository模式

建立一個Repository接口:IRepository.cs,裡面包含些常見資料處理操作方法:這個接口是一個泛型接口,以實作所有實體類的通用性。public interface IRepository<T>

    {

        List<T> FindAllInfo();

        T GetInfo(T model);

        bool  Add(T model);

        bool  Delete(T model);

        bool  Edit(T model);

    }

4.MVC中的ViewData

View在MVC模式中與使用者進行最直接的接觸,負責資料的呈現。注意:view隻是負責資料的呈現,我們要盡量讓view中不涉及業務邏輯的處理。既然View與背景代碼是相分離的,但View和Controller是如何聯系在一起的呢,答案就是ViewData。

  ASP.NET MVC預設使用WebForm來作為view。建立的aspx頁面繼承自ViewPage,所有的aspx頁面都必須繼承自ViewPage。我們再看一下ViewPage的部分代碼:

public class ViewPage : Page, IViewDataContainer

   我們使用傳統的asp.net開發時,經常會為了開發的需要,會寫一個類似PageBase類,例如會把部分比較通用的方法寫入基類。同樣在MVC中,我們也可以這樣做。

   第一:建立一個ViewPage<T>類(擴充ViewPage):這個類主要是完成一個繼承功能,對MvcContrib.FluentHtml.ModelViewPage,MvcContrib.FluentHtml.ModelViewUserControl的繼承,實作System.Web.Mvc.ViewPage的功能。還有一個非常重要的作用就是把所有的擴充方法都展現在這個類中。

    public class ViewPage<T> : MvcContrib.FluentHtml.ModelViewPage<T> where T : class

        public ViewPage()

        {

        }

    public class ViewUserControl<T> : MvcContrib.FluentHtml.ModelViewUserControl<T> where T : class

        public ViewUserControl()

第二.對MVC進行擴充(對ViewData)。例如對Html的擴充,我們在做增删改查類似操作時,當使用者送出後一般都會根據系統處理結果顯示一段提示文字給使用者。

         1:建立擴充類:HtmlHelperExtensions,主要包含兩個方法,一個是操作成功後的處理方法,另一個則是失敗後的處理結果。

public static class HtmlHelperExtensions

      {

        public static string ErrorBox(this HtmlHelper htmlHelper, ViewDataBase  errorViewData)

            if (errorViewData.ErrorMessage == null) return string.Empty;

            HtmlTextWriter writer = new HtmlTextWriter(new StringWriter());

            writer.AddAttribute("class", "error");

            writer.RenderBeginTag(HtmlTextWriterTag.Div);

            writer.Write(errorViewData.ErrorMessage);

            writer.RenderEndTag();

            return writer.InnerWriter.ToString();

        public static string MessageBox(this HtmlHelper htmlHelper, ViewDataBase messageViewData)

            if (messageViewData.Message == null) return string.Empty;

            writer.AddAttribute("class", "message");

            writer.Write(messageViewData.Message);

    }  

1 public  class ViewDataBase

3         public string Message { get; set; }

4         public string ErrorMessage { get; set; }

5

6         public ViewDataBase WithErrorMessage(string errorMessage)

7         {

8             this.ErrorMessage = errorMessage;

9             return this;

10         }

11         public ViewDataBase WithMessage(string message)

12         {

13             this.Message = message;

14             return this;

15         }

16     }

5.MVC中的ModelBinder

MVC送出表單時我們并不用對表單中各個對象進行一一對應,MVC會自動把表單的相關值賦給對象。這點看起來特别神奇,MVC 為我們提供了一個自動化的操作,這一切都歸功于IModelBinder 接口,系統預設會找它DefaultModelBinder來完成這一神聖的任務。DefaultModelBinder 内部通過大量的反射完成最終的指派操作,基本上能适應開發所需。至于如何實作大家可以到網上去搜尋下資料,既然有預設的,我們也可以自定義ModelBinder。

1:建立GuestBookBinder,讓它繼承IModelBinder,并實作其方法。這裡我們可以根據實際業務需要,修改方法,這裡隻是一個簡單實作。

public class GuestBookBinder : IModelBinder

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)

            var info = bindingContext.Model ?? new GuestBookInfo();

            var properties = bindingContext.ModelType.GetProperties();

            foreach (var item in properties)

            {

                if (bindingContext.PropertyFilter(item.Name))

                {

                    var result = bindingContext.ValueProvider[item.Name];

                    if (null == result)

                    { break; }

                    var value = result.ConvertTo(item.PropertyType);

                    item.SetValue(info, value, null);

                }

            }

            return info;

       2:注冊GuestBookBinder,自定義Binder寫好後,系統并不會自動識别,需要在應用程式初始化進行注冊,ControllerActionInvoker.GetParameterValue 根據 ModelBinders.Binders.GetBinder() 來找對應的 IModelBinder,如沒找到則傳回預設的 DefaultModelBinder。   

protected void Application_Start()

            ControllerBuilder.Current.DefaultNamespaces.Add("GuestBook.MVC.Controller");

            ModelBinders.Binders.Add(typeof(GuestBookInfo ), new GuestBookBinder ());

            RegisterRoutes(RouteTable.Routes);

      3:除了上面的注冊方法外,可以直接把 ModelBinderAttribute 用在對應的實體上 ,但不推薦這樣做。

[ModelBinder(typeof(GuestBookBinder))]

public class GuestBookInfo

      4:除了由 ControllerActionInvoker.GetParameterValue() 自動完成 BindModel 操作外,還提供了兩個方法:這兩個方法??一的差別在于,TryUpdateModel不會抛異常,前者會。

        1>:Controller.UpdateModel()

        2>:Controller.TryUpdateModel()

      示例:例如更新一則留言時,我們可以這樣寫:

[AcceptVerbs(HttpVerbs.Post)]

       public ActionResult Edit(int id, FormCollection formValues)

       {

           GuestBookInfo model = new GuestBookInfo();

           model.ID = id;

           model = inter.GetInfo(model);

           UpdateModel(model );

           inter.Edit(model);

           return RedirectToAction("Index");

       }

6.ViewData與TempData

ViewData 局限于目前Action,TempData跨Action.

TempData因為并不是通過網頁參數傳值,是以肯定是把資料存儲在某個地方的原因

   第一:檢視Controller類的源碼,其中包含一個重要的方法:在這個方面開始前就調用了基類的TempData.Load方法。

protected override  void ExecuteCore()

{

    base.TempData.Load(base.ControllerContext, this.TempDataProvider);

    try

        string requiredString = this.RouteData.GetRequiredString("action");

        if (!this.ActionInvoker.InvokeAction(base.ControllerContext, requiredString))

            this.HandleUnknownAction(requiredString);

    finally

        base.TempData.Save(base.ControllerContext, this.TempDataProvider);

}

       第二:TempData.Load方法:可以看到最終是由ITempDataProvider這個接口來完成。

public void Load(ControllerContext controllerContext, ITempDataProvider tempDataProvider)

    IDictionary<string, object> dictionary = tempDataProvider.LoadTempData(controllerContext);

    this._data = (dictionary != null) ? new Dictionary<string, object>(dictionary,

StringComparer.OrdinalIgnoreCase) : new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

    this._initialKeys = new HashSet<string>(this._data.Keys);

    this._modifiedKeys.Clear();

      第三:ITempDataProvider:我們可以在第一條中的代碼中發現,接口是這樣取的:

public ITempDataProvider TempDataProvider

    get

        if (this._tempDataProvider == null)

            this._tempDataProvider = new SessionStateTempDataProvider();

        return this._tempDataProvider;

    set

        this._tempDataProvider = value;

        第四:SessionStateTempDataProvider,從這個名字我們就可以猜測,資料應該是用Session方式儲存。主要包含了兩個方法,分别用于加載資料和儲存資料。

public virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext)

    HttpContextBase httpContext = controllerContext.HttpContext;

    if (httpContext.Session == null)

        throw new InvalidOperationException(MvcResources.SessionStateTempDataProvider_SessionStateDisabled);

    Dictionary<string, object> dictionary = httpContext.Session["__ControllerTempData"] as Dictionary<string,

object>;

    if (dictionary != null)

        httpContext.Session.Remove("__ControllerTempData");

        return dictionary;

    return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

public virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)

    httpContext.Session["__ControllerTempData"] = values;

      小結:TempData雖然用Session來實作資料的存儲,但對伺服器來講,代價雖然有,但并不高,因為從代碼上看TempData用完一次就會被消除掉。看到這,我們可以想,是否可以看定義一個TempDataProvider,當然可以。這裡我建立一個示例,并沒有做功能上的改變,可以根據實際情況修改:

      1:建立MyTempDataProvider,讓它繼承ITempDataProvider ,并且實作LoadTempData和SaveTempData。

      2:将MyTempDataProvider與Controller聯系上,我們可以選擇擴充預設控制器工廠(DefaultControllerFactory) ,重寫IController CreateController方法:

public override IController CreateController(RequestContext requestContext, string controllerName)

            Controller controller = base.CreateController(requestContext, controllerName) as Controller ;

            if (null != controller)

                controller.TempDataProvider = new MyTempDataProvider();

            return controller;

       3:注冊我們自定義的MyControllerFactory,這也是最後一步。

            ControllerBuilder.Current.SetControllerFactory(typeof(MyControllerFactory ));

7.Action和Filter

  Filter在Asp.net MVC中它隻能限制于Action,Controller。 繼承于ActionFilterAttribute,且可以覆寫如下幾個重要方法。

       1:void OnActionExecuting(ActionExecutingContext):Action執行前的操作

       2:void OnActionExecuted(ActionExecutedContext):Action執行後的操作

       3:void OnResultExecuting(ResultExecutingContext):解析ActionResult前執行

    4:void OnResultExecuted(ResultExecutedContext):解析ActionResult後執行

系統提供的比較常見的Filter:

       1:AcceptVerbs

       2:ActionName,上面兩個都限制了對Action的通路條件;

       3:OutputCache,設定緩存;      

   4:ValidateInput,增加資料驗證。

如:[AcceptVerbs(HttpVerbs.Get )]

     public ActionResult Index()

  3:ActionNameAttribute的用法,和 AcceptVerbsAttribute 方式差不多,如果不指定ActionName,則系統會預設找名稱和方法名相同之處的View。

[ActionName ("Edit")]

public ActionResult Edit(int id)

  自定義Filter:

              這裡建立一個沒有用戶端緩存的NoClientCacheAttribute。需要繼承ActionFilterAttribute,且重寫OnActionExecuting方法。

    public class NoClientCacheAttribute : ActionFilterAttribute

        public override void OnActionExecuting(ActionExecutingContext filterContext)

            HttpContext.Current.Response.CacheControl = "No-Cache";

     應用在Action上,特别簡單,像C#中的變通特性用法一樣。

[NoClientCache]

public ActionResult Details(int id)

8. System.Web.Mvc.Html下的HtmlHelper

   System.Web.Mvc.Html下的HtmlHelper隻能完成大部分html控件的輸出,但像img标簽預設是沒有提供的,這裡需要我們自行來擴充下Helper,畢竟上面的衆多方法都是擴充出來的。

        擴充Helper,我們可以利用TagBuilder,它能輸出所有标簽及屬性。TagBuilder提供下如下重要方法:

// Methods

    public TagBuilder(string tagName);

    public void AddCssClass(string value);//增加樣式

    public void GenerateId(string name);//設定控件ID

    private string GetAttributesString();

    public void MergeAttribute(string key, string value);//設定屬性值

    public void MergeAttribute(string key, string value, bool replaceExisting);

    public void MergeAttributes<TKey, TValue>(IDictionary<TKey, TValue> attributes);

    public void MergeAttributes<TKey, TValue>(IDictionary<TKey, TValue> attributes, bool replaceExisting);

    public void SetInnerText(string innerText);//設定顯示文本

    public override string ToString();

    public string ToString(TagRenderMode renderMode);//輸出控件html

       1:建立ImageHelper,利用TagBuilder部分方法最終輸出img标簽。

public static class ImageHelper

        public static string Image(this HtmlHelper helper, string id, string url, string alternateText)

            return Image(helper, id, url, alternateText, null);

        public static string Image(this HtmlHelper helper, string id, string url, string alternateText, object

htmlAttributes)

            // 建立IMG标簽

            var builder = new TagBuilder("img");

            // 增加ID屬性

            builder.GenerateId(id);

            // 增加屬性

            builder.MergeAttribute("src", url);

            builder.MergeAttribute("alt", alternateText);

            builder.MergeAttributes(new RouteValueDictionary(htmlAttributes));

            // 輸出完整的img标簽

            return builder.ToString(TagRenderMode.SelfClosing);

       2:頁面調用。

<%= Html.Image("img1", "http://a.lakequincy.com/img/633820582974214892.jpg", "這是一張圖檔", new

{border="4px"})%>

9.MVC下開發ajax程式

  MVC有個特點,一般情況下一個頁面檔案都會對應一個Controller,類似于web form模式下的頁面背景代碼。Conntroller裡面的每個公共方法(私有方法不行)都可以通過頁面位址中通路,例如我們在HomeController中有這樣一個方法:

        public   void Test(int i)

            System .Web .HttpContext .Current .Response .Write ("aaa"+i .ToString ());

       我們可以在浏覽器中輸入/Home/Test?i=1,此時頁面上就會輸出我們想要的内容,這也是web form模式沒有辦法直接實作的。即然MVC能夠直接調用Controller中的方法,也就是我們不用單獨建立一些類來實作,這點和ajaxpro的功能有點相似。下面我們就來實作在asp.net mvc中應用ajax,當然我選用jquery做為js架構,熟悉jquery的朋友看起來就非常容易了。

      1:建立一個學生類的集合,學生類結構如下:

    public class student

        public string sname { get; set; }

        public int ID { get; set; }

        public int Grade { get; set; }

     2:寫一個根據學生ID查找學生資訊的方法。這裡注意下,這個方法的傳回類型為JsonResult,它能夠給用戶端以json類型輸出資料(MVC能夠把目标對象轉換成json格式),這個和平時常見的ActionResult有所差別。

MVC筆記

public JsonResult TestMVC(int i, int j)

            int I = 0;

            List<student> list = new List<student>();

            for (int k = 0; k < 10; k++)

                student sd = new student() { sname = "aaa" + k.ToString() + j.ToString(), ID = k, Grade = k * 10 };

                list.Add(sd);

            var stu = (from m in list

                       where m.ID == i

                       select m

                         ).FirstOrDefault();

            JsonResult J = new JsonResult();

            J.Data = stu;

            return J;

      3:用戶端代碼:從背景取得資料後,填充到div中。

$.getJSON('/Home/TestMVC',{i:1,j:2},

                function(data) {

                    $("#divStudent").html(data.sname);

                );

       分析:以上三步基本上就可以實作一般的ajax程式,如有不同,也隻可能是程式寫法問題,大體流程都差不多應該相同。這種寫法已經非常簡潔了,但還有可以提高的地方。

             第一:開發人員需要拼接ajax請求的位址。本例中為Home/TestMVC

             第二:開發人員需要準備建構ajax方法使用的data參數。本例中為,{i:1,j:2}

      解決思路:讓程式自動為我們完成上面兩步。可以參考ajaxpro的實作原理,每個方法異步請求的方法上加一個自定義特性标簽,編譯器遇到自定義标簽後,自動生成一些js方法,來讓開發者前端調用更加友善。例如生成如下代碼:i,j分别是異步請求方法的兩個參數,callback為異步請求後的回調方法。

MVC筆記

   var HomeController = {

            TestMVC: function(i, j,callback)

                                                {

                                                     $.getJSON('/Home/TestMVC?id=&',{i:i, j:j}, callback);

                                                }

     我們可以這樣調用:我們隻需要輸入相應參數,以及完成回調方法即可。是不是簡單了點。下一篇來講講具體實作方法。

HomeController.TestMVC(j,j+1, function(data) {

             $("#divStudent").html(data.sname);

             });  

繼續閱讀