文章出處

升級文檔:

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的配置更改,并且去除了tagsprojectUrllicenseUrlnetcoreapp1.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 站點,然后綁定發布目錄就行,應用程序池的模式需要改為“無代碼托管”。


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()