Asp.net MVC 示例项目"Suteki.Shop"分析之---ViewData

简介:
    使用强类型的ViewData好处有许多,比如说在IDE中就会有更好的支持,比如代码提示。同时在View与Controller之间有更严谨的“约定”。在Suteki.Shop项目中作者对强类型的ViewPage引入是通过MvcContrib实现的,下面就是ViewPage<T>代码(Suteki.Shop\Views\ViewPage.cs):     
 
public   class  ViewPage < T >  : MvcContrib.FluentHtml.ModelViewPage < T >   where  T :  class  
{
        
public  ViewPage() :  base ( new  LowercaseFirstCharacterOfNameBehaviour())
        {}
}

public   class  ViewUserControl < T >  : MvcContrib.FluentHtml.ModelViewUserControl < T >   where  T :  class
{
        
public  ViewUserControl() :  base ( new  LowercaseFirstCharacterOfNameBehaviour())
        {}
}

          可以看出ViewPage和ViewUserControl只是对MvcContrib中ModelViewPage,ModelViewUserControl的继承,代码很简单,没什么太多可说的。
        强类型的ViewData使用形如:ViewPage<TViewData>,我们可以通过打开一个View看一下,比如“编辑用户信息”时的视图头声明部分:
      
<% @ Page Title = ""  Language = " C#  Inherits = " Suteki.Shop.ViewPage<ShopViewData> "
    
         其中的ShopViewData就是TViewData。在Suteki.Shop中作者使用ShopViewData对Model中大部分类作了相应的属性和数据绑定的统一封装,感觉ShopViewData就是Model的集合体或者是“缩影”,这样的好处就我看来主要是在View中进行强类型ViewData绑定时统一参数,这里感觉有偷懒之嫌。不过因此造成其自身视图数据的“庸肿”,其内部有太多的属性,还是就是绑定传递时的效率可能也会存在一些问题(只是猜测,未测试过,呵呵)。
         好了,下面就开始正文。
    
         首先我们要看一下Suteki.Common\ViewData文件夹下面的几个类,包括:
    
         IErrorViewData,IMessageViewData,ViewDataBase等,其类图如下:

    
        
    
         从图中看出,ViewDataBase是其体系“核心”, 其实现了 IMessageViewData, IErrorViewData这两个接口。实体代码如下:
   
 
public   abstract   class  ViewDataBase : IMessageViewData, IErrorViewData
{
        
public   string  Message {  get set ; }
        
public   string  ErrorMessage {  get set ; }

        
public  ViewDataBase WithErrorMessage( string  errorMessage)
        {
            
this .ErrorMessage  =  errorMessage;
            
return   this ;
        }

        
public  ViewDataBase WithMessage( string  message)
        {
            
this .Message  =  message;
            
return   this ;
        }
}
   
         该抽象类的属性MessageErrorMessage分别实现了IMessageViewDataIErrorViewData的接口属性。主要 用于显示临时操作信息(比如“成功添加用户”,“成功编辑用户”等)。其所提供的两个方法“WithErrorMessage” 和“WithMessage”只是对相应属性的简单绑定而已。
   
        有了ViewDataBase之后,下面就来看一下其子类实现了,下面是相应类图:
    
    
        正如前面所介绍的那样,子类中最“重要”的当属“ShopViewData”,其包括了基本所有Model中的类型,并将它们以“属性”的方法提供出来以便于前台View使用,同时ShopViewData还提供了与其属性相关的绑定方法(均以With...”开头),下面就是其代码。   
 
Code
    
         为了便于使用,Suteki.Shop还以静态属性的方式进行了封闭,最终以ShopView这个类开放出来提供给Action和View使用,其实现代码如下:
    
 
///   <summary>
///  So you can write 
///  ShopView.Data.WithProducts(myProducts);
///   </summary>
public   class  ShopView
{
     
public   static  ShopViewData Data {  get  {  return   new  ShopViewData(); } }
}
        
         下面以“编辑用户”这个Action来看一下其使用方法: 
 
[AcceptVerbs(HttpVerbs.Post), UnitOfWork]
public  ActionResult Edit([DataBind] User user,  string  password)
{
        
if ( !   string .IsNullOrEmpty(password))
        {
            user.Password 
=  userService.HashPassword(password);
        }

        
try
        {
            user.Validate();
        }
        
catch  (ValidationException validationException)
        {
            validationException.CopyToModelState(ModelState, 
" user " );
            
return  View( " Edit " , EditViewData.WithUser(user));
        }

        
return  View( " Edit " , EditViewData.WithUser(user).WithMessage( " Changes have been saved " )); 
}
   
         注意其中的EditViewData属性就是初始化一个ShopViewData实例并调用该实例的WithRoles()方法来完成对用户规则的获取。然后在"Edit"这个Action的返回语句中继续绑定其他信息,如当前编辑的用户信息“user”,以及操作提示信息“Changes have been saved”。
         这样就可以在View中对ShopViewData进行显示操作了。这里要说明的是在View中对Message的显示是通过下面这一行完成的:      
 
