ASP.NET 5 RC1 升级 ASP.NET Core 1.0 RC2 记录
升级文档:
- Migrating from DNX to .NET Core
- Migrating from ASP.NET 5 RC1 to ASP.NET Core 1.0 RC2
- Migrating your Entity Framework Code from RC1 to RC2
ASP.NET Core 1.0 RC2 发布:解读发布:.NET Core RC2 and .NET Core SDK Preview 1
之前,使用 ASP.NET 5 RC1 开发了一个项目,并且这个项目已经用于生产环境,项目中包含的一些东西:
- “伪 DDD” 框架(https://github.com/yuezhongxin/DDD.Sample)
- ASP.NET 5 Web 和 ASP.NET 5 WebApi 项目
- xUnit 单元测试(包含对 WebApi 的测试)
- EntityFramework 程序包
- AutoMapper 程序包
- 自定义开发的程序包
- Bootstrap 前端框架
- Log 日志记录
- Identity 身份验证
- HttpClient 调用其他 WebApi
现在要升级到 ASP.NET Core 1.0 RC2,官方升级文档只是简单的介绍了下,实际升级的过程中还是遇到了不少的问题,我们平常开发 ASP.NET 应用程序,上面所列出的东西基本都包含了,所以,下面纪录升级的过程,包含一些问题和解决方式,希望可以帮助到大家。
1. 代码更新
升级的首要前提,开发环境需要安装:
然后,我们打开 ASP.NET 5 RC1 应用程序的解决方案:
映入眼帘的是程序包还原失败,并且是一大堆错误:
什么鬼?有点莫名其妙,程序包还原失败的原因是,在原有的程序包源中找不到了,具体就是微软把相关程序包都删掉了,好坑啊😂,我还以为是程序包源变更了,后来我找了当时的好几个程序包源,试了都不行,那是不是所有的程序包都删掉了?其实不是,微软只保留了这种1.0.0-rc1-final
版本的程序包,其他的基本上都删掉了,比如下面的这种版本程序包:
这个项目我基本上用的都是1.0.0-rc2-xxxxx
版本的程序包,比较坑,为什么用这种版本的?主要是当时解决一些特殊问题,就不详细说了,如果大家感兴趣的话,可以查看下相关文章,如果你的项目用的是1.0.0-rc1-final
版本的程序包,那么还原和生成应该是成功的。
当时 ASP.NET Core 1.0 RC2 一发布出来,我开发环境就安装更新了,然后前几天这个项目有一个 bug 需要进行修复,打开之后就是上面这个鸟样,所以,修复 bug 必须要升级到 RC2 版本,这也是我这次升级的主要原因。
上面废话有点多,主要想发泄吐槽下,言归正传,我们在代码更新之前,需要了解下 .NET Platform Standard 的概念,详细可以看我前两天写的一篇文章:理解 .NET Platform Standard,这篇文章就是我在升级的过程中记录的,因为之前对它的不了解,所以后来踩了一些坑,简单来说,就是你想让你的应用程序基于什么平台运行?具体指的是基础类库和运行时,在 ASP.NET Core 1.0 RC2 应用程序中的体现,就是project.json
中的frameworks
配置,如果你的应用程序是基于 Desktop CLR 运行,frameworks
配置为net461
,那么你开发的程序包并不需要对应升级,只是相应的把其他需要升级到 RC2 版本的程序包对应升级下,但如果需要跨平台(基于 CoreCLR/CoreFx),那么所有的程序包都需要进行升级。
首先,我们需要把所有程序集的project.json
配置,由原有的(示例):
{
"version": "1.0.0-*",
"description": "CNBlogs.Ad.Domain.Entities Class Library",
"authors": [ "xishuai" ],
"tags": [ "" ],
"projectUrl": "",
"licenseUrl": "",
"frameworks": {
"net451": { }
},
"dependencies": {
"EntityFramework.Core": "7.0.0-rc2-16432"
}
}
修改为(示例):
{
"version": "1.0.0-*",
"description": "CNBlogs.Ad.Domain.Entities Class Library",
"authors": [ "xishuai" ],
"frameworks": {
"netcoreapp1.0": {
"imports": ["dnxcore50", "portable-net45+win8"]
}
},
"dependencies": {
"Microsoft.EntityFrameworkCore": "1.0.0-rc2-final"
}
}
上面主要是frameworks
的配置更改,并且去除了tags
、projectUrl
和licenseUrl
,netcoreapp1.0
的前身是dnxcore50
,意思是跨平台,为什么需要需要添加imports
配置?主要是为了兼容,详细看上面的那篇文章。
frameworks
的配置修改好之后,下面就是程序包的版本升级了,我大概记录了一些:
Old Version | New Version |
---|---|
"EntityFramework.Core": "7.0.0-rc2-16432" | "Microsoft.EntityFrameworkCore": "1.0.0-rc2-final" |
"EntityFramework.MicrosoftSqlServer": "7.0.0-rc2-16432" | "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0-rc2-final" |
"Microsoft.AspNet.Authentication.Cookies": "1.0.0-rc2-16160" | "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0-rc2-final" |
"Microsoft.AspNet.DataProtection.Extensions": "1.0.0-rc2-15874" | "Microsoft.AspNetCore.DataProtection.Extensions": "1.0.0-rc2-final" |
"Microsoft.AspNet.Diagnostics": "1.0.0-rc2-16303" | "Microsoft.AspNetCore.Diagnostics": "1.0.0-rc2-final" |
"Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc2-15994" | "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-rc2-final" |
"Microsoft.AspNet.Mvc": "6.0.0-rc2-16614" | "Microsoft.AspNetCore.Mvc": "1.0.0-rc2-final" |
"Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-rc2-16614" | "Microsoft.AspNetCore.Mvc.TagHelpers": "1.0.0-rc2-final" |
"Microsoft.AspNet.Server.Kestrel": "1.0.0-rc2-16156" | "Microsoft.AspNetCore.Server.Kestrel": "1.0.0-rc2-final" |
"Microsoft.AspNet.StaticFiles": "1.0.0-rc2-16036" | "Microsoft.AspNetCore.StaticFiles": "1.0.0-rc2-final" |
"Microsoft.Extensions.Configuration.FileProviderExtensions": "1.0.0-rc2-15905" | "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0-rc2-final" |
"Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-15905" | "Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final" |
"Microsoft.Extensions.Logging": "1.0.0-rc2-15907" | "Microsoft.Extensions.Logging": "1.0.0-rc2-final" |
"Microsoft.Extensions.Logging.Console": "1.0.0-rc2-15907" | "Microsoft.Extensions.Logging.Console": "1.0.0-rc2-final" |
"Microsoft.Extensions.Logging.Debug": "1.0.0-rc2-15907" | "Microsoft.Extensions.Logging.Debug": "1.0.0-rc2-final" |
"Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-rc2-16142", | "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-rc2-final" |
还有一些其他的后面再说下,我们可以先更改代码,然后根据 VS 2015 的提示“安装对应程序包”功能,可以很方便的进行升级,如图:
1.1. Serializable 引用找不到
将frameworks
修改为netcoreapp1.0
之后,Serializable
的引用找不到了。
解决方式:添加"System.Runtime.Serialization.Formatters": "4.0.0-rc3-24113-00"
程序包。
程序包源为 https://dotnet.myget.org/F/dotnet-core,而不是 http://nuget.cnitblog.com/nuget/core。
1.2. EntityFramework 代码更改
原先代码:
public class EFDbContext : DbContext, IDbContext
{
public EFDbContext(DbContextOptions options)
: base(options)
{ }
}
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<EFDbContext>(options => options.UseSqlServer(Configuration["data:ConnectionString"]));
}
修改为:
public class EFDbContext : DbContext, IDbContext
{
public EFDbContext(DbContextOptions<EFDbContext> options)
: base(options)
{ }
}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<EFDbContext>(options =>
options.UseSqlServer(Configuration["data:ConnectionString"]));
}
如果程序集中引用了"Microsoft.EntityFrameworkCore": "1.0.0-rc2-final"
程序包,需要将frameworks
修改如下:
"frameworks": {
"netcoreapp1.0": {
"imports": ["dnxcore50", "portable-net45+win8"]
}
}
否则会出现下面的错误:
1.3. xUnit 单元测试更改
Getting started with xUnit.net (.NET Core / ASP.NET Core)
Microsoft.AspNet.TestHost
程序包,在 .NET Core RC2 版本被移除了,所以我们没办法对 WebApi 进行测试了(应该有其他的解决方式,待研究),原有的写法(虽然还是可以引用1.0.0-rc1-final
版本,但和 RC2 已经不兼容了):
namespace CNBlogs.Ad.BaseTests
{
public class BaseWebApiTest
{
protected TestServer _server;
public BaseWebApiTest()
{
_server = TestServer.Create(app =>
{
var env = app.ApplicationServices.GetRequiredService<IHostingEnvironment>();
var appEnv = app.ApplicationServices.GetRequiredService<IApplicationEnvironment>();
var loggerFactory = app.ApplicationServices.GetRequiredService<ILoggerFactory>();
new CNBlogs.Ad.WebApi.Startup(env, appEnv).Configure(app, env, loggerFactory);
}, services =>
{
var connectionString = @"";
services.AddMvc();
services.Configure(connectionString);
});
}
}
}
xUnit 单元测试的相关代码,基本上不需要更改,我们只需要更新下project.json
的配置,原先配置:
{
"version": "1.0.0-*",
"description": "CNBlogs.Ad.BaseTests Class Library",
"authors": [ "xishuai" ],
"tags": [ "" ],
"projectUrl": "",
"licenseUrl": "",
"frameworks": {
"dnx451": { }
},
"dependencies": {
"CNBlogs.Ad.WebApi": "1.0.0-*",
"CNBlogs.Ad.Infrastructure": "1.0.0-*",
"CNBlogs.Ad.Infrastructure.Interfaces": "1.0.0-*",
"EntityFramework.Core": "7.0.0-rc2-16432",
"xunit": "2.1.0",
"xunit.runner.dnx": "2.1.0-rc1-build204",
"Microsoft.AspNet.TestHost": "1.0.0-rc2-16032"
},
"commands": {
"test": "xunit.runner.dnx"
}
}
修改为:
{
"version": "1.0.0-*",
"description": "CNBlogs.Ad.BaseTests Class Library",
"authors": [ "xishuai" ],
"testRunner": "xunit",
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0-rc2-3002702"
}
},
"imports": [ "dnxcore50", "portable-net45+win8" ]
}
},
"dependencies": {
"CNBlogs.Ad.Bootstrapper": "1.0.0-*",
"CNBlogs.Ad.WebApi": "1.0.0-*",
"CNBlogs.Ad.Infrastructure": "1.0.0-*",
"CNBlogs.Ad.Infrastructure.Interfaces": "1.0.0-*",
"Microsoft.EntityFrameworkCore": "1.0.0-rc2-final",
"Microsoft.AspNet.TestHost": "1.0.0-rc1-final",
"xunit": "2.1.0",
"dotnet-test-xunit": "1.0.0-rc2-build10015"
}
}
除了使用 VS 2015 的 Test Explorer 跑单元测试之外,我们还可以使用新的dotnet test
命令。
1.4. Web 和 WebApi 代码更改
增加Program.cs
:
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
并添加web.config
(移除wwwroot
目录下的web.config
):
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
-->
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified"/>
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
</system.webServer>
</configuration>
之前引用的"Serilog.Framework.Logging": "1.0.0-*"
日志组件,现在已被弃用,需要重新引用:
"Serilog": "2.0.0-rc-563",
"Serilog.Extensions.Logging": "1.0.0-rc2-10104",
"Serilog.Sinks.RollingFile": "2.0.0-rc-703"
日志代码配置:
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Error()
.WriteTo.RollingFile(Path.GetFullPath("logs/log-{Date}.txt"))
.CreateLogger();
}
去除了之前的IApplicationEnvironment appEnv
参数,因为现在Startup
已不支持,如果使用的话,会直接抛出异常,WriteTo.RollingFile
是日志写入的方式,现在Serilog
都进行拆分了各种程序包,以Serilog.Sinks.XXXX
的形式,详情查看:https://github.com/serilog
appsettings.json
文件中的Logging: LogLevel
配置更改:
Old Levels | New Levels |
---|---|
Critical | Critical |
Error | Error |
Warning | Warning |
Information | Information |
Verbose | Debug |
Debug | Trace |
移除app.UseIISPlatformHandler();
配置。
_ViewImports.cshtml
文件中的:
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers`
修改为:
@addTagHelper *, Microsoft.AspNet.Mvc.TagHelpers`
并引用"Microsoft.AspNetCore.Mvc.TagHelpers": "1.0.0-rc2-final"
程序包。
CookieAuthentication
配置更新为:
var dataProtection = Microsoft.AspNetCore.DataProtection.DataProtectionProvider.Create(
new DirectoryInfo(@"C:\shared-auth-ticket-keys"), x => x.SetApplicationName("XXXXX"));
var cookieOptions = new CookieAuthenticationOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
CookieHttpOnly = true,
ExpireTimeSpan = TimeSpan.FromMinutes(43200),
CookieName = ".XXXXX",
CookiePath = "/",
DataProtectionProvider = dataProtection
};
app.UseCookieAuthentication(cookieOptions);
完整的Startup.cs
代码:
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using CNBlogs.Ad.Bootstrapper;
using Serilog;
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Serilog.Sinks.RollingFile;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.DataProtection;
using AutoMapper
namespace CNBlogs.Ad.Web
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
Configuration = builder.Build();
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Error()
.WriteTo.RollingFile(Path.GetFullPath("logs/log-{Date}.txt"))
.CreateLogger();
}
public IConfigurationRoot Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddDbContext<EFDbContext>(options =>
options.UseSqlServer(Configuration["data:ConnectionString"]));
services.AddEnyimMemcached();
Mapper.Initialize(cfg =>
{
cfg.CreateMap<CNBlogs.Ad.Domain.Entities.AdText, AdTextDTO>();
});
services.AddTransient<IUnitOfWork, UnitOfWork>();
services.AddScoped<IDbContext, EFDbContext>();
services.AddTransient<IUserService, UserService>();
services.AddTransient<IAdTextRepository, AdTextRepository>();
services.AddAuthentication();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddSerilog();
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
var dataProtection = Microsoft.AspNetCore.DataProtection.DataProtectionProvider.Create(
new DirectoryInfo(@"C:\shared-auth-ticket-keys"), x => x.SetApplicationName("XXXX"));
var cookieOptions = new CookieAuthenticationOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
CookieHttpOnly = true,
ExpireTimeSpan = TimeSpan.FromMinutes(43200),
CookieName = "XXXX",
CookiePath = "/",
DataProtectionProvider = dataProtection
};
app.UseCookieAuthentication(cookieOptions);
app.UseMvc(routes =>
{
routes.MapRoute(
name: "Default",
template: "{controller}/{action}/{id?}");
});
}
}
}
2. 发布程序
首先,ASP.NET Core 1.0 RC2 的发布有两种方式:
- Portable:便携式,发布不包含运行时文件,服务器需要安装 .NET Core。
- Self-Contained:携带式,发布包含运行时文件,服务器不需要安装 .NET Core。
默认是 Portable 的发布方式,和 ASP.NET 5 RC1 所不同的是,ASP.NET Core 1.0 RC2 只能通过dotnet publish
的命令进行发布。
project.json
配置代码:
{
"version": "1.0.0-*",
"buildOptions": {
"emitEntryPoint": true,
"preserveCompilationContext": true
},
"runtimeOptions": {
"gcServer": true
},
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.0-rc2-3002702",
"type": "platform"
}
//....
},
"tools": {
"Microsoft.AspNetCore.Server.IISIntegration.Tools": {
"version": "1.0.0-*",
"imports": "portable-net45+win8+dnxcore50"
}
},
"frameworks": {
"netcoreapp1.0": {
"imports": [ "dnxcore50", "portable-net45+win8" ]
}
},
"publishOptions": {
"include": [
"wwwroot",
"Views",
"appsettings.json",
"web.config"
]
},
"scripts": {
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
}
}
dotnet publish
命令发布:
发布生成目录为:\CNBlogs.Ad.Web\bin\Debug\netcoreapp1.0\publish
,目录结构:
runtimes
目录下有各个平台的native
文件,其作用就是即时编译,publish
目录除了基本的程序集之外,并没有运行时的文件。
根目录下的web.config
配置:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
-->
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\CNBlogs.Ad.Web.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
</system.webServer>
</configuration>
如果是 Self-Contained 方式发布,我们需要在project.json
文件中添加runtimes
配置(参考:Types of portability in .NET Core):
"runtimes": {
"win10-x64": {},
"osx.10.11-x64": {}
}
并去除dependencies
程序包的platform
配置:
"Microsoft.NETCore.App": {
"version": "1.0.0-rc2-3002702",
"type": "platform"//去除
}
dotnet publish
命令重新发布,目录结构:
和上面的 Portable 发布不同的是,发布文件中多了coreclr.dll
等,并且由CNBlogs.Ad.WebApi.dll
变成了CNBlogs.Ad.WebApi.exe
,我们甚至直接可以点击CNBlogs.Ad.WebApi.exe
运行网站,根目录下的web.config
配置:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
-->
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath=".\CNBlogs.Ad.WebApi.exe" arguments="" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
</system.webServer>
</configuration>
如果是 Self-Contained 方式发布,服务器只需要安装 ASP.NET Core Module,相关文档:https://github.com/aspnet/IISIntegration/issues/105
如果是 Portable 方式发布,服务器则需要安装 .NET Core RC2
IIS 和之前的 ASP.NET 5 RC1 发布配置一样,创建 Web 站点,然后绑定发布目录就行,应用程序池的模式需要改为“无代码托管”。
微信公众号:你好架构
出处:http://www.cnblogs.com/xishuai/
公众号会不定时的分享有关架构的方方面面,包含并不局限于:Microservices(微服务)、Service Mesh(服务网格)、DDD/TDD、Spring Cloud、Dubbo、Service Fabric、Linkerd、Envoy、Istio、Conduit、Kubernetes、Docker、MacOS/Linux、Java、.NET Core/ASP.NET Core、Redis、RabbitMQ、MongoDB、GitLab、CI/CD(持续集成/持续部署)、DevOps等等。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。