剛用MVC完成一個小項目,總結一些MVC技巧
剛用MVC完成了一個小項目,MVC技術又有了一次提升,所以,再次寫一點總結性的東西。
開發環境:Visual Studio 2010 RC, MVC 2 RC, Entity Framework, SQL Server 2008
1、不對IIS做任何修改,如何在IIS6下運行MVC?
這個可以參考我前面一篇文章
(原創,和微軟官方做法不同,可以不修改IIS設置就達到目的)
傳送門:http://www.rzrgm.cn/dozer/archive/2010/02/13/run-MVC-in-IIS6.html
2、不同Areas的Controller重復導致的問題
兩個不同的Areas會有不同的命名空間,但是會有相同的 Controller
而在網站MapRoute的時候卻只能識別 Controller,因此會出現錯誤。
假設,我在新建一個MVC項目后,直接新建一個Areas,并且命名為Admin,新建一個Home Controller
運行,彈出以下錯誤:
“/”應用程序中的服務器錯誤。
The controller name 'Home' is ambiguous between the following types:
MvcApplication1.Controllers.HomeController
MvcApplication1.Areas.Admin.Controllers.HomeController
遇到這種情況,只要這樣就可以了
context.MapRoute(
"Web_default",
"{controller}/{action}/{id}",
new {controller="Home", action = "Index", id = "" },
new string[] { "MvcApplication1.Areas.Web.Controllers" }
);
調用 MapRoute 的時候在后面多傳入一個 String[] ,并且填入你需要 Route 的那個 Areas 的 Controller 所在的命名空間
注意!2個地方都要改
3、關于Filter
MVC提供的4個Filter很方便,但是有一個問題,Filter中不能直接調用 ViewData,TempData等字段。
雖然可以在傳入的 filterContext 中調用到,但是很不方便。
這時候,其實可以把4個Filter繼承一下,自己寫一個新的,并在內部放入ViewData等私有字段
用 IAuthorizationFilter 來舉個例子:
public abstract class BaseFilterAttribute : FilterAttribute
{//這里可以根據自己的喜好來設定
protected HttpSessionStateBase Session;
protected ModelStateDictionary State;
protected ViewDataDictionary ViewData;
protected TempDataDictionary TempData;
protected HttpRequestBase Request;
protected Dictionary<string, string> RouteValues;
protected UrlHelper Url;
protected void Initialize(ControllerContext filterContext)
{//初始化
Request = filterContext.RequestContext.HttpContext.Request;
RouteValues = new Dictionary<string, string>();
foreach (var v in filterContext.RequestContext.RouteData.Values)
{
RouteValues.Add(v.Key,v.Value.ToString());
}
ViewData = filterContext.Controller.ViewData;
TempData = filterContext.Controller.TempData;
State = ViewData.ModelState;
Session = filterContext.RequestContext.HttpContext.Session;
Url = new UrlHelper(filterContext.RequestContext);
}
}
public abstract class AuthorizationFilter : BaseFilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{//調用初始化函數
Initialize(filterContext);
onAuthorization(filterContext);
}//這里把原來的 OnAuthorization 替換了一下
public abstract void onAuthorization(AuthorizationContext filterContext);
}
然后需要使用 IAuthorizationFilter 的時候只要繼承上面的 AuthorizationFilter 即可
4、MVC中控制js代碼(修改版)
大家覺得MVC的架構變了,但其實原理和Asp.net一樣,還是用 Response來輸出數據
所以,只要在 Action 的 Return 函數前調用 Response.Write("text"); 即可實現。
其實和以前一樣,下面舉個例子,在一個頁面中彈出一個對話框后再跳轉到別的頁面
public ActionResult Test()
{//彈出對話框
Response.Write(<script>alert('test');</script>));//跳轉到index
Response.Write("<script>window.location.href='" + Url.Action("index") + "';</script>");return null;
}
修改版:
這里,我換了一種思路來實現:
public ActionResult Test()
{
ViewData["JSAlert"] = "保存成功";
ViewData["JSHref"] = "保存成功";
return PartialView("JS");
}
然后返回一個 用戶控件
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script type="text/jscript">
alert('<%=ViewData["JSAlert"] %>');
window.location.href = '<%=ViewData["JSHref"] %>';
</script>
5、Web.Debug.config 和 Web.Release.config 的用法
利用 Web.config, Web.Debug.config, Web.Release.config
可以在不同環境下生成3中不同的Web.config版本
在VS中調試的時候,直接使用Web.config
用Debug發布的時候,使用Web.Debug.config
用Release發布的時候,使用Web.Release.config
然后,這三個文件怎么用呢?
你可以實現 Web.config 存在一個字段,然后當發布的時候用 Web.Debug.config 內的字段替換掉
也可以本來不存在,發布的時候添加
更可以本來存在,發布的時候刪除
這里就做簡單的介紹,介紹一種替代的方法:
代碼
//Web.config下,假設有這個字段
<connectionStrings>
<add name="ModelContainer"
connectionString="metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlClient;provider connection string="Data Source=192.168.174.131,1433;Initial Catalog=Port80;User ID=port80;Password=port80;MultipleActiveResultSets=True""
providerName="System.Data.EntityClient"/>
</connectionStrings>
//在Web.Debug.config下
<connectionStrings>
<add name="ModelContainer"
connectionString</span>="metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlClient;provider connection string="Data Source=.\sqlexpress;Initial Catalog=Port80;Integrated Security=True""
providerName="System.Data.EntityClient"
xdt:Transform="Replace"
xdt:Locator="Match(name)"/>
</connectionStrings>
這樣,在用Debug發布的時候,那個字段就會被替換掉,具體用法在Web.Debug.config文件內寫了一個網站,上面有全部的語法
6、MVC中捕捉錯誤
MVC中,有一個Filter可以捕捉錯誤,但是它的用法是利用Attribute來實現的,而且只能加在Controller和Action上,所以不能捕捉別出的錯誤
其實理論上所有的錯誤肯定產生于Controller中,但有2種情況下,就不會被捕捉了
1、頁面不存在的時候,找不到對應的Controller,那沒有任何Controller被執行,所以自然也不會捕捉到錯誤了
2、在 IAuthorizationFilter 下發生錯誤的時候,錯誤捕捉代碼在IExceptionFilter中,而IAuthorizationFilter的優先權高于IExceptionFilter,所以也就捕捉不到了
那有沒有別的方法?參考了一個老外的代碼,發現了一種完美的方法
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
Response.Clear();
HttpException httpException = exception as HttpException;
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Error");
if (httpException == null)
{
routeData.Values.Add("action", "Index");
}
else //It's an Http Exception, Let's handle it.
{
switch (httpException.GetHttpCode())
{
case 404:
// Page not found.
routeData.Values.Add("action", "HttpError404");
break;
case 500:
// Server error.
routeData.Values.Add("action", "HttpError500");
break;
// Here you can handle Views to other error codes.
// I choose a General error template
default:
routeData.Values.Add("action", "General");
break;
}
}
// Pass exception details to the target error View.
routeData.Values.Add("error", exception.Message);
// Clear the error on server.
Server.ClearError();
// Call target Controller and pass the routeData.
IController errorController = new WEB.Controllers.ErrorController();
errorController.Execute(new RequestContext(
new HttpContextWrapper(Context), routeData));
}
把這段代碼放到 Global.asax 中,并且新建一個 Controller 叫做 Error
namespace WEB.Controllers
{
public class ErrorController : Controller
{
public ActionResult Index(string error)
{
ViewData["Title"] = "WebSite 網站內部錯誤";
ViewData["Description"] = error;
return View("Index");
}
public ActionResult HttpError404(string error)
{
ViewData["Title"] = "HTTP 404- 無法找到文件";
ViewData["Description"] = error;
return View("Index");
}
public ActionResult HttpError500(string error)
{
ViewData["Title"] = "HTTP 500 - 內部服務器錯誤";
ViewData["Description"] = error;
return View("Index");
}
public ActionResult General(string error)
{
ViewData["Title"] = "HTTP 發生錯誤";
ViewData["Description"] = error;
return View("Index");
}
}
}
但其實,這樣也不是完美的,因為如果你參考了我第一個問題中,在IIS6下不修改IIS設置,運行了MVC,那當后綴名不是.aspx的時候,錯誤不會被捕捉
因為這時候輸入的地址根本沒有交給網站來處理,IIS直接拋出了錯誤,因為IIS認為這個后綴名不是你所能執行的
7、View User Control中的強類型
在這樣一個場景中,頁面的右側有一個ajax表單用來登陸,放在View User Control中,所有的頁面中都存在這個VUC
而這些頁面中的主要Model類型是不同的,所以,這個View User Control肯定不能用強類型了
不能用強類型了,那怎么用 TextBoxFor 等強類型方法呢?
其實我的理解,強類型主要就是利用了 lambda表達式和反射,對Model進行了類型判斷,防止出錯
lambda中是何如識別Model類型的呢? vs編譯器會自動根據上下文來判斷,所以,如果綁定了強類型,那么
<%= Html.TextBoxFor(Model => Model.Passport)%>
就會被自動識別出來
但是上面那個場景,View User Control 中使用的Model和頁面中的不同,如果強制綁定了Model,就會出錯,提示Model類型不正確,其實可以這樣做:
在View User Control中不綁定強類型,然后這樣調用強類型方法
<%= Html.TextBoxFor(u => (u as User).Passport)%>
這里最好不要用Model,如果你的頁面Model類型也是User,并且傳入了Model數據,那個這個View User Control中的表單也會被填充
如有錯誤,或者有疑問,可以直接留言,我會及時答復
或者發我郵箱:dozer@dozer.net.cn


浙公網安備 33010602011771號