比起Datebase First,Code First要在Visual Studio這邊做的作業就比較多了
主要原因就是把本來在SQL Server 上建立資料表的動作,移到了程式這邊來做
Code First雖然一切都在VS這邊打好程式碼,盡量不碰到資料庫
但首先我們還是要先把資料庫環境先建立一開頭,不然沒有資料庫環境,也就沒有地方給我們將code的內容回寫建立相關資料庫
那我這邊使用的是SQL Server,我就先建立好了一個Web的資料庫和Web的登入帳號
當然還是有方法不用的,就是你使用的是系統管理員的帳號就可以完全不用進來這邊先設定,但應該是不會這樣做
那首先要使用Code First,需要先安裝以下二個套件:
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tools
到方案總管的相依性>管理NuGet套件
到上圖的導覽搜尋上面幾個套件然後進行安裝
接著開始建立你想要的資料表欄位
我們建立一個Models的資料夾,並在下面新增一個類別,這是對應你資料庫的資料表檔案
假使我們有一個資料表要取名為News,那我們就建立一個News的class
接著在裡面打上對應的欄位名稱
namespace WebAPI.Models;
public class News
{
public Guid NewsId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime StartDateTime { get; set; }
public DateTime EndDateTime { get; set; }
public DateTime InsertDateTime { get; set; }
public int InsertEmployeeId { get; set; }
public DateTime UpdateDateTime { get; set; }
public int UpdateEmployeeId { get; set; }
public int Click { get; set; }
public bool Enable { get; set; }
}
接著在建立一個資料庫物件檔WebContext.cs,那這邊可以依照你的資料庫名稱取名
裡面我們先打上以下內容
using Microsoft.EntityFrameworkCore;
namespace WebAPI.Models;
public class WebContext : DbContext
{
public WebContext(DbContextOptions<WebContext> options) : base(options)
{
}
public DbSet<News> News { get; set; }
}
接著我們再到appsettings.json寫上我們設定好的連線字串
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"WebDatabase": "Server=127.0.0.1;Database=Web;User ID=Web;Password=123456;TrustServerCertificate=true"
}
}
然後我們在Program.cs
中加入資料庫物件的DI注入(這以後會講,這邊先這樣照做)。
builder.Services.AddDbContext<WebContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("WebDatabase")));
其中Configuration.GetConnectionString("WebDatabase")
可以從appsettings.json
中抓出"WebDatabase:"後面的內容,這部分就是說,請把一些參數的東西寫在appsettings.json
裡,然後再用Configuration.GetConnectionString
去抓取,而不要直接把連線字串寫死在程式裡,在日後維護或安全的考量上會較為良好。
接著我們就可以下指令來準備先建立一個有點類似紀錄你資料庫資料表中變化的紀錄檔的東西
Add-Migration InitialCreate
成功執行後,會多出一個紀錄檔案存放的資料夾,跟這次資料表變化的檔案紀錄
接著我們在下一個指令,準備將我們剛剛的News在SQL Server中建立對應的資料表
Update-Database
以上沒錯誤代表成功,之後我們回到我們的資料庫看
就會看到News的資料表被建好了,還有另一個是Code First的紀錄資料表
不過這時候我們點進去看資料表的欄位
雖然欄位名稱都跟我們建的類別檔內容一樣,但似乎哪裡可能還需要做調整
沒錯,資料類型長度可能不見得是我們要的,譬如我這邊不想要長度是MAX,或者我有些欄位不允許Null
那我們就要回頭到WebContext.cs增加一些東西,畢竟你不告訴它你要什麼,它怎麼會知道
所以再加上以下程式碼
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<News>(entity =>
{
entity.Property(e => e.NewsId).HasDefaultValueSql("(newid())");
entity.Property(e => e.Content).IsRequired();
entity.Property(e => e.Enable).HasDefaultValue(true);
entity.Property(e => e.EndDateTime)
.HasDefaultValueSql("(getdate())")
.HasColumnType("datetime");
entity.Property(e => e.InsertDateTime)
.HasDefaultValueSql("(getdate())")
.HasColumnType("datetime");
entity.Property(e => e.StartDateTime)
.HasDefaultValueSql("(getdate())")
.HasColumnType("datetime");
entity.Property(e => e.Title)
.IsRequired()
.HasMaxLength(250);
entity.Property(e => e.UpdateDateTime)
.HasDefaultValueSql("(getdate())")
.HasColumnType("datetime");
});
}
這邊我想要讓主鍵可以自動產生Id所以下了一個sql函式newid()
接著就標題欄位的長度250就好,還有不能為空IsRequired()
等等各欄位想要的配置都寫上去,看的這邊是不是有點不想用Code First了XDDD,用SSMS介面建好像比較簡單
加完之後我們要紀錄一下,告訴它我的表格有做更新
Add-Migration NewsUp
這邊Add-Migration後面的英文式自己取的一個紀錄名稱,那我這邊要更新News所以叫NewsUp
上面雖然有個警示,但只是個小提醒,因為我們有調整了一下欄位結構,所以可能會有資料損失的可能的一個提醒
成功之後就會多一個紀錄檔
接著在執行更新資料庫指令
Update-Database
成功之後我們在回到資料庫看看,應該都會進行修正成我們要的了
以上就是Code First基本的操作,那之前還有兩個資料表沒建這邊就留給大家自行練習了
看起來好像麻煩不少,那主要的原因是因為把本來在資料庫裡設計的工作,都轉成在程式這邊設計了
也就是多了一份資料庫設計的工作要做,但是是用Code來做
那這樣的好處是什麼?
大概就是能不用開啟資料庫環境,直接在vs2022上改完code,資料庫環境也跟著修改
你就不用開著兩邊修改,而我們也只要學會怎麼用code寫資料庫,而不用去學資料庫怎麼操作
其實微軟一直有在各方面做把事情單一化的功能,就是你只要學一個東西,就可以使用到各個不同的地方
但好不好用就見仁見智了XD
最後我們要來測試連線資料庫把資料取出來,但是目前沒有資料可以取,我們也不碰SSMS新增資料怎麼辦
這邊可以在程式中設定預設資料,當某個資料表中如果沒有資料,我們就先新增一些資料進去
這種作法其實很有用處,假設你有關鍵資料是必須存在資料表中程式才能順利啟動,那就可以加上這段,避免以後移機時可能發生的錯誤
這邊我們先新增一個SeedData.cs檔
using Microsoft.EntityFrameworkCore;
namespace WebAPI.Models
{
public class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
using (var context = new WebContext(serviceProvider.GetRequiredService<DbContextOptions<WebContext>>()))
{
if (!context.News.Any())
{
context.News.AddRange(
new News
{
Title = "第一個新聞",
Content = "新聞的內容1",
InsertEmployeeId = 1,
UpdateEmployeeId = 1,
Click = 0,
Enable = true
},
new News
{
Title = "第二個新聞",
Content = "新聞的內容2",
InsertEmployeeId = 1,
UpdateEmployeeId = 1,
Click = 0,
Enable = true
}, new News
{
Title = "第三個新聞",
Content = "新聞的內容3",
InsertEmployeeId = 1,
UpdateEmployeeId = 1,
Click = 0,
Enable = true
});
context.SaveChanges();
}
}
}
}
}
這個檔簡單來說就是先判斷News中有沒有資料,如果沒有就新增三筆進去
之後再到Program.cs中加上以下設定
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
SeedData.Initialize(services);
}
如此一來,一開始便會先偵測News中有沒有資料,並依照SeedData的內容進行新增,當然這個技巧Database First也是可以使用的
接著開始準備取出資料,先建立一個新的NewsController.cs
namespace WebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class NewsController : ControllerBase
{
private readonly WebContext _webContext; //先在全域宣告資料庫物件
public NewsController(WebContext webContext) //這邊是依賴注入使用我們剛設定好的資料庫物件的寫法
{
_webContext = webContext;
}
}
}
如果之前有寫過C#跟資料庫連線但沒用過依賴注入的人,可能會覺得很特殊,以前使用宣告資料庫可能會是這樣寫的
using (var _webContext = new WebContext())
{
}
但這要自己管理好生命週期,且更有新手可能連using都不會用,因而時不時有人就踩坑讓系統爆炸
現在用的這種叫依賴注入的寫法,由程式自動幫你管理好生命週期,你儘管用不需要擔心會採什麼坑
所以既然都來到的.NET Core時代,就改用依賴注入的寫法吧
為什麼我特別這麼說,因為發現還是很多人都轉來.NET Core了,還是放不下以前的寫法,其實這並沒有很難
最後我們在Get這邊簡單寫一下回傳News資料表的資料,如下
[HttpGet]
public IEnumerable<News> Get()
{
return _webContext.News;
}
最後按下啟動不偵錯,看看我們建立的三筆資料有沒有建立上去且成功被撈出來。
上圖資料都撈出來了,就代表我們這次的設定是沒有問題的
不過看到這邊,應該有很多人不想用Code First了,沒關係,選擇你喜歡的開發方式就可以了
那這邊順帶一提,可能有人會跟我剛接觸Code First時有個疑問
就是這是在開發環境可以這樣做,那我在正式環境的時候,難道要用vs去連正式環境的資料庫去做資料庫更新嗎?
當然不是,其實跟你原來的做法一樣就好
就算是用Database First你先在開發環境建好資料庫,到時候到了正式環境,你可能是用匯出SQL,或精靈方式
把開發環境的資料庫建到正式環境,或給DBA相關更新SQL指令,請他更新上去
那在Code First也是一樣的作法,你只是在開發環境用Code First建好開發環境的資料表
但之後你一樣是從SQL Server去匯出相關更新SQL,或精靈匯出,之後再帶到正式環境去更新
差別只差在Database First是你自己在SQL Server上建資料表,而Code First是指令工具幫你建的
最後開發環境上都會有一個跟你程式相符的資料庫內容
然後你再用你原本怎麼移轉到正式環境的方式即可
不需要一定要用Code First的工具建的才能夠運作,資料庫裡的資料表結構都一樣即可