---
# System prepended metadata

title: 在VS code環境撰寫.NetCore
tags: [' .NetCore', Programing, ' C#', ' VSCode']

---

---
tags: Programing, C#, .NetCore, VSCode
---
# 在VS code環境撰寫.NetCore
[TOC]

# 環境設定
## 詳細請見[官網教學](https://docs.microsoft.com/zh-tw/dotnet/core/tutorials/with-visual-studio-code)
# 建立.NetCore專案
## 在目標資料夾CLI輸入 

### 建立專案
```bash=
dotnet new console # 當前目錄建立專案

# 或是

dotnet new console [project-name] # 在當前目錄建立資料夾 [project-name]
```

### 執行專案
```bash=
dotnet run # 當前目錄下專案

#或是

dotnet run --project [project-name] # 開啟 [project-name] 此目錄
```

接著在VS Code內安裝C# Extension套件後  
便可以進行Debug模式了  
若要執行Release模式，可以執行  
```bash=
dotnet run --configuration Release
```

Debug跟Release兩個模式的差異在於
---
### Debug模式

    不會對程式進行優化，以及提供break point的方式
    讓使用者可以逐步、修改變數來進行測試

---
### Release模式

    則是會進行程序的最佳化
    使程式能跑得更快、空間更小
    潛在問題則是可能導致Race Condition
---
    
# 發佈應用程式

執行以下指令來產生publish後結果  

```bash=
dotnet publish --configuration Release
```

在專案目錄底下的`bin/Release/netcoreapp3.1`分別有四個重要的檔案  

## [專案名稱].deps.json
    
這個檔案為應用程式runtime的參考套件檔  
如果有應用其他 `dll` 或 `.net core components`  
    
## [專案名稱].dll
    
這個檔案為可執行的動態連結函式庫  
可透過以下指令來執行  
    
```bash=
dotnet [專案名稱].dll
```

## [專案名稱].exe
    
這個檔案為應用程式的可執行檔  
不過某些作業系統不支援 ex. mac  
    
## [專案名稱].pdb
    
這是debug的象徵檔(?)  
當要對publich後的應用程式debug時便會需要用到  

## [專案名稱].runtimeconfig.json

