2019年10月1日 星期二

程式設計師應有的程式技能-ASP.NET(C#)資料庫存取-MVC架構技術

第一部分:資料庫資料表建置


[鐵人賽Day22] ASP.Net Core MVC 進化之路 - Entity Framework Core / Code first


與過去Entity Framework 6相比,
Entity Framework Core(簡稱EF Core)在使用上做了相當的改變,
本文將介紹EF Core的使用方式。

ORM 

如果有接觸過EF6的讀者對ORM這個名詞應該都不陌生。
ORM是什麼呢?
ORM全名為Object Relationship Mapping(物件關聯對應),
主要可以幫助我們用「寫程式」的方式去描述資料庫的結構,
並幫我們實現資料庫的新刪修查的功能,
你可以將它想像成一個幫你跟資料庫溝通的代理人
好處是它可以幫我們將資料自動轉為強型別的物件。

而在.Net中實現ORM的工具有許多種,
Entity Framework系列是微軟官方推出的ORM工具,
它可以使用LINQ的描述方式幫助我們操作資料。
但假如貴公司還是習慣使用純粹的SQL語法操作,
可以考慮使用Dapper這套工具。

Entity Framework Core

EF Core.Net中幫助我們與DB溝通的ORM工具,
跟前一代的EF6相比最大的差別是輕量化與跨平台。
在過去EF6的時候edmx速度相對較慢,
但也真的非常強大XD。
EF Core將過去的Model First拿掉了,
目前的建制方式剩下以下兩種:
  • Code First(純手工的)
  • Code First with Database(從DB來的Code First)

以下將示範使用Code First的建立方式。
首先先建立好一個ASP.Net Core MVC的範本專案,
接著使用Nuget安裝Microsoft.EntityFrameworkCore 。


下面這兩張表描述著Person(人)與Education(學歷)的一對多關係,
我們使用Code First來建立。


DbContext 是EF Core中幫我們與資料庫溝通的類別,
所以我們先建立一個CustomDbContext 並繼承DbContext ,
並使用DbSet<TEntity> 來裝載描述資料表的類別。
public class CustomDbContext : DbContext
{
    public CustomDbContext(DbContextOptions<CustomDbContext> options) : base(options)
    {
    }

    public DbSet<Person> Person { get; set; }
    public DbSet<Education> Education { get; set; }
}

接著先來建立Person及Education類別。
Person.cs
public class Person
{
    [Required]
    public int Id { get; set; }

    [Required]
    [StringLength(20)]
    public string Name { get; set; }
        
    public int? Age { get; set; }
}

Education.cs
public class Education
{
    [Required]
    public int Id { get; set; }

    [StringLength(20)]
    public string SchoolName { get; set; }

    [Required]
    public int GraduatedYeay { get; set; }

    [Required]
    [ForeignKey("Person")]
    public int PersonId { get; set; }
        
    public virtual Person Person { get; set; }
}
 在資料欄位中凡命名包含ID者將會自動被視為主鍵,
你也可以透過 [Key] 自行設定。

[Required] 可設定必要的欄位(不允許Null),
但結構型別(int, double, float, datetime)不得為空值(只會是預設值),
所以使用 int? 方式設定。
 [StringLength()] 則代表字串長度。

好了之後在專案中建立一個dbsetting.json,
裡面會放置連接資料庫需用到的ConnectionStrings
{
  "ConnectionStrings": {
    "SampleDBConnection": "Server=(LocalDB)\\MSSQLLocalDB;Database=SampleDB;"
  }
}

接著修改Program中的組態設定,
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((webHostBuilder, configurationBinder) =>
        {
            configurationBinder.AddJsonFile("dbsetting.json", optional: true);
        })
        .UseStartup<Startup>();

接著在StartupConfigureServices方法加入連接字串設定。
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<CustomDbContext>(options =>
    {
        options.UseSqlServer(Configuration.GetConnectionString("SampleDBConnection"));
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

最後在下面Configure方法中設定每次執行前檢查資料庫是否被建立。
public void Configure(IApplicationBuilder app, CustomDbContext customDbContext)
{
    customDbContext.Database.EnsureCreated();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

你也可以透過Add-Migration指令的方式進行建立,
有興趣的讀者可參考這篇

最後請將程式執行起來後,
使用資料庫連接工具(如SSMS)進行連接,
查看我們剛才建立的資料表。
Person

Education


Code First的內容就先介紹到這邊,
下篇會接著介紹EF CoreCode First From Database的使用方式
若內容有誤在麻煩指正。

參考

【資料引用資訊】
google關鍵字:[鐵人賽Day22] ASP.Net Core MVC 進化之路 - Entity Framework Core / Code first

第二部分:更動資料庫資料表


[C#][MVC5]使用Code First 擴充Identity Table


打從接觸MVC也有快兩年的時光了
但由於公司內部採用既有驗證機制(SSO)
所以對Identity一直沒有什麼接觸
最近碰到趕緊來紀錄一下


首先我們先來建一個新的專案IdentityExtensionTableDemo


按下確定之後
點選變更驗證 -> 個別使用者帳戶


專案範本會自動幫你加入Identity Owin的相關程式碼
後面大家應該就不陌生了
瘋狂下一步就好
開起來之後點選Models -> IdentityModels.cs


IdentityModels.cs


首先我們要從ApplicationUser動手
假設要新增一個Education的Table
所以我們先來新增Education.cs並加入一些基本的屬性


掛上Attribute指定資料型態長度
[Required]會影響到該欄位是否允許NULL


最後要來建關聯了
因為ApplicationUser -> Education是一對多的關係
所以需要在Education裡面記住ApplicationUser的ID
​​
之後還沒結束
記得回到ApplicationUser.cs中宣告一個ICollection<Education>的屬性


好了之後就可以來build我們的schema了
使用Code First務必記得最常用的三個指令

- Enable-Migrations : 會幫你自動在專案下建立一個Migration的資料夾,爾後所有Migration的紀錄都會擺在這
- Add-Migration 當Schema有異動的時候,會幫你建一個Migration(有點類似)
- Update-Database : 在DefaultConnection指定的目標建置資料庫(預設在/App_Data下)

好了之後請先打開套件管理器主控台(Package Manager Console, PMC)


輸入Enable-Migrations


可以看到專案底下多了一個Migrations的資料夾


接著輸入Add-Migration [名稱]


好了之後會自動打開一個.cs檔(放在/Migrations資料夾底下)
裡面會是這次Migration所要調整的東西
因為是第一次建所以內容會比較多一點


最後輸入Update-Database


好了之後回到方案總管
記得要顯示所有檔案(預設會隱藏)
然後點右鍵加入至專案


點兩下後會跳到伺服器總管的畫面


再點兩下快速檢視一下


如果還不確定到底關聯有沒有拉成功
可以用Entity Framework確認一下



這樣就大功告成了
不過眼尖的朋友可能會發現
它幫我產的Education自動加了字尾s
如果想要移掉的話可以在ApplicationDbContext.cs動手腳


最後的程式碼放在GitHub
【資料引用資訊】
google關鍵字:[C#][MVC5]使用Code First 擴充Identity Table

2019年8月12日 星期一

程式設計師應有的程式技能-ASP.NET(C#)語法與API資料介接-後端技術

18. (番外篇) 五分鐘實作 C# 介接 OpenData(政府行政機關辦公日曆表)

前言

今天主要講的是快速介接政府資料開放平臺上的政府行政機關辦公日曆表
緣由是最近實作一個小型的工作行事曆
主要是讓客戶可以送件時,可以計算交件期限
邏輯蠻簡單的,從送件日根據案件等級往後推 N 個工作天即是
所以我們需要幾個資訊
  1. 送件日期
  2. 案件等級
  3. 判別工作日與例假日
工作日與例假日的部分,希望每年都可以匯入人事行政處公布的辦公日曆表
之後若遇颱風或其他不可抗拒因素彈性放假時,也要有地方可以手動調整
那我們就開始先接資料吧

先找到資料來源

  1. 先到 政府行政機關辦公日曆表 點選 JSON 檢視資料
    如圖
  2. 複製 資料網址
  3. 將網址貼到 Chrome 上就可以看到要接收的資料了
    data

發送 Http request & 接收 response

  1. 我們要先建立一個 WebRequest
var url = "http://data.ntpc.gov.tw/api/v1/rest/datastore/382000000A-000077-002";
var request = WebRequest.Create(url);
// 透過 Chrome 開發者工具可以取得 Method, ContentType
request.Method = "GET";
request.ContentType = "application/json;charset=UTF-8";
  1. 取得 request 的 response stream
var response = request.GetResponse() as HttpWebResponse;
var responseStream = response.GetResponseStream();
var reader = new StreamReader(responseStrea  Encoding.GetEncoding("utf-8"));
var srcString = reader.ReadToEnd();
  1. srcString 就是我們要的資料了

建立 JSON 格式對應的 class

感謝 Visual studio 提供強大的貼上 JSON 做為類別 功能
  1. 先複製在 Chrome 貼上資料來源後,畫面中的 JSON 資料
    copy
  2. 建立一個 class
    create class
  3. 編輯(E) => 選擇性貼上(S) => 貼上 JSON 做為類別(J)
    paste

將剛剛的 response string 轉為 class 吧

我利用 Newtonsoft 提供的轉型方法
var jsonData = Newtonsoft.Json.JsonConvert.DeserializeObject<HolidayOpenData>(srcString);
整段 code 看起來就會是
static void Main(string[] args)
{
    var url = "http://data.ntpc.gov.tw/api/v1/rest/datastore/382000000A-000077-002";
    var request = WebRequest.Create(url);

    var response = request.GetResponse() as HttpWebResponse;
    var responseStream = response.GetResponseStream();
    var reader = new StreamReader(responseStream, Encoding.GetEncoding("utf-8"));
    var srcString = reader.ReadToEnd();
    var jsonData = Newtonsoft.Json.JsonConvert
        .DeserializeObject<HolidayOpenData>(srcString);
    foreach (var holiday in jsonData.result.records)
    {
        Console.WriteLine($"Date: {holiday.date}, IsHoliday: {holiday.isHoliday}, Category: {holiday.holidayCategory}");
    }
    Console.ReadKey();
}
運行結果如圖
result

結論

目前用簡單的方式先接資料
看之後要開 thread 或是採用 async await 方式都可以
完整的程式碼在 Github 上 https://github.com/ChiYunJai/Day18_OpenData
目前有個小缺點是她日期為 string
看之後轉型成 DateTime 會比較方便處理
接著開一張資料表紀錄某年某月某天是否為工作日即可
匯入步驟也很簡單
  1. 先將 openData 取得所有 IsHoliday = "是" 的日期匯入
  2. 剩下的都是為工作日匯入
  3. 提供介面讓使用者可以根據情況調整某天為工作日或例假日即可
最後在算交件期限時,用送件日期當 query 條件去找即可!
【資料引用資訊】
google關鍵字:18. (番外篇) 五分鐘實作 C# 介接 OpenData(政府行政機關辦公日曆表)