<% =  Html.MessageBox(ViewData.Model)  %>
     
         而这个方法是对HtmlHelper这个MVC类的扩展方法,其方法定义如下:  
 
public   static   string  MessageBox( this  HtmlHelper htmlHelper, IMessageViewData 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();
}
 
          大家看到了其传入的参数是IMessageViewData类型,而传入的是“ShopViewData”类型,如下图所示:
    
      
        而看过上面内容的话,就可以通过其类图中实现的方法看出这个继承实现链表: 
      ShopViewData ==> ViewDataBase == > IMessageViewData

    
         所以扩展文法直接就完成了这种“向上转型”操作。    
    
         除了“编辑用户”这种在Action中直接绑定Message字段属性的方式,Suteki.Shop还提供了Filter方式的“操作信息”绑定,比如 CopyMessageFromTempDataToViewData(Suteki.Shop\Filters),其代码如下:
 
public   class  CopyMessageFromTempDataToViewData : ActionFilterAttribute
{
        
public   override   void  OnActionExecuted(ActionExecutedContext filterContext) 
        {
                var result 
=  filterContext.Result  as  ViewResult;

                
if (result  !=   null   &&  filterContext.Controller.TempData.ContainsKey( " message " ))
                {
                    var model 
=  result.ViewData.Model  as  ShopViewData;

                    
if (model  !=   null   &&   string .IsNullOrEmpty(model.Message))
                    {
                        model.Message 
=  filterContext.Controller.TempData[ " message " as   string ;
                    }
                }
        }
}
    

    
         大家请注意,上面的filterContext.Controller类型是ControllerBase(详细说明参见我之前写的这篇文章),其提供了Message属性来实现临时数据TempData["message"]的获取来绑定工作,代码如下:
 
[Rescue( " Default " ), Authenticate, CopyMessageFromTempDataToViewData]
public   abstract   class  ControllerBase : Controller, IProvidesBaseService
{
        
        
     
public   string  Message
     {
         
get  {  return  TempData[ " message " as   string ; }
         
set  { TempData[ " message " =  value; }
     }
}

    
         这样就可以通过CopyMessageFromTempDataToViewData这个Filter来实现将临时数据绑定到ShopViewData中的Message属性,并提供给前台View使用了。当然这是有条件的,就是上面代码中的这一行:
     if (model  !=   null   &&   string .IsNullOrEmpty(model.Message)) 

         从逻辑上看,这样做应该是防止对已绑定操作信息(model.Message不为空)进行“误覆盖”吧。
    
    
         好了,今天的内容就先到这里了。
    

本文转自 daizhenjun 51CTO博客,原文链接:http://blog.51cto.com/daizhj/160560,如需转载请自行联系原作者
相关文章
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
443 5
Visual Studio 快速分析 .NET Dump 文件
【11月更文挑战第10天】.NET Dump 文件是在 .NET 应用程序崩溃或出现问题时生成的,记录了应用程序的状态,包括内存对象、线程栈和模块信息。通过分析这些文件,开发人员可以定位和解决内存泄漏、死锁等问题。在 Visual Studio 中,可以通过调试工具、内存分析工具和符号加载等功能来详细分析 Dump 文件。此外,还可以使用第三方工具如 WinDbg 进行更深入的分析。
1372 1
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
487 7
|
存储 运维
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
.NET开发必备技巧:使用Visual Studio分析.NET Dump,快速查找程序内存泄漏问题!
532 2
|
开发框架 JSON .NET
ASP.NET Core 标识(Identity)框架系列(三):在 ASP.NET Core Web API 项目中使用标识(Identity)框架进行身份验证
ASP.NET Core 标识(Identity)框架系列(三):在 ASP.NET Core Web API 项目中使用标识(Identity)框架进行身份验证
440 1
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
401 0
|
开发框架 缓存 .NET
【App Service】在Azure App Service中分析.NET应用程序的性能的好帮手(Review Stack Traces)
【App Service】在Azure App Service中分析.NET应用程序的性能的好帮手(Review Stack Traces)
247 0
|
Linux C# C++
【Azure App Service For Container】创建ASP.NET Core Blazor项目并打包为Linux镜像发布到Azure应用服务
【Azure App Service For Container】创建ASP.NET Core Blazor项目并打包为Linux镜像发布到Azure应用服务
255 0
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
411 0
|
开发框架 .NET API
如何在 ASP.NET Core Web Api 项目中应用 NLog 写日志?
如何在 ASP.NET Core Web Api 项目中应用 NLog 写日志?
936 0