1. 程式人生 > >.NET Core 1.0 RC2 歷險之旅

.NET Core 1.0 RC2 歷險之旅

文章背景:對於.NET Core大家應該並不陌生, 從它被 宣佈 到現在已經有1-2年的時間了,其比較重要的一個版本1.0 RC2 也即將釋出。.Net Core從一個一個的測試版到現在的RC2,經歷了很多個大大小小的變化。特別是在RC1到RC2的更新之中,.NET Core命令列工具(dotnet cli)從 dnx 變為 dotnet,並且廢除了 DNVM 和 DNU,使得 .NET Core 的開發變得更為簡單,其相關工具鏈也基本成型。雖然網上關於.NET Core的示例專案不在少數,而且微軟官方也提供了不少示例專案,但大多針對的是.NET Core的不同版本,因此很多示例專案並不是能很容易的執行起來。所以我決定寫一篇針對RC2這個版本的.NET Core入門文章並提供一些能直接執行的

示例專案

下載安裝 .NET Core SDK

從 dotnet cli github專案主頁找到最新版的.Net Core SDK下載:

例如 Mac OS X的最新版的.NET Core SDK的下載地址為:

https://dotnetcli.blob.core.windows.net/dotnet/beta/Installers/Latest/dotnet-dev-osx-x64.latest.pkg

安裝前請確認當前系統是否已經安裝了老版本的.NET Core, 如果已經安裝,請先解除安裝。

如在Mac OS X上已安裝的話,請執行如下命令刪除:

sudo rm -rf /usr/local/share/dotnet

在Mac OS X上安裝之前請先確保 openssl 已經被安裝了:

brew install openssl

開發工具 Visual Studio Code 及其 C# 外掛安裝

如不準備使用VSCode進行開發的話,請忽略此部分。我不確定最新版本的 Visual Studio 2015 Update 2 是否對.NET Core 1.0 RC2有很好的支援。

  1. 從官方網站下載安裝 VSCode

    https://code.visualstudio.com/

  2. 安裝VSCode C#外掛

    由於支援 RC2 的 C# 外掛 v1.0 還未正式釋出到 VS Code extension 倉庫裡, 因此你只能手動從github下載並安裝:

    https://github.com/OmniSharp/omnisharp-vscode/releases

    Mac OS X 下通過 VSCode 開啟下載下來的檔案即可。

    等到 VS Code C# 外掛 v1.0 版本正式釋出了,你就可以通過VSCode的命令視窗來安裝 C# 支援了。詳細操作如下:

    執行VSCode, 然後使用快捷鍵 ⌘ + P 啟動快速開啟命令視窗,然後輸入如下命令安裝C#擴充套件。最新版的csharp擴充套件已支援 RC2 的.NET程式的除錯。

     ext install csharp