這是應用程式的run-time設定檔  
它會識別目前執行的 .NET Core版本  
我們可以對它增加設定  
詳情參考  
[.NET Core run-time時的設定檔](https://docs.microsoft.com/zh-tw/dotnet/core/run-time-config/#runtimeconfigjson)  

# 執行方法

## 詳情參見[官網教學](https://docs.microsoft.com/zh-tw/dotnet/core/tutorials/publishing-with-visual-studio-code#run-the-published-app)

# 建立程式庫
```bash=
dotnet new sln
```

依序建立 `Library` 、 `Console`  
建立方式為：在專案目錄底下  

## 建立 Library

```bash=
dotnet new classlib -o [LibraryName]
dotnet sln add [LibraryName]/[LibraryName].csproj
```

接著修改 `[LibraryName].csproj` 以下為範例

```C#=
using System;

namespace UtilityLibraries
{
    public static class StringLibrary
    {
        public static bool StartsWithUpper(this string str)
        {
            if (string.IsNullOrWhiteSpace(str))
                return false;

            char ch = str[0];
            return char.IsUpper(ch);
        }
    }
}
```

接著將此函式庫 build 起來

```bash=
dotnet build
```

libary的部分目前便完成了

## 建立 Console

接下來建立Console應用程式

```bash=
dotnet new console -o [consoleAppName]
dotnet sln add [consoleAppName]/[consoleAppName].csproj
```

建立好Template後  
便可以修改Progarm.cs  
下面的程式碼著重在載入函式庫的部分  
用的是 `[LibraryName]/[LibraryName].csproj` 內的 `namespace`  

```C#=
using System;
using UtilityLibraries; // 重點在此

class Program
{
    static void Main(string[] args)
    {
         //Todo
         //在此寫執行程式
    }    
}
```

到此 Console Application的部分也完成了  

## 加入專案參考
接下來要把Console Application所引用到的外部函式庫給加入參考  
指令如下  

```bash=
dotnet add [consoleAppName]/[consoleAppName].csproj reference [LibraryName]/[LibraryName].csproj
```

## 執行應用程式
如標題  
而指令如下=

```bash=
dotnet run --project [consoleAppName]/[consoleAppName].csproj
```
# 對程式庫進行單元測試
## 建立單元測試專案
首先得先在原先專案上新增單元測試專案  
指令如下  
```bash=
dotnet new mstest -o StringLibraryTest
```
執行後便建立起後續需要用的專案了  
接下來這裡解釋一下裡頭的`UnitTest1.cs`  
```C#=
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace StringLibraryTest
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
        }
    }
}
```

## Microsoft.VisualStudio.TestTools.UnitTesting 
提供用來單元測試的型別  

## TestClassAttribute
運用 [TestClass] 去定義 UnitTest1這個Class  

## TestMethodttribute
運用 [TestMethod] 去定義 UnitMethod1這個Class  

## 將測試專案加入專案
將單元測試的專案加入整個專案內  
```bash=
dotnet sln add StringLibraryTest/StringLibraryTest.csproj
```

## 對測試專案加入參考
在測試專案加入呼叫的函式庫的參考
```bash=
dotnet add [UnitTest]/[UnitTest].csproj reference [LibraryName]/[LibraryName].csproj
```

## 加入並執行單元測試的方法
在測試傳案裡透過呼叫Assert內的函式來判斷測試對或錯  
ex. `Assert.AreEqual`、`Assert.AreSame`  ...  
透過這些函式可以產生出測試結果  
以下為範例  

```C#=
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using UtilityLibraries;

namespace StringLibraryTest
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestStartsWithUpper()
        {
            // Tests that we expect to return true.
            string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα", "Москва" };
            foreach (var word in words)
            {
                bool result = word.StartsWithUpper();
                Assert.IsTrue(result,
                       String.Format("Expected for '{0}': true; Actual: {1}",
                                     word, result));
            }
        }

        [TestMethod]
        public void TestDoesNotStartWithUpper()
        {
            // Tests that we expect to return false.
            string[] words = { "alphabet", "zebra", "abc", "αυτοκινητοβιομηχανία", "государство",
                               "1234", ".", ";", " " };
            foreach (var word in words)
            {
                bool result = word.StartsWithUpper();
                Assert.IsFalse(result,
                       String.Format("Expected for '{0}': false; Actual: {1}",
                                     word, result));
            }
        }

        [TestMethod]
        public void DirectCallWithNullOrEmpty()
        {
            // Tests that we expect to return false.
            string[] words = { string.Empty, null };
            foreach (var word in words)
            {
                bool result = StringLibrary.StartsWithUpper(word);
                Assert.IsFalse(result,
                       String.Format("Expected for '{0}': false; Actual: {1}",
                                     word == null ? "<null>" : word, result));
            }
        }
    }
}
```
篇幅雖然很長  
但著重的點在最後Assert的呼叫以及準備資料的設定  
在修改完之後  
執行以下指令來跑單元測試  

```bash=
dotnet test [UnitTestLibrary]/[UnitTestLibrary].csproj
```

以下測試為單元測試結果成功的輸出範例  

```tiddlywiki=
Starting test execution, please wait...

A total of 1 test files matched the specified pattern.

Test Run Successful.
Total tests: 3
     Passed: 3
 Total time: 5.1116 Seconds
```

以下測試為單元測試結果失敗的輸出範例  

```tiddlywiki=
Starting test execution, please wait...

A total of 1 test files matched the specified pattern.
  X TestDoesNotStartWithUpper [283ms]
  Error Message:
   Assert.IsFalse failed. Expected for 'Error': false; Actual: True
  Stack Trace:
     at StringLibraryTest.UnitTest1.TestDoesNotStartWithUpper() in C:\
Projects\ClassLibraryProjects\StringLibraryTest\UnitTest1.cs:line 33

Test Run Failed.
Total tests: 3
     Passed: 2
     Failed: 1
 Total time: 1.7825 Seconds
```
## 測試發行版本程式庫
執行
```bash=
dotnet test [UnitTestLibrary]/[UnitTestLibrary].csproj --configuration Release
```

## 偵錯測試
在VS code的畫面中  
會有`Run All Test`這個選項(在範例的7行跟8行之間)  
如果看不到請重整畫面(Reload Window)  
在VS Code重整頁面預設按鍵為`Ctrl + R`



# 建立Web API 
## 建立 WebAPI 專案
首先先建立Web API專案  
接著在專案內加入需要的NuGet套件  
```bash=
dotnet new webapi -o TodoApi
cd TodoApi
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.InMemory
code -r ../TodoApi
```
這樣便建立了 WebAPI 的雛型了

## 測試API功能
在上面我們建立好的WebAPI可以透過輸入按鍵Ctrl + F5來看效果  
執行之後開啟`https://localhost:5001/WeatherForecast`  
可以看到執行結果為回傳一個Json格式

## 新增模型類型
在這裡模型指的是這個app管理的資料  
以下為範例  
首先 新增資料夾名稱為 `Models`  
接下來新增`TodoItem`.cs 到Models並且撰寫下面的程式碼
```C#=
namespace TodoApi.Models
{
    public class TodoItem
    {
        public long Id {get; set;}
        public string Name{get; set;}
        public bool IsComplete {get; set;}
    }
}
```
雖然模型類別可以到處放  
但習慣上我們會將它放置在Models內以便管理

## 新增資料庫內容
在 `Models` 新增`TodoContext.cs`這個類別  
並且撰寫以下程式碼
```C#=
using Microsoft.EntityFrameworkCore;

namespace TodoApi.Models
{
    public class TodoContext : DbContext
    {
        public TodoContext(DbContextOptions<TodoContext> options)
            : base(options)
        {
        }

        public DbSet<TodoItem> TodoItems { get; set; }
    }
}
```

## 登錄資料庫內容
在ASP.NET Core許多服務必須註冊在dependency injection (DI)  容器上  
像是上面提及的DB context  
容器(Container)提供服務給這些controller們

接下來更新Startup.cs成以下這樣
```C#=
// Unused usings removed
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;

namespace TodoApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<TodoContext>(opt =>
               opt.UseInMemoryDatabase("TodoList"));
            services.AddControllers();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}
```
在複製貼上之前說明一下行為  
首先移除底下未用到的函式庫  
接下來加入DB Context到DI container中  
最後是指定DB Context將使用在in-memory資料庫

## 建置控制器(Controller)
執行以下指令建置控制器
```bash=
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add oackage Microsoft.EntityFrameworkCore.Desion
dotnet tool install --global dotnet-aspnet-codegenerator
dotnet tool update -g dotnet-aspnet-codegenerator
dotnet aspnet-codegenerator controller -name TodoItemsController --async -api -m TodoItem -dc TodoContext -outDir Controllers
```


