MVC笔记

简介: 1.MVC概念 --Model:用于存储数据的组件--View:根据Model数据进行内容展示的组件--Controller:接受并处理用户指令(操作Model),选择一个View并输出内容。

1.MVC概念

--Model:用于存储数据的组件

--View:根据Model数据进行内容展示的组件
--Controller:接受并处理用户指令(操作Model),选择一个View并输出内容。
Controller对View进行引用,但是View不知道Controller的存在。Controller和View都是单向引用Model

MVC变种:Observer模式,MVP模式。

2.mvc路由机制

MVC中重要的路由处理,默认情况是在Global.asax文件中,我们也可以把这块内容独立出来。

代码
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;

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

            writer.AddAttribute( " class " , " message " );
            writer.RenderBeginTag(HtmlTextWriterTag.Div);
            writer.Write(messageViewData.Message);
            writer.RenderEndTag();
           
return writer.InnerWriter.ToString();
        }
    }  


1 public  class ViewDataBase

2     {
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)
{
    HttpContextBase httpContext
= controllerContext.HttpContext;
   
if (httpContext.Session == null )
    {
       
throw new InvalidOperationException(MvcResources.SessionStateTempDataProvider_SessionStateDisabled);
    }
    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,这也是最后一步。

protected void Application_Start()
        {
            ControllerBuilder.Current.DefaultNamespaces.Add(
" GuestBook.MVC.Controller " );
            ModelBinders.Binders.Add(
typeof (GuestBookInfo ), new GuestBookBinder ());
            ControllerBuilder.Current.SetControllerFactory(
typeof (MyControllerFactory ));
            RegisterRoutes(RouteTable.Routes);
        }

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有所区别。
    

代码
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为异步请求后的回调方法。
      

代码
   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);
             });  

 

 

目录
相关文章
|
前端开发
前端学习笔记202305学习笔记第三十一天-什么是mvc-数据操作和视图更新4
前端学习笔记202305学习笔记第三十一天-什么是mvc-数据操作和视图更新4
60 1
|
前端开发
前端学习笔记202305学习笔记第三十一天-什么是mvc-前端路由解析和渲染4
前端学习笔记202305学习笔记第三十一天-什么是mvc-前端路由解析和渲染4
54 0
|
前端开发
前端学习笔记202305学习笔记第三十一天-什么是mvc-前端路由解析和渲染2
前端学习笔记202305学习笔记第三十一天-什么是mvc-前端路由解析和渲染2
53 0
|
前端开发
前端学习笔记202305学习笔记第三十一天-什么是mvc-vc和路由的绑定2
前端学习笔记202305学习笔记第三十一天-什么是mvc-vc和路由的绑定2
54 1
|
前端开发
前端学习笔记202305学习笔记第三十一天-什么是mvc-mvc和M层请求数据2
前端学习笔记202305学习笔记第三十一天-什么是mvc-mvc和M层请求数据2
47 1
|
前端开发
前端学习笔记202305学习笔记第三十天-什么是mvc-m层的创建和数据展示
前端学习笔记202305学习笔记第三十天-什么是mvc-m层的创建和数据展示
57 0
|
前端开发 API
前端学习笔记202305学习笔记第三十天-什么是mvc-c层api 前后端联动1
前端学习笔记202305学习笔记第三十天-什么是mvc-c层api 前后端联动1
75 0
|
前端开发
前端学习笔记202305学习笔记第二十九天-什么是mvc-m层的创建和数据展示1
前端学习笔记202305学习笔记第二十九天-什么是mvc-m层的创建和数据展示1
36 0
|
前端开发 API
前端学习笔记202305学习笔记第三十一天-什么是mvc-c层api 前后端联动3
前端学习笔记202305学习笔记第三十一天-什么是mvc-c层api 前后端联动3
56 0
|
前端开发 API
前端学习笔记202305学习笔记第三十一天-什么是mvc-c层api 和mvc总结3
前端学习笔记202305学习笔记第三十一天-什么是mvc-c层api 和mvc总结3
48 0