剛剛檢查完支持工單中的一些代碼,筆者想針對 ASP.NET MVC 應(yīng)用的改進(jìn)寫一些建議。這些內(nèi)容仍在筆者腦海中,愿與各位一同分享。若你已使用 MVC 一段時間,那么以下內(nèi)容可能并不新鮮。本文更適用于不常使用 MVC 或尚未充分了解 MVC 的讀者。
公司主營業(yè)務(wù):成都網(wǎng)站設(shè)計、成都做網(wǎng)站、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)公司是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)公司推出瀘州免費做網(wǎng)站回饋大家。假設(shè)以下場景:你想弄清楚一個網(wǎng)絡(luò)應(yīng)用在生產(chǎn)環(huán)境下為何消耗了 Web 服務(wù)器2GB 內(nèi)存,于是,你將生產(chǎn)環(huán)境中運行的應(yīng)用版本部署到本地運行,用于分析和調(diào)試。
仔細(xì)查看代碼后,你認(rèn)真地分析,可能還時不時搖搖頭,最終弄清了問題的本質(zhì),那么此時,你應(yīng)該給出反饋了。
這就是筆者今天的經(jīng)歷,從中總結(jié)出5點建議,希望能使讀者在使用 ASP.NET MVC 代碼時更加得心應(yīng)手。
筆者收到的支持工單,其根本原因在于,從數(shù)據(jù)庫中提取了大量數(shù)據(jù),導(dǎo)致占用了過量內(nèi)存。
這一問題十分常見。假如你建立了一個普通的博客,其中包含了文章以及多種媒體(圖片、視頻、附件)。你將一個 Media 數(shù)組放到 Post 域?qū)ο笾?,后者將所有圖片數(shù)據(jù)儲存在一個字節(jié)數(shù)組中。由于你使用了 ORM,因此需要采用某種方法將域模型設(shè)計完善;我們都經(jīng)歷過這一步。
public class BlogPost { public ICollectionMedia { get; set; } } public class BlogMedia { public byte[] Data { get; set; } public string Name { get; set; } }
這種設(shè)計并沒有大的不妥,你很準(zhǔn)確地建立了域模型。但問題在于,當(dāng)你通過最常用的 ORM 發(fā)起查詢時,所有與博客文章相關(guān)的數(shù)據(jù)都會被加載出來。
public IListGetNewestPosts(int take) { return _db.BlogPosts.OrderByDescending(p => p.PostDate).Take(take).ToList(); }
這一行看起來毫無問題(除非你曾受其困擾,所以了解它并非無害),但如果不取消延遲加載或沒讓 ORM 忽略日志媒體上的大「Data」屬性,那么就可能導(dǎo)致非常嚴(yán)重的后果。
你應(yīng)當(dāng)了解 ORM 是如何進(jìn)行查詢和映射對象的,并確保所查詢內(nèi)容就是需要的內(nèi)容(比如使用 projection),這一點十分重要。
public IListGetNewestPosts(int take) { return _db.BlogPosts.OrderByDescending(p => p.PostDate).Take(take).Select(p => new PostSummary() { Title = p.Title, Id = p.Id }).ToList(); }
這能確保只抓取任務(wù)真正需要的數(shù)據(jù)量。如果你要做的僅僅是使用標(biāo)題和 ID 在主頁上建立一個鏈接,那么得到這倆屬性就夠了。
你可以在知識庫中準(zhǔn)備5種以上的方法,為使用戶界面更加完善,再仔細(xì)也不為過。
這一條比較難注意到。設(shè)想 MVC 視圖中的以下代碼:
@foreach(var post in Model.RelatedPosts) { ... }
看起來沒什么問題,但如果仔細(xì)看看這一模型屬性中隱含的內(nèi)容呢?
public class MyViewModel { public IListRelatedPosts { get { return new BlogRepository().GetRelatedPosts(this.Tags); } } }
呀!「視圖模型」中含有業(yè)務(wù)邏輯,此外還直接調(diào)用了一個數(shù)據(jù)存取方法。如此一來,數(shù)據(jù)存取代碼被引入了陌生的區(qū)域,并隱藏在屬性中。將此代碼移動到控制器中,便于對其進(jìn)行討論并有意識地為視圖模型添加內(nèi)容。
此處正好說明一下,適當(dāng)?shù)膯卧獪y試可幫助發(fā)現(xiàn)此類問題;由于肯定不能攔截對這此類方法的調(diào)用,你可能會恍然大悟,不該將知識庫注入視圖模型中。
如需在視圖中執(zhí)行業(yè)務(wù)邏輯,那就應(yīng)重新考慮視圖模型和邏輯。不建議在 MVC Razor 視圖中執(zhí)行此類操作。
@{ var blogController = new BlogController(); }
切勿在視圖中使用業(yè)務(wù)邏輯,但除此之外,你可以創(chuàng)建一個控制器!將業(yè)務(wù)邏輯移動到動作方法中,并將視圖模型用于原本的用途。還可以將業(yè)務(wù)邏輯移動到單獨的動作方法中,這一動作方法僅在視圖內(nèi)被調(diào)用,這樣就可在必要時單獨對其進(jìn)行緩存。
//In the controller: [ChildActionOnly] [OutputCache(Duration=2000)] public ActionResult TagsForPost(int postId) { return View(); } //In the view: @{Html.RenderAction("TagsForPost", new { postId = p.Id });}
注意 「ChildActionOnly」 屬性。MSDN中提到:
任何一個標(biāo)有 「ChildActionOnlyAttribute」的方法都只能與 「Action」或「RenderAction」HTML 擴展方法一同被調(diào)用。
這就意味著,沒有人能通過操作 URL 來訪問你的子動作(如果你采用了默認(rèn)路徑)。
在 MVC 庫中,局部模塊和子動作都是很有用的工具,所以充分利用起來吧!
有了以上的代碼做鋪墊,如果只緩存視圖模型,又會有怎樣的效果呢?
public ActionResult Index() { var homepageViewModel = HttpContext.Current.Cache["homepageModel"] as HomepageViewModel; if (homepageViewModel == null) { homepageViewModel = new HomepageViewModel(); homepageViewModel.RecentPosts = _blogRepository.GetNewestPosts(5); HttpContext.Current.Cache.Add("homepageModel", homepageViewModel, ...); } return View(homepageViewModel); }
什么效果也沒有!由于是通過視圖中的控制器變量和視圖模型中的屬性進(jìn)入數(shù)據(jù)層,因此并不能提升性能……緩存視圖模型并沒有什么用處。
試試緩存 MVC 動作的輸出吧:
[OutputCache(Duration=2000)] public ActionResult Index() { var homepageViewModel = new HomepageViewModel(); homepageViewModel.RecentPosts = _blogRepository.GetNewestPosts(5); return View(homepageViewModel); }
請注意非常好用的「OutputCache」屬性。MVC 支持 ASP.NET 輸出緩存,因此請在適當(dāng)情況下,充分利用這一特點。如需緩存模型,那么模型基本上應(yīng)為帶自動(且只讀)屬性的 POCO,不能調(diào)用其他知識庫方法。
另外還想介紹筆者尚未嘗試的一個好方法,即采用不同的輸出緩存供應(yīng)商,從而在AppFabric、NoSQL 或其他任何需要的地方進(jìn)行緩存。MVC 的可擴展性非常強。
如果不好好利用 ORM 的特征集,那真是極大的損失。筆者所檢查的代碼庫中用到了 NHibernate,但是并未真正利用好。本可以用來解決一部分內(nèi)存問題的 NHibernate高級射影功能完全被忽略了。這一問題有時是因為使用“庫模式”所造成的僵化思維,有時則是由于缺乏必要的知識。
與僅僅使用基本的類方法相比,通過利用 EF 或 NHibernate 特征,知識庫的功能可以大大增加。它們可以在控制器中形成和返回你真正想要的數(shù)據(jù),大大增強控制器的邏輯性。趕緊閱讀 ORM 文件,了解一下它可以提供的功能吧,這將使你受益良多。
筆者認(rèn)為,采用知識庫模式,就好比驅(qū)除掉霧霾,使明媚的陽光從 ORM 窗口照進(jìn)來。剛接觸 RavenDB 時,筆者丟棄了知識庫層(實際上是整個數(shù)據(jù)項目),在應(yīng)用服務(wù)層中完全使用 Raven 查詢,用了一點點擴展方法來重復(fù)使用查詢邏輯。筆者發(fā)現(xiàn),許多邏輯都明顯依賴于特定的上下文,且利用 Raven 的擴展特性進(jìn)行投射、形成并分批處理查詢,大有益處。
如果你認(rèn)為可以將 ORM 抽象化,筆者強烈建議你換個角度思考。ORM 確實是抽象概念,如果你認(rèn)為,由于 ORM 是「抽象」的,所以輕而易舉就能用別的 ORM 置換現(xiàn)有的 ORM,那么事實會讓你大吃一驚。因為我之前也是這么想的,直到我了解到,轉(zhuǎn)換至 Raven 簡直改變了我整個代碼庫,這是我完全沒有預(yù)料到的。ORM 不僅僅影響到數(shù)據(jù)存取,還會影響域以及業(yè)務(wù)邏輯,甚至?xí)绊懹脩艚缑?。通過移除知識庫抽象,可以切實降低數(shù)據(jù)存取代碼的整體復(fù)雜度。
家父常常拿這句話提醒我。有時候,通過仔細(xì)查閱代碼,也會發(fā)現(xiàn)你認(rèn)為人人皆知的道理,事實并非如此;你可能從實踐經(jīng)驗中了解到這一點,或在 google 上讀到這一點,就錯誤地假設(shè)這是人人都知道的事實了。
希望這篇文章能幫到需要的人!
OneAPM 助您輕松鎖定 .NET 應(yīng)用性能瓶頸,通過強大的 Trace 記錄逐層分析,直至鎖定行級問題代碼。以用戶角度展示系統(tǒng)響應(yīng)速度,以地域和瀏覽器維度統(tǒng)計用戶使用情況。想閱讀更多技術(shù)文章,請訪問 OneAPM 官方博客。
本文轉(zhuǎn)自 OneAPM 官方博客
原文地址:http://kamranicus.com/blog/2014/01/29/5-tips-to-improve-your-mvc-site/
創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動態(tài)BGP最優(yōu)骨干路由自動選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機房獨有T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動現(xiàn)已開啟,新人活動云服務(wù)器買多久送多久。