使用.NET CLI (dotnet) 建立,編譯和執行專案

  1. 建立專案

    首先在控制檯/Terminal下進入你要建立專案的目錄,然後執行如下命令:

     dotnet new

    dotnet cli 建立新專案的時候支援專案型別引數-t,但當前只支援Console引數。:(

    執行之後會生成兩個檔案

     project.json    -   類似於.NET Framework裡的專案檔案
     Program.cs      -   程式啟動入口

    使用restore命令下載依賴

     dotnet restore

    如出現網路錯誤導致restore失敗的情況請重試幾次,貌似這種情況比較少。

    如發現類似下面的Warning也請不要驚慌,這是由於CLI的版本號與下載下來的.NET Core類庫的版本號不一致導致的,這種情況不會影響編譯和執行。

     warn : Dependency specified was Microsoft.NETCore.App (>= 1.0.0-rc2-3002464) but ended up with Microsoft.NETCore.App 1.0.0-rc2-3002468.
  2. 編譯專案

    在控制檯或Terminal開啟專案所在目錄,執行如下命令編譯:

     dotnet build

如出現如下類似編譯錯誤:

   error NU1002: The dependency Microsoft.CodeAnalysis.Common 1.2.0-beta1-20160202-02 does not support framework .NETCoreApp,Version=v1.0
   error NU1002: The dependency Microsoft.CodeAnalysis.CSharp 1.2.0-beta1-20160202-02 does not support framework .NETCoreApp,Version=v1.0
   

出現這個問題的可能原因是NuGet上通過版本號匹配到的依賴包並不能使用,你需要做如下操作之後再重新restore一下。

a. 在專案根目錄新增檔案 NuGet.config, 並寫入以下內容:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <packageSources>
            <!--To inherit the global NuGet package sources remove the <clear/>line below -->
            <clear/>
            <add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json"/>
            <add key="api.nuget.org" value="https://api.nuget.org/v3/index.json"/>
        </packageSources>
    </configuration>
    

b. 開啟 project.json 檔案將 dependencies 節點中的 Microsoft.NETCore.App 的版本好更改為 1.0.0-rc2-*:

    "dependencies": {
        "Microsoft.NETCore.App": {
            "type": "platform",
            "version": "1.0.0-rc2-*"
        }
    }
    

c. 然後再重新執行 dotnet restore 之後編譯

  1. 執行專案

    在控制檯或Terminal開啟專案所在目錄,執行如下命令執行:

     dotnet run

    如遇如下錯誤,

     Expected to load libhostpolicy.dylib from [/usr/local/share/dotnet/shared/Microsoft.NETCore.App/1.0.0-rc2-3002468]
     This may be because the targeted framework ["Microsoft.NETCore.App": "1.0.0-rc2-3002468"] was not found.

    也不要慌張,到目錄 bin/Debug/netcoreapp1.0中 找到檔案 *.runtimeconfig.json, 將其中 runtime 版本號修改為與本機 CLI 版本號一致即可。

     {
         "runtimeOptions": {
             "framework": {
                 "name": "Microsoft.NETCore.App",
                 "version": "1.0.0-rc2-3002485"
             }
         }
     }

    如本機 dotnet --version 命令返回值為 “1.0.0-rc2-002485”,則應runtime config中的版本號應替換為“1.0.0-rc2-3002485”。

    然後再嘗試執行 dotnet run

  2. 除錯專案 (Visual Studio Code)

    使用 VSCode 開啟專案所在資料夾之後,VSCode 會問你是否新增啟用專案除錯相關的檔案,你選OK之後目錄下會新增資料夾“.vscode”,其中會包含兩個檔案:

     launch.json
     tasks.json

    當你發現無法除錯失敗的時候,你可以到 launch.json 檔案,檢查啟動所指向的檔案是否正確:

     {
         "name": ".NET Core Launch (console)",
         ...
         "program": "${workspaceRoot}/bin/Debug/netcoreapp1.0/netcore.dll",
         ...
     }

使用 .NET Core 進行 ASP.NET MVC 開發

雖然 dotnet cli 並沒有提供直接建立 web/mvc專案的選項,但是我們還是可以手動來建立 mvc 專案的。

  1. 首先是更新 project.json 來支援 mvc:

     {
         "version": "1.0.0-*",
         "content": [
             "wwwroot",
             "Views"
         ],
         "compilationOptions": {
             "preserveCompilationContext": true,
             "emitEntryPoint": true,
             "debugType": "portable"
         },
         "dependencies": {
             "Microsoft.AspNetCore.Diagnostics": "1.0.0-*",
             "Microsoft.AspNetCore.Mvc": "1.0.0-*",
             "Microsoft.AspNetCore.Mvc.TagHelpers": "1.0.0-*",
             "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-*",
             "Microsoft.AspNetCore.Server.Kestrel": "1.0.0-*",
             "Microsoft.AspNetCore.StaticFiles": "1.0.0-*",
             "Microsoft.Extensions.Logging.Console": "1.0.0-*",
             "Microsoft.NETCore.App": {
                 "type": "platform",
                 "version": "1.0.0-rc2-*"
             }
         },
         "frameworks": {
             "netcoreapp1.0": {
             "imports": [
                 "portable-net45+wp80+win8+wpa81+dnxcore50"
             ]
             }
         },
         "tools": {
             "Microsoft.AspNetCore.Server.IISIntegration.Tools": {
             "version": "1.0.0-*",
             "imports": "portable-net45+wp80+win8+wpa81+dnxcore50"
             }
         },
         "scripts": {
             "postpublish": "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%"
         }
     }
  2. 在NuGet.config 檔案中增加 ASP.NET 的包的下載地址,如此檔案不存在請先新增:

     <?xml version="1.0" encoding="utf-8"?>
     <configuration>
         <packageSources>
             <!--To inherit the global NuGet package sources remove the <clear/> line below -->
             <clear />
             <add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
             <add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
             <add key="AspNetCI" value="https://www.myget.org/F/aspnetcirelease/api/v3/index.json" />
         </packageSources>
     </configuration>
  3. 增加 Startup.cs 檔案 然後在 Program.cs 增加啟動程式碼:

    Startup.cs

     using Microsoft.AspNetCore.Builder;
     using Microsoft.AspNetCore.Hosting;
     using Microsoft.Extensions.DependencyInjection;
     using Microsoft.Extensions.Logging;
    
     namespace HelloMvc
     {
         public class Startup
         {
             public void ConfigureServices(IServiceCollection services)
             {
                 // 註冊MVC相關服務到ASP.NET Core的反轉控制器
                 services.AddMvc();
             }
    
             public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
             {
                 loggerFactory.AddConsole(LogLevel.Debug);
    
                 //啟用靜態檔案支援
                 app.UseStaticFiles();
    
                 if (env.IsDevelopment())
                 {
                     app.UseDeveloperExceptionPage();
                 }
    
                 // 啟用 Mvc 並定義預設的路由
                 app.UseMvc(routes =>
                 {
                     routes.MapRoute(
                         name: "default",
                         template: "{controller=Home}/{action=Index}/{id?}");
                 });
             }
         }
     }

    Program.cs

     using System;
     using System.IO;
     using Microsoft.AspNetCore.Hosting;
    
     namespace HelloMvc
     {
         public class Program
         {
             public static void Main(string[] args)
             {
                 var host = new WebHostBuilder()
                             .UseKestrel()
                             .UseContentRoot(Directory.GetCurrentDirectory())
                             .UseDefaultHostingConfiguration(args)
                             .UseIISIntegration()
                             .UseStartup<Startup>()
                             .Build();
    
                 host.Run();
             }
         }
     }
  4. MVC 其它

    ASP.NET Core中 MVC 具體的開發方法,請參考官方文件 https://docs.asp.net/en/latest/mvc/index.html 來學習使用,我在這裡就不再累述了。
    完整示例可參考我在github上的示例專案:https://github.com/kerryjiang/dotnetcore-samples/tree/master/mvc

    MVC專案也可通過 dotnet run 命令執行,還可以使用VSCode進行除錯。

在 .NET Core 中使用 EntityFramework + Sqlite

.NET Core 中的 EntityFramework 也在 RC2 也有較大的變化。包的名字從 "EntityFramework." 變化為 "Microsoft.EntityFrameworkCore."。
如需使用Sqlite的話,project.json的包依賴應該為:

"dependencies": {
    ...
    ...
    "Microsoft.EntityFrameworkCore": "1.0.0-*",
    "Microsoft.EntityFrameworkCore.Sqlite": "1.0.0-*",
    "Microsoft.NETCore.App": {
        "type": "platform",
        "version": "1.0.0-rc2-*"
    }
}

另外,frameworks 節點對 netcoreapp1.0 也需增加新的imports (portable-net45+win8+wp8+wpa81 和 portable-net45+win8+wp8):

"frameworks": {
    "netcoreapp1.0": {
        "imports": [
            "portable-net45+wp80+win8+wpa81+dnxcore50",
            "portable-net45+win8+wp8+wpa81",
            "portable-net45+win8+wp8"
        ]
    }
}

然後在Startup.cs中註冊EF相關的服務和DbContext:

public void ConfigureServices(IServiceCollection services)
{
    services.AddEntityFramework()
            .AddEntityFrameworkSqlite()
            .AddDbContext<WebsiteDbContext>(
                options => options.UseSqlite("Data Source=./mvcefsample.sqlite")); //設定連結字串
                
    services.AddMvc();
}

再到Configure裡面初始化資料庫或者啟用DbMigration:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        ...
        
        using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
        {
            var db = serviceScope.ServiceProvider.GetService<WebsiteDbContext>();
            
            // do db migrate automatically
            // db.Database.Migrate();
            
            if (db.Database.EnsureCreated())
            {
                for (int i = 0; i < 10; i++)
                {
                    var article = new Article {
                        Title = string.Format("Article {0}",  i + 1),
                        Content = string.Format("Article {0} content blabla blabla",  i + 1),
                        CreatedTime = DateTime.Now,
                        UpdatedTime = DateTime.Now
                    };
                    
                    db.Articles.Add(article);
                }
                db.SaveChanges();
            }
        }
    }

