# C#Database1Entities and DataTable 兩者都是 C# 處理資料庫的方式,但本質與用途完全不同 | 名稱 | 類型 | 用途 | |---------------------|------------------------------|-----------------------------------------| | Database1Entities | EF (Entity Framework) 的 DbContext | 透過物件關聯對應 (ORM) 來存取資料庫 | | DataTable | ADO. NET 的資料表 | 以表格的方式存放資料,類似 Excel 的表格 | [TOC] ## Database1Entities 用來管理 資料庫 的存取 💡 主要用來跟 SQL 資料庫 進行互動,並將資料映射成 C# 物件。 書寫的部分可以搭配[C#LINQ(微SQL)](/a6zrFWuFRs6g4ZJ8xn8vTw)來做參考 ### 安裝 Entity Framework 打開 「NuGet 套件管理器」,在 Visual Studio 中,安裝 Entity Framework: 1. 工具 (Tools) → NuGet 套件管理員 (NuGet Package Manager) → 管理解決方案的 NuGet 套件 1. 搜尋 EntityFramework,點擊安裝 ![image](https://hackmd.io/_uploads/r1GfxyghJl.png=50%) ### 💠實體化(ADO)與class 💡💡💡如果使用 ADO.NET,你需要執行 transaction.Commit();來進行儲存資料 (怪怪的...ADO如何在下次點擊開始時還保留資料?) table的寫法只需要與實體化的寫法一樣,get,set ```csharp= public partial class Table { public string date { get; set; } public string who { get; set; } public string money { get; set; } public string pay { get; set; } } ---------------------------------------------- 程式寫法: db.Table.RemoveRange(db.Table); // 清空 Table 表 db.Table.AddRange(importData); db.SaveChanges(); // 儲存變更 ``` ```csharp= using System.Data.Entity; using System.ComponentModel.DataAnnotations; public class Database1Entities : DbContext { public DbSet<User> Users { get; set; } //資料表名稱不一定要 User } public partial class User { [Key] //標註主鍵 public string 入住日期 { get; set; } public string 退房日期 { get; set; } public string 人數 { get; set; } public string 支付方式 { get; set; } public string 房型 { get; set; } } ``` ### 💠名稱問題 💡資料表名稱是否一定要 User? ✔️不一定! User 類別 只是 C# 內的類別名稱 預設情況下,會將類別名稱轉為 Users 作為資料表名稱(因為 DbSet < User > 會自動轉為 Users)。 💠但如果你的資料庫名稱不是 Users,可以這樣設定: ```csharp= [Table("你的資料表名稱")] public partial class User ``` 例: ```csharp= [Table("顧客資料")] public partial class User ``` 這樣就會對應到 顧客資料 這張表,而不是 Users。 --- ### 💠不建議使用中文 你的 User 類別屬性名稱是 入住日期、退房日期 等中文,雖然 C# 支援中文變數,但不建議這樣做,因為: * 可能會導致某些程式碼難以維護。 * 可能會有 資料庫映射問題。 * 在 LINQ 查詢 時可能會有編譯問題。 ✅ 修正方式(使用英文命名並設定映射): ```csharp= [Table("顧客資料")] public partial class User { [Key] [Column("入住日期")] public string CheckInDate { get; set; } [Column("退房日期")] public string CheckOutDate { get; set; } [Column("人數")] public string NumberOfPeople { get; set; } [Column("支付方式")] public string PaymentMethod { get; set; } [Column("房型")] public string RoomType { get; set; } } ``` 這樣: 程式碼保持英文可讀性。 [Column("原本資料庫欄位名稱")] 讓 EF 對應到原本的資料庫欄位,不用改資料庫名稱。 ### 💠DbContext 連結字串(? DbContext 應該要有建構函式 你的 Database1Entities 繼承了 DbContext,但沒有設定 資料庫連線字串,應該這樣改: ```csharp= public class Database1Entities : DbContext { public Database1Entities() : base("name=你的連線字串名稱") { } public DbSet<Reservation> Reservations { get; set; } } ``` 這樣才會連接到 App.config / Web.config 的資料庫設定。 ### 基本結構 ```csharp= using (Database1Entities db = new Database1Entities()) //// 創建資料庫連線 { var users = db.Users.ToList(); // 取得 Users 資料表的所有資料 foreach (var user in users) { Console.WriteLine(user.Name); } }//程式結束時會釋放資料庫連線,避免連線資源占用 ``` * db.Users 是 Users 資料表的 ORM 物件,透過 .ToList() 抓取所有資料。 * EF 會自動轉換 SQL 查詢,讓我們直接操作 C# 物件,而不用寫 SQL 語法。 ### 查詢 LINQ 🔹取得所有資料 ```csharp= var data = db.Table1.ToList(); ``` 🔹 取得符合條件的資料 ```csharp= var result = db.Table1.Where(x => x.Name == "John").ToList(); ``` 🔹 取得單筆資料 FirstOrDefault() 只會回傳 第一筆符合條件的資料,如果沒有資料,會回傳 null。 ```csharp= var person = db.Table1.FirstOrDefault(x => x.Id == 1); if (person != null) { Console.WriteLine($"找到使用者:{person.Name}"); } ``` ### 查詢2 Find 根據主鍵(Primary Key)查找資料。 ```csharp= var user = db.Users.Find(1); ``` 📌 這會在 Users 資料表中,尋找 Id = 1 的使用者 📌 Find 只適用於主鍵查找,不能查其他欄位 如果資料庫內 Id = 1 存在,***則 user 會是一個 User 物件***; 如果沒有找到,則 user 會是 null。 💠如果 Users 表有複合主鍵 ```csharp= var order = db.Orders.Find(1, "A123"); ``` 📌 這裡 Find(1, "A123") 會依序對應到 Id 和 OrderCode 兩個主鍵 💠非主鍵查詢 ```csharp= var user = db.Users.FirstOrDefault(u => u.Email == "test@example.com"); ``` ### 查詢3 似LIKE 1️⃣ 使用 Contains、StartsWith、EndsWith 在 EF LINQ 查詢中,LIKE 對應的方法有: * Contains() 等同於 LIKE '%值%' * StartsWith() 等同於 LIKE '值%' * EndsWith() 等同於 LIKE '%值' 📌 範例:查詢名稱包含 "John" 的用戶 ```csharp= using (var db = new DatabaseContext()) { var users = db.Users.Where(u => u.Name.Contains("John")).ToList(); } ``` 📌 查詢以 "J" 開頭的用戶 ```csharp= var users = db.Users.Where(u => u.Name.StartsWith("J")).ToList(); ``` 📌 查詢以 "son" 結尾的用戶 ```csharp= var users = db.Users.Where(u => u.Name.EndsWith("son")).ToList(); ``` --- 原生態LIKE使用: ```csharp= var users = db.Users.Where(u => EF.Functions.Like(u.Name, "%John%")).ToList(); ``` ### 檢查是否存在 * Any 有時我們只需要確認資料是否存在,而不需要真的查詢: ```csharp= // 檢查是否有名稱為 Alice 的使用者 bool exists = db.Users.Any(x => x.Name == "Alice"); ``` * Find 使用是否為null來判斷 ```csharp= var user = db.Users.Find(1); if (user != null){} ``` ### 使用 Where 篩選 ```csharp= var users = db.Users.Where(u => u.Age > 20).ToList(); ``` 📌 Where 讓你篩選符合條件的資料。 ### 取得所有資料 ```csharp= var users = db.Users.ToList(); // 取得 Users 資料表的所有資料 foreach (var user in users) { Console.WriteLine($"ID: {user.Id}, Name: {user.UserName}"); } ``` ToList() 會把 Users 表內所有資料讀取到 List 裡。 ```csharp= foreach (var item in db.Users) { listBox1.Items.Add($"{item.入住日期}-{item.退房日期}-{item.人數} 付現:{item.支付方式} {item.房型}"); } ``` ### 計算筆數 ```csharp= db.Users.Count() ``` ### 新增 ```csharp= using (Database1Entities db = new Database1Entities()) { Table1 newPerson = new Table1() { Id = 3, Name = "Alice", Age = 22 }; db.Table1.Add(newPerson); // 加入資料 db.SaveChanges(); // 儲存變更到資料庫 } ``` ```csharp= using (var db = new Database1Entities()) { var user = new User() { 入住日期 = dateTimePicker1.Value.ToString("yyyy年MM月dd日"), 退房日期 = dateTimePicker2.Value.ToString("yyyy年MM月dd日"), 人數 = comboBox1.Text, 支付方式 = radioButton1.Checked ? "是" : "否", 房型 = comboBox2.Text }; db.Users.Add(user); db.SaveChanges(); } ``` ### 修改 ```csharp= using (Database1Entities db = new Database1Entities()) { var person = db.Table1.FirstOrDefault(x => x.Id == 3); if (person != null) { person.Name = "Alice Updated"; // 修改名稱 db.SaveChanges(); // 儲存變更 } } ``` ### 刪除 ```csharp= using (Database1Entities db = new Database1Entities()) { var person = db.Table1.FirstOrDefault(x => x.Id == 3); if (person != null) { db.Table1.Remove(person); // 刪除資料 db.SaveChanges(); // 儲存變更 } } ``` ### 清空 ```csharp= db.Table.RemoveRange(db.Table); // 清空 Table 表 ``` db.Tables.RemoveRange 是 Entity Framework(EF) 中用來批量刪除資料庫資料的方法。 ### OrderBy ---- ### 升序、降序 OrderBy,OrderByDescending * 升序 ```csharp= var result = database1Entities.table.OrderBy(x => x.Id).ToList(); ``` * 降序 ```csharp= var result = database1Entities.table.OrderByDescending(x => x.Age).ToList(); ``` ### 取出排序後的前 5 筆資料 這樣可以取出年齡最小的前 5 筆資料。 ```csharp= var top5 = database1Entities.table.OrderBy(x => x.Age).Take(5).ToList(); ``` ### 取出 Age 大於 25 並排序 這樣可以 先篩選出 Age > 25 的資料,再按照 Name 排序。 ```csharp= var result = database1Entities.table .Where(x => x.Age > 25) .OrderBy(x => x.Name) .ToList(); ``` | 方法 | 作用 | |--------------------------------|-----------------------| | OrderBy(x => x.欄位) | 升序排序 (小 → 大) | | OrderByDescending(x => x.欄位) | 降序排序 (大 → 小) | | ThenBy(x => x.欄位) | 第二個條件升序排序 | | ThenByDescending(x => x.欄位) | 第二個條件降序排序 | | Take(n) | 取前 n 筆資料 | | Skip(n) | 跳過前 n 筆資料 | ---- ### 分頁Take(n) 和 Skip(n) 分頁是用來 限制查詢結果的數量,通常搭配 Take(n) 和 Skip(n) 使用。 ```csharp= int pageSize = 10; // 每頁顯示 10 筆 int pageNumber = 2; // 取得第 2 頁的資料 var pagedUsers = db.Users .OrderBy(x => x.Id) .Skip((pageNumber - 1) * pageSize) .Take(pageSize) .ToList(); ``` 這樣就會 從第 11 筆開始,取 10 筆資料。 * Skip(n):跳過前 n 筆資料。 * Take(m):從目前查詢的結果中,取出最多 m 筆資料。 ### 最大值、最小值 ```csharp= var maxAge = db.Users.Max(x => x.Age); // 取得最大年齡 var minAge = db.Users.Min(x => x.Age); // 取得最小年齡 var avgAge = db.Users.Average(x => x.Age); // 取得年齡平均值 ``` ### 執行 SQL ```csharp= db.Users.FromSqlRaw("SELECT * FROM Users").ToList() ``` ### 💡使用 Code First Migrations [C# Migration 管理資料庫結構變更](/e7FdOBEkQwevbmEGSHBIxw)這裡有一些相關介紹 這是最好的方式,可以讓你更新資料庫結構而不會刪除舊資料。 1️⃣ 打開 Visual Studio 的「套件管理員主控台」(Package Manager Console) (工具 -> NuGet 套件管理員 -> 套件管理員主控台) 2️⃣ 啟用 Migrations 在 套件管理員主控台 輸入: ```pgsql= Enable-Migrations ``` ⚡ 這會在專案中建立 Migrations 資料夾,並產生 Configuration.cs 3️⃣ 產生遷移(同步 Model 和 Database) ```sql= Add-Migration InitialCreate ------------------------------- Add-Migration 名稱 ``` 🚀 這會建立一個 InitialCreate 檔案,記錄變更 4️⃣ 更新資料庫 ```pgsql= Update-Database ``` 📌 這步驟會將 C# Model 更新到資料庫,不會刪除舊資料。 ### TransactionScope 確認儲存 ```csharp= using (var scope = new TransactionScope()) { using (var db = new Database1Entities()) { var user = new User() { 入住日期 = dateTimePicker1.Value.ToString("yyyy年MM月dd日"), 退房日期 = dateTimePicker2.Value.ToString("yyyy年MM月dd日"), 人數 = comboBox1.Text, 支付方式 = radioButton1.Checked ? "是" : "否", 房型 = comboBox2.Text }; db.Users.Add(user); db.SaveChanges(); } scope.Complete(); // 只有所有操作成功,才會真正儲存 } ``` ### 交易處理 Transaction 如果你有多個資料庫操作,需要確保 所有步驟都成功才提交變更,可以使用 Transaction。 ```csharp= using (var transaction = db.Database.BeginTransaction()) { try { var user = new User { Name = "Bob", Age = 20 }; db.Users.Add(user); db.SaveChanges(); var user2 = db.Users.FirstOrDefault(x => x.Id == 2); if (user2 != null) { user2.Name = "Updated Name"; db.SaveChanges(); } transaction.Commit(); // 成功後提交 } catch (Exception) { transaction.Rollback(); // 發生錯誤時回滾 } } ``` ### 關聯查詢 (Join) 假設 Users 和 Orders 之間有關聯 (UserId),可以用 Join 來查詢: ```csharp= var userOrders = db.Users .Join(db.Orders, user => user.Id, order => order.UserId, (user, order) => new { UserName = user.Name, OrderAmount = order.Amount }) .ToList(); ``` ## DataTable ADO. NET 提供的一種 記憶體內的資料表格,用來儲存查詢結果,類似 Excel 的表格 💡 DataTable 主要用於 離線存取資料,不依賴 ORM。 ```csharp= DataTable dt = new DataTable(); dt.Columns.Add("ID", typeof(int)); // 新增欄位 ID dt.Columns.Add("Name", typeof(string)); // 新增欄位 Name // 新增一筆資料 dt.Rows.Add(1, "Alice"); dt.Rows.Add(2, "Bob"); // 讀取 DataTable foreach (DataRow row in dt.Rows) { Console.WriteLine($"{row["ID"]}: {row["Name"]}"); } ``` * dt.Columns.Add() → 新增欄位 * dt.Rows.Add() → 新增資料 * dt.Rows → 讀取資料 ## 差異總結 | 特性 | Database1Entities (Entity Framework) | DataTable (ADO. NET) | |--------------------|--------------------------------------|----------------------| | 性質 | ORM (物件關聯對應) | 表格型資料結構 | | 存取方式 | 使用 Linq 查詢 (db.Table1.Where(x => x.Id == 1)) | 使用 迴圈搜尋 (DataTable.Select()) | | 綁定 DataGridView | dataGridView.DataSource = db.Table1.ToList(); | dataGridView.DataSource = dt; | | SQL 操作 | 自動產生 SQL 查詢 (Linq) | 需要手動寫 SQL | | 離線存取 | ❌ 需要連線資料庫 | ✅ 可以存放在記憶體,不需連線 | | 使用場景 | 大多數企業級應用程式,適合大規模資料存取 | 小型應用程式,或是當作暫存表 | * 如果你使用 Entity Framework (ORM),就用 Database1Entities * 如果只是暫存資料、需要離線存取,可以使用 DataTable * 如果要操作大量 SQL、效能需求高,可以使用 ADO. NET (DataTable + SqlCommand) ## 使用時機 #### 直接載入 DataGridView: ```csharp= using (Database1Entities db = new Database1Entities()) { dataGridView1.DataSource = db.Table1.ToList(); // 直接載入資料 } ``` 📌 優點: * 程式碼簡單,直接從資料庫讀取並顯示。 📌 缺點: * 無法輕鬆修改資料(例如,你要調整 DataGridView 的資料格式)。 * 資料無法脫離資料庫使用(需要 Database1Entities 存取)。 * 不能使用篩選、排序,如果 Table1 資料很多,載入時會影響效能。 #### 透過 DataTable 載入 如果你使用 DataTable,你可以先讀取資料,然後再顯示在 DataGridView 📌 優點: 1. 可以脫離資料庫操作(你可以用 DataTable 存資料,不必連線資料庫)。 1. 可修改資料後再寫回資料庫(你可以先修改 DataTable,確認後再存)。 1. 提供更多彈性: * 你可以調整欄位名稱。 * 只選取部分欄位,而不會一次抓全部資料。 * 加強效能,避免每次都直接查詢資料庫。 📌 缺點: * 需要額外的記憶體來存 DataTable。 * 程式碼較長。 | 使用情境 | 選擇方式 | | ---------------------------------- | ---------------------------------------- | | 只需要顯示資料,不修改、不篩選 | 直接綁定 DataGridView (db.Table1.ToList()) | | 需要在 UI 進行篩選、排序或修改 | 使用 DataTable | | 資料來源不只是資料庫(例如 API、CSV)| 使用 DataTable | | 想避免頻繁查詢資料庫 | 使用 DataTable | 如果只是顯示資料,直接綁定 DataGridView 是最簡單的方法。但如果你想 修改資料、篩選、排序,或避免頻繁存取資料庫,使用 DataTable 會更好! ## Database1Entities with DataTable Entity Framework 會回傳 List<T> 或 IQueryable<T>,但 DataGridView 需要的是 DataTable,所以我們要轉換。 ```csharp= using (Database1Entities db = new Database1Entities()) // 建立資料庫連線 { DataTable dt = new DataTable(); // 建立 DataTable dt.Columns.Add("ID"); dt.Columns.Add("Name"); dt.Columns.Add("Age"); // 查詢資料 var query = db.Table1.Select(t => new { t.Id, t.Name, t.Age }).ToList(); foreach (var item in query) { dt.Rows.Add(item.Id, item.Name, item.Age); // 加入 DataTable } dataGridView1.DataSource = dt; // 綁定到 DataGridView 顯示 } ```