升級文檔:
- 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 站點,然后綁定發布目錄就行,應用程序池的模式需要改為“無代碼托管”。
文章列表