完整示例可參考我在github上的示例專案:https://github.com/kerryjiang/dotnetcore-samples/tree/master/mvc-ef

在 ASP.NET MVC Core 中使用 ASP.NET Identity

  1. ASP.NET 中身份驗證是免不了的事情,首先第一步在 project.json 中新增包依賴:

     "dependencies": {
         ...
         ...
         "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "1.0.0-*",
         ...
         "Microsoft.EntityFrameworkCore": "1.0.0-*",
         "Microsoft.EntityFrameworkCore.Sqlite": "1.0.0-*",
         "Microsoft.NETCore.App": {
             "type": "platform",
             "version": "1.0.0-rc2-*"
         }
     }
  2. 實現帶有 Identity 支援的 DbContext:

     using Microsoft.EntityFrameworkCore;
     using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
    
     namespace MvcIdentitySample
     {
         public class ApplicationUser : IdentityUser
         {
    
         }
    
         public class WebsiteDbContext : IdentityDbContext<ApplicationUser>
         {
             public WebsiteDbContext(DbContextOptions<WebsiteDbContext> options)
                 : base(options)
             {
    
             }
    
             //public DbSet<Article> Articles { get; set; }
         }
     }
  3. 然後在 Startup 中的 ConfigureSerivces 方法中註冊服務:

     public void ConfigureServices(IServiceCollection services)
     {
         // register services about EF
         services.AddEntityFramework()
                 .AddEntityFrameworkSqlite()
                 .AddDbContext<WebsiteDbContext>(
                     options => options.UseSqlite("Data Source=./mvcidentitysample.sqlite"));
    
         // register services about ASP.NET Identity
         services.AddIdentity<ApplicationUser, IdentityRole>()
             .AddEntityFrameworkStores<WebsiteDbContext>()
             .AddDefaultTokenProviders();
    
         services.AddMvc();
     }
  4. 再到 Startup 中的 Configure 方法中啟用 Identity:

     public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
     {
         ...
    
         // the authentication must be configured before mvc
         app.UseIdentity();
    
         // To configure external authentication please see http://go.microsoft.com/fwlink/?LinkID=532715
         /*
         app.UseFacebookAuthentication(new FacebookOptions
         {
             AppId = "901611409868059",
             AppSecret = "4aa3c530297b1dcebc8860334b39668b"
         });
         */
    
         app.UseMvc(routes =>
         {
             routes.MapRoute(
                 name: "default",
                 template: "{controller=Home}/{action=Index}/{id?}");
         });
     }

