前言
ASP.NET 5 是一次令人驚嘆的對於ASP.NET的創新革命. 他將構建目標瞄准了 .NET Core CLR, 同時ASP.NET又是對於雲服務進行優化,並且是跨平台的框架.很多文章已經稱贊過了新的平台的優點. 但是在這篇文章中,我將展示給大家一個用VisualStudio2013,ASP.NET 4.5,MVC5,Entity Frameworkd 7編寫的小型的Demo,然后將其升級到VisualStudio2015,ASP.NET 4.5,MVC5,Entity Frameworkd 7。這個新的網站將能同時運行在.NET 4.6 CLR 和 .NET Core CLR. 上路吧。
The Random Acts of Kindness home page.
版權說明
http://www.codesnippet.info/ 手工翻譯了本文,擁有完整的版權。請勿進行未授權的轉載。
更好的閱讀體驗:從ASP.NET 升級到ASP.NET5(RC1)
本網站授權轉載
博客園 www.cnblogs.com
簡書 www.jianshu.com
譯者注:當前時間點,MVC6 RC2已經Release了。
The Random Acts 網站
我原創了RandomActs這樣的一個網站,原本是為了我在2013年和2014年關於單元測試的一些演講(talk)。RandomActs是一個架空的用來跟蹤別人的善舉的網站。這是一種類似於做善事(“do good”譯者注:呵呵。。。)的在線社交活動的項目。你使用它來創建Acts (事件), Actors (志願者), 然后將 actors 和 acts進行配對.所有的數據被保存在一個SQL Server數據庫中,一共有3張表格RandomActs, RandomActors, and RandomActActors。
RandomActs 做得比一般的僅僅使用框架的Demo要好。 它使用了repository 類(譯者注:貌似沒有好的中文對應,EF術語) 它的 controllers也涉及到了使用依賴注入(dependency injection )的 repository 類 . 他還使用了 Entity Framework 的延遲加載特性(Lazy Load) 來計算Acts和Actors的數量. 同時他還內置了一個“等待隊列”,如果志願者的數量超過了需要做的善舉的數量(譯者注:活來不及干,就排隊唄)。
盡管下文所要描述的步驟是為了我的程序定制的,但是我相信,這也會和你的程序相似,所以你最好能夠按照步驟來升級。顯而易見,如果您的項目很復雜,尤其你的解決方案包含了多個項目(Project),使用了很多第三方庫(譯者注:如果你的第三方庫不支持NET Core,比如使用了很多繪圖的庫,只能呵呵了),你可能需要做更多的工作。無論如何,希望本文至少能夠給你指出一條明道在升級這條都是坑的路上。
版本和代碼庫
本文使用的是Beta 8 updated for RC1 ,源代碼如下:
The ASP.NET 4.x version of the project can be cloned here: https://github.com/plitwin/RandomActs.git
The ASP.NET 5 (RC1) version can be cloned here: https://github.com/plitwin/RandomActs5.git
大伙看清楚啊,是Beta8!!!
大伙看清楚啊,是Beta8!!!
大伙看清楚啊,是Beta8!!!
(Important Thing Repeat Three Times)
遷移步驟
事前准備
ASP.NET5的項目結構和之前的ASP.NET項目結構可謂了天壤之別啊。
- 所有的代碼都在src目錄底下了。(以前是在Project下面的)
- 所有的靜態文件都放到了wwwroot子文件夾下面去了。
- web.config 木有了。。取而代之的是一系列的JSON文件,最重要的是 project.json.
- Global.asax 也木有了。。路由設定( Routing)放在一個新的文件了 startup.cs.
- startup.cs 也可以依賴注入的(dependency injection ),只是默認不啟用罷了。
這就意味着,你不能舒舒服服的用VS2015打開MVC6工程,然后按下一個諸如叫做“升級”的按鈕,泡一杯咖啡和前台小姑娘聊聊天,接受一個天貓快遞,打一盤LOL,然后就大功告成了。別做夢了,至少現在還沒有這個一個按鈕,也不知道以后會不會有。(It doesn't exist. Not at this point,not sure if it ever will.)。你也別指望能夠新建一個空的ASP.NET5的工程,然后將你現在的代碼拖曳進來,然后就完事了。由於一些根本性的變更,一些必要的升級手段是必須的。
盜圖:來自於 Visual Studio 2015 開發 ASP.NET 5 有何變化?
第一步,新建一個工程
新建一個工程,輸入解決方案名稱,以下省略500字。注意,選擇 ”No authentication “,當然如果你願意,也可以保留標准配置。
這個時候,你最好試試看,能不能編譯成功,是不是會有詭異的錯誤,有沒有出來歡迎頁面。
(譯者注:可能Package展開錯誤,或者其他錯誤,按照慣例,重新啟動VS,13.8384%的概率可以修復這個問題。修不好就上博客園,百度,雅虎,天貓,京東看看,反正也修不好,不如先血拼一下)
第二步: 復制現有的文件
是時候復制文件了(Time to do something). 由於前文所訴,兩個版本之間差距蠻大的,所以請按照下面的步驟做。
新建一個Models文件夾,將原工程的Model文件都放在新工程的Models文件夾中。
復制所有的Controller文件到新的文件夾中。
復制所有View到新的View中
Act
ActActor
Actor
別復制其他的View啊。。。
第三步: 復制導航欄
鑒於ASP.NET 5 對 _Layout.chshtml 文件做了重大修改 (客戶端如何或許文件), 請將下面的代碼段放到你的_Layout.cshtml 。(在哪?Shared views下面)
- 修復標題(title),這樣就可以明顯標識出這是你的網站。將你原來頁腳的代碼頁放到 footer 標簽中吧。(譯者注:語義化HTML5)
- 使用新的Tag-Helper系統來新建一個指向Home的鏈接。(譯者注:呵呵,前端工程師看着很開心,和HTML語法好像啊)
<a asp-controller="Home" asp-action="Index" class="navbar-brand">New Project</a>
將 “Home” 改為 “Act”, 將所有 “New Project” 改為 “Random Acts of Kindness”.
接下來修改所有的導航鏈接吧,當然也是使用高大上的Tag-Helper,最后的完成效果應該如下所示。Great。。
這一步大部分人都可以無障礙理解的吧。。。
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a asp-controller="Act" asp-action="Index">Events</a></li>
<li><a asp-controller="Actor" asp-action="Index">Volunteers</a></li>
<li><a asp-controller="Act" asp-action="About">About</a></li>
<li><a asp-controller="Act" asp-action="Contact">Contact</a></li>
</ul>
</div>
第四步: Entity Framework 7 登場
打開 project.json添加下面一個配置項目 (這樣的話Nuget就會開始下載這兩個Package了):
"EntityFramework.Commands": "7.0.0-rc1-final",
"EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final"
別忘了再添加這個 project.json:
"ef": "EntityFramework.Commands"
別問我有什么用,我不懂EF
第五步:Connection String
appsettings.json: 看清楚了,不是project.json!!!
"ConnectionStrings": {
"RandomActsDB" : "Server=.;Database=RandomActs5DB;Trusted_Connection=True;MultipleActiveResultSets=True;"
}
這里認為你用的是高大上的MSSQL,其他數據庫愛好者(MongoDB是最好的數據庫),請酌情修改。
第六步:注冊EF的Dependency Injection
又是我不懂的EF。。。。
下面的這些加在 startup.cs:
using Microsoft.Data.Entity;
using RandomActs.Models;
Add the following code to the Configure Services method.
var cnx = Configuration.Get<string>("ConnectionStrings:RandomActsDB");
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<RAOKContext>(options => options.UseSqlServer(cnx));
第七步: 全局檢索和替換
開始放大招了,全局替換。。。老外膽夠肥啊。。。。
要么我也替換”Replace“ 為 ”替換“ 。。。
Replace "System.Data.Entity" with "Microsoft.Data.Entity"
Replace "System.Web.Mvc" with "Microsoft.AspNet.Mvc"
Replace "using System.Web;" with an empty string (effectively deleting all these using statements)
Replace "HttpStatusCode.BadRequest" with "(int) HttpStatusCode.BadRequest"
第八步:修復 Bind 語法
Bind 特性的語法已經簡化了(別簡化啊。。。).你可以用新的語法來代替 [Bind(Include="item1, item2, item3")], 現在可以這樣用了 [Bind("item1","item2","item3")] . 舉個栗子。。。。
變身前
public ActionResult Edit([Bind(Include="RandomActId,Title")] RandomAct randomact)
變身后
public ActionResult Edit([Bind("RandomActId","Title")] RandomAct randomact)
友情提示:
編譯一下,看到一大推錯誤信息,你就知道那些地方需要修改了。(作者原文啊)
第九步: 修復 SelectList
任何使用SelectList 方法的Controller,添加引用
using Microsoft.AspNet.Mvc.Rendering;
繼續友情提示,看看令人抓狂的ErrorList吧
第十步:修復 Entity Framework Find 方法
在 repository 類中, 替換所有Entity Framework Find 方法為 SingleOrDefault 方法 包括所有lambda expression限制返回件數的地方
舉個栗子:
變身前
context.RandomActors.Find(id);
變身后
context.SingleOrDefault(x => x.RandomActorId == id);
第十一步: 修復路由
startup.cs
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Act}/{action=Index}/{id?}");
}
第十二步: 修復路由 設定Repository類的依賴注入( Dependency Injection )
startup.cs. 添加這些 ConfigureServices 方法去實現依賴注入吧。
services.AddScoped<IRandomActRepository, RandomActRepository>();
services.AddScoped<IRandomActorRepository, RandomActorRepository>();
services.AddScoped<IRandomActActorRepository, RandomActActorRepository>();
每一個控制器有兩個構造器,第一個現在不需要了,應為上面的依賴注入已經代勞了。那就干掉第一個構造器吧,就是帶有this的擴展方法的構造器。
public ActController() : this(new RandomActRepository())
{
}
第十三步:修改Repository 類中對於 DbContext 的引用,來支持Dependency Injection
- 將這些在RAOKContext 類中的構造器干掉,在 RAOKContext.cs (你也可以干掉整個構造器)
: base("RandomActsDB")
-
這樣整個工程現在就不會報錯了,世界就清凈了。如果還有錯誤,好好的看一下還有什么漏網之魚。如果你現在就急不可耐想要運行工程,呵呵,一個運行時所與正在路上。你還木有一個數據庫連接呢。這個錯誤有時候會讓你抓狂( head scratching ),直到你靈光乍現( "ah ha" moment)意識到你的repository類需要修改用來支持injected DbContex的接受者。(modified to receive the injected DbContext.)
-
繼續修改吧,每一個Repository類,去掉這行代碼
RAOKContext context = new RAOKContext();
- 將上面的代碼改成這個樣子
public RAOKContext context { get; private set; }
public RandomActActorRepository(RAOKContext raokcontext)
{
context = raokcontext;
}
上面的是RandomActActorRepository的示范. 構造器的名字不要搞錯啊。
第十四步: 新建數據庫
我起初無意將這步也添加進來的,因為我是通過遷移一個現存的數據庫來取代新建的。
回頭細想,可能你沒有一個數據庫,所以你可能需要一些新建數據庫的命令。
這些可憐的命令你執行一次就可以了。打開VS 命令提示(command prompt ),切換到Project文件夾,輕輕鍵入下面的命令,然后就可以了。
dnx ef migrations add CreateRandomActsDB
dnx ef database update
這樣的話,數據庫的基本雛形就完成了,使用EF的遷移工具,提交更改給SQLServer,然后就么有然后了。
友情提示: ef 將需要第五步的Connection String!!如果發生錯誤,瞅瞅第五步做對了嗎,
第十五步 去掉 jqueryval Script References
一些頁面使用到了不再需要的script bundle,這樣會出現運行時錯誤,將這些代碼從頁面的最底下刪掉就可了。
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
第十六步:修復Lazy Loading
如果你現在屁顛屁顛的開始運行你的代碼,一個”ArgumentNullException: Value cannot be null.“錯誤將會不期而遇。
為什么呢?EF7 RC1還沒有實現 lazy loading!!!!!(我和我的小伙伴都驚呆了)
如果你使用過之前版本的EF,你應該體驗過魔術般的LazyLoading(the magic of lazy loading)隱示的(implicitly )按需求加載需要的數據。但是現在這個版本的EF,它隔屁了。(或許現在已經實現了,EF不懂的路過)
解決方式是,顯示的(explicitly)在需要數據之前,就加載他們。
(本人不懂EF,下面這段,大伙看着辦吧。不誤人子弟了)
The solution is to explicitly enable eager loading whenever related entities will be needed for a given entity. The RandomActs app took advantage of lazy loading for the volunteer dropdown in the ActActorContoller class and for the counts that are displayed on the views of the ActController and the ActorController classes.
從ActController的 Index 方法中將延遲加載換成搶先加載:
變身前
return View(actRepository.All);
變身后
return View(actRepository.AllIncluding(x => x.Actors));
ActorController也如法炮制
變身前
return View(actorRepository.All);
變身后
return View(actorRepository.AllIncluding(x => x.Acts));
ActActorController也是如此. 不過考慮到第十步的SingleOrDefault 問題,請這樣修改
變身前
return context.RandomActs.SingleOrDefault(x => x.RandomActId == id);
變身后
return this.AllIncluding(x => x.Actors).SingleOrDefault(x => x.RandomActId == id);
RandomActActorRepository也如法炮制
變身前
return context.RandomActActors.Where(x => x.RandomActId == actId);
變身后
return context.RandomActActors.Where(x => x.RandomActId == actId).Include(x => x.Actor).Include(x => x.Act);
最后是 ActActorController
變身前
return context.RandomActActors.SingleOrDefault(x => x.RandomActActorId == id);
變身后
return context.RandomActActors.Include(x => x.Actor).Include(x => x.Act).SingleOrDefault(x => x.RandomActActorId == id);
可選
RandomActs現在可以無障礙運行了,不過,如果你想更加有ASP.NET5的范,你還可以更加優化代碼
繼續全局檢索替換 "ActionResult" 為 "IActionResult".
將Html helper 方法改為Tag Helper ,如果你閑的蛋疼。
變身前
@Html.TextBoxFor(model => model.Title, new { style = "width: 400px" })
變身后
<input asp-for="Title" style="width: 400px" />
變身前
@using (Html.BeginForm())
{
}
變身后
<form asp-controller="Act" asp-action="Edit" method="post">
</form>
感謝網友的指正
引用dudu的文章 http://www.cnblogs.com/dudu/p/dotnet-core-framework-mono.html
在.NET Core推出之前,.NET Core是參考.NET Framework重新開發的.NET實現,Mono是.NET Framework的一個開源的、跨平台的實現。
如果拿操作系統打個比方,這時,.NET Framework是Unix,.NET Core是Linux,Mono是Mac OS X。
在.NET Core推出之后,.NET Framework與Mono將基於.NET Core重新構建。.NET Framework將成為.NET Core在Windows上的一個發行版,Mono將成為.NET Core的一個跨平台發行版。
再拿操作系統打個比較,那時,.NET Core是Linux,.NET Framework是Ubuntu,Mono是Red Hat。