您的位置:首页 > 编程语言 > ASP

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

2009-05-25 09:18 856 查看
使用强类型的ViewData好处有许多,比如说在IDE中就会有更好的支持,比如代码提示。同时在View与Controller
之间有更严谨的“约定”。在Suteki.Shop项目中作者对强类型的ViewPage引入是通过MvcContrib实现的,下面就是
其ViewPage<T>代码(Suteki.Shop/Views/ViewPage.cs):
可以看出ViewPage和ViewUserControl只是对MvcContrib中ModelViewPage,ModelViewUserControl的继承,
代码很简单,没什么太多可说的。

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())
{}
}


强类型的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;
}
}


该抽象类的属性Message,ErrorMessage分别实现了IMessageViewData和IErrorViewData的接口属性。主要
用于显示临时操作信息(比如“成功添加用户”,“成功编辑用户”等)。其所提供的两个方法“WithErrorMessage”
和“WithMessage”只是对相应属性的简单绑定而已。

有了ViewDataBase之后,下面就来看一下其子类实现了,下面是相应类图:




正如前面所介绍的那样,子类中最“重要”的当属“ShopViewData”,其包括了基本所有Model中的类型,并将
它们以“属性”的方法提供出来以便于前台View使用,同时ShopViewData还提供了与其属性相关的绑定方法(均以
“With...”开头),下面就是其代码。
为了便于使用,Suteki.Shop还以静态属性的方式进行了封闭,最终以ShopView这个类开放出来提
供给Action和View使用,其实现代码如下:




Code
public class ShopViewData : ViewDataBase
{
public Category Category { get; set; }
public IEnumerable<Category> Categories { get; set; }

public Product Product { get; set; }
public IEnumerable<Product> Products { get; set; }

public IEnumerable<Role> Roles { get; set; }

public User User { get; set; }
public IEnumerable<User> Users { get; set; }

public Basket Basket { get; set; }

public Order Order { get; set; }
public PagedList<Order> Orders { get; set; }
public OrderSearchCriteria OrderSearchCriteria { get; set; }

public IEnumerable<Country> Countries { get; set; }
public Country Country { get; set; }

public IEnumerable<CardType> CardTypes { get; set; }

public Postage Postage { get; set; }
public IEnumerable<Postage> Postages { get; set; }

public PostageResult PostageResult { get; set; }

public Card Card { get; set; }

public IEnumerable<StockItem> StockItems { get; set; }

public IEnumerable<Content> Contents { get; set; }

public IEnumerable<OrderStatus> OrderStatuses { get; set; }

// attempt at a fluent interface

public ShopViewData WithCategory(Category category)
{
this.Category = category;
return this;
}

public ShopViewData WithCategories(IEnumerable<Category> categories)
{
this.Categories = categories;
return this;
}

public ShopViewData WithProduct(Product product)
{
this.Product = product;
return this;
}

public ShopViewData WithProducts(IEnumerable<Product> products)
{
this.Products = products;
return this;
}

public ShopViewData WithRoles(IEnumerable<Role> roles)
{
this.Roles = roles;
return this;
}

public ShopViewData WithUser(User user)
{
this.User = user;
return this;
}

public ShopViewData WithUsers(IEnumerable<User> users)
{
this.Users = users;
return this;
}

public ShopViewData WithBasket(Basket basket)
{
this.Basket = basket;
return this;
}

public ShopViewData WithOrders(PagedList<Order> orders)
{
this.Orders = orders;
return this;
}

public ShopViewData WithOrder(Order order)
{
this.Order = order;
return this;
}

public ShopViewData WithOrderSearchCriteria(OrderSearchCriteria orderSearchCriteria)
{
this.OrderSearchCriteria = orderSearchCriteria;
return this;
}

public ShopViewData WithCountries(IEnumerable<Country> countries)
{
this.Countries = countries;
return this;
}

public ShopViewData WithCountry(Country country)
{
this.Country = country;
return this;
}

public ShopViewData WithCardTypes(IEnumerable<CardType> cardTypes)
{
this.CardTypes = cardTypes;
return this;
}

public ShopViewData WithPostage(Postage postage)
{
this.Postage = postage;
return this;
}

public ShopViewData WithPostages(IEnumerable<Postage> postages)
{
this.Postages = postages;
return this;
}

public ShopViewData WithTotalPostage(PostageResult postageResult)
{
this.PostageResult = postageResult;
return this;
}

public ShopViewData WithCard(Card card)
{
this.Card = card;
return this;
}

public ShopViewData WithStockItems(IEnumerable<StockItem> stockItems)
{
this.StockItems = stockItems;
return this;
}

public ShopViewData WithContents(IEnumerable<Content> contents)
{
this.Contents = contents;
return this;
}

public ShopViewData WithOrderStatuses(IEnumerable<OrderStatus> orderStatuses)
{
this.OrderStatuses = orderStatuses;
return this;
}
}


/// <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),
其代码如下:





所以扩展文法直接就完成了这种“向上转型”操作。

除了“编辑用户”这种在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不为空)进行“误覆盖”吧。


好了,今天的内容就先到这里了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: