续前文。
本文是“松结对编程”系列的第十七篇。(松结对编程栏目目录)
上一篇文章基本上把Controller中Action和与Action直接相关的复用讲到了,下面讲讲比较困难的部分。
几个关键问题
1. 如何实现整体结构相同,而局部不同的复用
![](https://img-my.csdn.net/uploads/201302/01/1359684225_5316.png)
![](https://img-my.csdn.net/uploads/201302/02/1359783160_2375.png)
<div class = "item-hierarchy-title">
@item.Link()
</div>
<div class = "item-hierarchy-body">
@MFCUI.TryRenderPage(this,
"~/Areas/DLC/Views/MFC/Items/ItemHorizentalListViews/" + item.What + "/_" + item.Type + ".cshtml",
"~/Areas/DLC/Views/MFC/Items/ItemHorizentalListViews/_" + item.What + ".cshtml",
item)
<div class="item-hierarchy-footer">
@MFCUI.ImageLink("删除", "/MFC/Items/Hide?id=" + item.ID, cssClass: "hide hoverbodyof" + item.ID, showText: false, returnTo: this)
@MFCUI.ImageLink("修改名称", "/MFC/Items/EditTitle?id=" + item.ID, cssClass: "hide right hoverbodyof" + item.ID, showText: false, returnTo: this)
@item.ShowMoveLeftRight(this)
</div>
</div>
中间那个_TryRenderPage是我们封装的方法,可以理解为就是两次RenderPage,先找名为"/大类/_小类.cshtml“的View,找到不就找"_大类.cshtml"的文件;找到就传入item。
如果大家的类设计中没有What/Type这些字段,至少GetType是不同的,就可以这样使用。
2. 新建好team要转向成员分配(/TeamMembers/Index),新建好product要转向创建发布(/Products/ReleaseSchedule)
这种一般是传入一个returnUrl参数,比如(在Teams的Index.cshtml中):
Html.RenderAction("ItemHorizentalList", "Items",
new
{
area = "MFC", rootID = programID, what = SystemItemWhat.Deaprtment, type = ItemWhattype.DepartmentTypeTeam,
returnUrl = HttpUtility.UrlEncode("/Site/TeamMembers/Index?programID=" + programID)
});
returnUrl完成了重新定向的工作。当然,需要Items/ItemHorizentalList配合(在ItemsController.ItemHorizentalList()中:):
public ActionResult ItemHorizentalList(int rootID, string what, string type, string returnUrl)
{
var root = _repMFC.ReadAt<Item>(rootID);
IEnumerable<Item> subItems = root.SubItems().Where(i => i.What == what && i.Type == type);
return MFCDefaultView(root, subItems, what, type, returnUrl);
}
returnUrl被传入ItemHorizentalList.cshtml中,显示新建按钮的时候会用到:
@MFCUI.ImageLink("新建", "/MFC/Items/Create?fatherID=" + root.ID + "&what=" + what + "&type=" + type + "&returnUrl=" + returnUrl,
imgUrl: "/MFC/Items/Create48.png", showText: false)
然后是ItemsController/Create():
[HttpPost, UrlLog]
public ActionResult Create(int fatherID, string what, string type, FormCollection collection, string returnUrl)
{
ViewBag.Father = _repMFC.ReadAt<Item>(fatherID);
try
{
Item item = ……;
returnUrl = String.IsNullOrEmpty(returnUrl)
? "~/MFC/Items/Edit?id=" + item.ID + "&createNotice=off"
: returnUrl.Replace("[id]", item.ID.ToString());
Notice.CreateNotice(Notice.NoticeTypes.ItemCreated, User.Identity.Name, item, new[] {Notice.RecipientTypes.DeptTeam, Notice.RecipientTypes.UserCurrentOwner}, repMFC: _repMFC);
return Redirect(returnUrl);
}
catch (Exception e)
{
ModelState.ReportException(e);
return View(MFCWebViewPage.ViewFailed, e);
}
}
注意这里附带处理了没有指定returnUrl的情况(会转向到Edit);returnUrl有很多用处,除了返回某个页面/自动导向某个页面,还可以重复指向Create以便不断创建多个。
3. 哪些Action需要写View
哪些Action需要写View,然后在View里边RenderAction(比如这个Teams/Index);哪些不用写View,直接在Controller的Action里边RedirectToAction到ItemsController的操作?(这样简单,连View都不用谢,但有局限就是Url会跳转)?
前者的坏处是要额外写一个View。但好处是会保留Url,也就是看上去还在Teams/Index,但现实的内容却是Items/ItemHorizentalList的内容。这样如果要根据Url来安排主菜单或相关链接,则比较方便。
后者因为已经完全转向到别处了,正好相反。
这个事情困扰了我们一段时间,逐渐发现一个规律:如果一个页面是“长期页面”,比如Index,也就是说所有业务都围绕其开展,所有相关链接都在这个页面上,则写一个View保持不跳转;如果一个页面是“短期页面”,比如Create/Edit/Delete,也就是来看完后,几乎不会停留在这个页面上,则直接在Controller里边转向,加一个returnUrl办完事后回到长期页面就可以了。
4. 这些编码方法与松结对编程有何关系?
原因包括: