前言
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。