注意 app.UseIdentity() 這句必須放到啟用 MVC 之前,否則 Identity 無法生效,這個問題折騰了我幾個小時。:(

  1. 由於 ASP.NET MVC 6 和 最新的 ASP.NET Identity 的變化,其使用方法與老版本的模版程式碼略有不同。

    a. 頁面頭部登陸狀態部分頁面(/Views/Shared/_LoginPartial.cshtml),由於 taghelper 的引入和 模版引擎的變化,這個頁面的程式碼看起來會和以前有明顯的不同:

     @using Microsoft.AspNetCore.Identity
     @using MvcIdentitySample
     @using MvcIdentitySample.Models
     @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
     @inject SignInManager<ApplicationUser> SignInManager
     @inject UserManager<ApplicationUser> UserManager
    
     @if (SignInManager.IsSignedIn(User))
     {
         <form asp-controller="Account" asp-action="LogOff" method="post" id="logoutForm" class="navbar-right">
             <ul class="nav navbar-nav navbar-right">
                 <li>
                     <a asp-controller="Manage" asp-action="Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>
                 </li>
                 <li>
                     <button type="submit" class="btn btn-link navbar-btn navbar-link">Log off</button>
                 </li>
             </ul>
         </form>
     }
     else
     {
         <ul class="nav navbar-nav navbar-right">
             <li><a asp-controller="Account" asp-action="Register">Register</a></li>
             <li><a asp-controller="Account" asp-action="Login">Log in</a></li>
         </ul>
     }

    b. 新增 _ViewImports.cshtml (/Views/_ViewImports.cshtml) 的使用避免了重複在多個view裡面增加相同的using和其它定義:

     @using Microsoft.AspNetCore.Identity
     @using MvcIdentitySample
     @using MvcIdentitySample.Models
     @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

    這樣就不用在剛才的_LoginPartial.cshtml頁面裡增加同樣的程式碼了。

總結

大家通過以上介紹和示例應該可以瞭解到,當前使用.NET Core 來進行簡單的應用開發是可行的,dot cli(dotnet) 這個工具已經比上個版本(RC1)的工具簡單方便了很多,而且統一的Web開發和Console開發,CLI工具本身以後應該不會有太大的變化。因為.NET Core 1.0 RC2還在開發測試階段,有的包可能還沒有釋出到NuGet上,因此而造成的找不到合適包的情況屬於常見問題之一,不過只要在NuGet.config中增加了合適的Package Source,這個問題就很好解決了。所以大家如果想要把自己的專案,公司的專案遷移到.NET Core, 現在就可以開始動手了,不用再等到時間不確定的 1.0 release。

另外,本文所展示的示例程式碼限於篇幅與排版,無法做到十分詳細,例如程式碼中所需要的using並未提及,還需要讀者來自行新增(VS裡ALT+SHIFT+F10, VSC裡面好像只能手動點感嘆號?)。

而且此文旨在嘗試.NET Core的可用性,因此並未對如ASP.NET MVC 6和 EF7相關技術做深入探討,如需瞭解請檢視相關技術文件:

最後再提醒大家一次,本文中所涉及的程式碼的完整示例專案已放到 github 上,歡迎大家fork/star/send pr:

https://github.com/kerryjiang/dotnetcore-samples