C# 14與.NET10新功能 - 2

這是《C# 14與.NET10新功能》系列文章的第二篇,我們將介紹.NET 10在ASP.NET Core網頁與資料存取的重點新功能以及如何利用GitHub Copilot應用程式現代化(GitHub Copilot app modernization)升級舊專案。
網頁與資料存取升級
.NET 10版不僅僅關注底層執行效能,也為開發者日常使用的應用程式框架帶來了許多更實用的更新。讓我們先來看看如何使用ASP.NET Core 10 和 Entity Framework Core 10 的新功能來打造一個現代化的安全應用程式。
ASP.NET Core 10:支援利用通行密鑰(Passkey)登入
ASP.NET Core 10支援利用通行密鑰 (Passkey)登入網站。想像一下,若您正在開發一個網站管理系統。首先,您需要一個安全的方式來讓管理員登入,然後才可查詢所有使用者、產品等相關資料。ASP.NET Core 10現在可以使用通行密鑰(Passkey)實現無密碼登入,有時為了提升管理後台的安全性,您可以放棄傳統的密碼登入方式,改用通行密鑰(Passkey)這是一種以WebAuthn 標準為基礎的無密碼技術,使用者可以透過指紋、臉部辨識或 PIN 碼安全登入,既方便又極大地降低了帳號被盜的風險。
ASP.NET
Core 10 透過 ASP.NET Core 的身分識別 API(ASP.NET Core Identity API)內建對 Passkey 的支援,讓您可以輕鬆地為網站或應用程式加入這項現代化的安全功能,而無需處理複雜的底層加密細節。
Entity Framework Core 10:使用原生 LEFT JOIN 簡化資料查詢
EF Core
10 終於引入了對 LEFT JOIN 和 RIGHT JOIN 的原生支援。這是一個期待已久的功能,它讓處理這類關聯式資料的查詢變得極為直觀,程式碼也會變得更為簡潔、更易於維護。讓我們來透過一個使用Entity Framework Core 10與Sqlite資料庫的主控台應用程式來說明。
使用Visual
Studio 2026開發工具建立一個主控台專案(Console
App),然後使用NuGet安裝「Microsoft.EntityFrameworkCore」與「Microsoft.EntityFrameworkCore.Sqlite」套件:
Install-Package
Microsoft.EntityFrameworkCore
Install-Package
Microsoft.EntityFrameworkCore.Sqlite
在 EF Core 10版,JOIN的語法非常接近 SQL,您可以寫得幾乎像 SQL,但仍然保持 LINQ 語意,在「Program.cs」檔案加入以下程式碼,查詢資料庫中所有的產品(Products),並把其所屬的分類(Categories)名稱一起帶出。若某些產品沒有對應的分類(NULL),依然要顯示產品資訊,這就是 LEFT JOIN:
using System;
using Microsoft.EntityFrameworkCore;
// 定義Product實體
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; } = string.Empty;
public int? CategoryId { get; set; }
}
// 定義Category實體
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; } = string.Empty;
}
// 定義 DbContext
public class NorthwindContext : DbContext
{
// 定義 DbSet 屬性,代表Product資料表
public DbSet<Product> Products => Set<Product>();
// 定義 DbSet 屬性,代表Category資料表
public DbSet<Category> Categories => Set<Category>();
// 設定資料庫連線(此處使用 SQLite 檔案)
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// 使用 EF Core SQLite 提供者,資料檔案為 northwind.db
optionsBuilder.UseSqlite("Data Source=northwind.db");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 設定Product主鍵為ProductId
modelBuilder.Entity<Product>().HasKey(p => p.ProductId);
// 設定Category主鍵為CategoryId
modelBuilder.Entity<Category>().HasKey(c => c.CategoryId);
}
}
public class Program
{
public static void Main()
{
// 使用 SQLite 範例:如有舊資料庫先刪除,然後建立並寫入範例資料
using var ctx = new NorthwindContext();
// 刪除舊資料庫並建立新資料表
ctx.Database.EnsureDeleted();
ctx.Database.EnsureCreated();
// 寫入Category分類資料
ctx.Categories.AddRange(
new Category { CategoryId = 1, CategoryName = "Beverages" },
new Category { CategoryId = 2, CategoryName = "Condiments" },
new Category { CategoryId = 3, CategoryName = "Confections" }
);
// 寫入Product產品資料(部分 CategoryId 為 null,用於示範 LEFT JOIN)
ctx.Products.AddRange(
new Product { ProductId = 1, ProductName = "Chai", CategoryId = 1 },
new Product { ProductId = 2, ProductName = "Chang", CategoryId = 1 },
new Product { ProductId = 3, ProductName = "Aniseed Syrup", CategoryId = 2 },
new Product { ProductId = 4, ProductName = "Chef Anton's Cajun Seasoning", CategoryId = 2 },
new Product { ProductId = 5, ProductName = "Grandma's Boysenberry Spread", CategoryId = 2 },
new Product { ProductId = 6, ProductName = "Uncategorized Sample Product", CategoryId = null }
);
ctx.SaveChanges();
// 使用原生 LEFT JOIN(LINQ 查詢語法),EF Core 會轉譯為 SQL 的 LEFT JOIN
var query = (
from p in ctx.Products
join c in ctx.Categories on p.CategoryId equals c.CategoryId into gj
from c in gj.DefaultIfEmpty() // LEFT JOIN:包含沒有對應類別的產品
orderby p.ProductId
select new
{
p.ProductId,
p.ProductName,
CategoryName = c != null ? c.CategoryName : null
}
);
// 輸出產生的 SQL 查詢語句(SQLite 會產生 SQL)
try
{
Console.WriteLine(query.ToQueryString());
}
catch
{
// 若無法產生 SQL 字串則忽略
Console.WriteLine("無法產生查詢的 SQL 字串。");
}
Console.WriteLine();
Console.WriteLine("Product List :");
// 執行查詢並輸出結果
foreach (var row in query.ToList())
{
Console.WriteLine($"#{row.ProductId} {row.ProductName} | Category: {row.CategoryName ?? "(無分類)"}");
}
}
}
執行這個範例,LINQ 查詢的LEFT JOIN語法所產生的SQL如下:
SELECT
"p"."ProductId", "p"."ProductName",
"c"."CategoryName"
FROM
"Products" AS "p"
LEFT
JOIN "Categories" AS "c" ON
"p"."CategoryId" = "c"."CategoryId"
ORDER
BY "p"."ProductId"
這個範例執行的結果請參考下圖所示:
圖 1:Left Join範例
Right Join
LINQ 本身不支援 RIGHT JOIN功能,下面示範如何在 EF Core / LINQ中「模擬 RIGHT JOIN」語法。簡單的說,從想要保留全部列的那一邊開始做 LEFT JOIN(也就是把原本的左右資料表互換):
RIGHT
JOIN = 保留右表所有列
因此在LINQ語法中等同於從右邊的資料表(此處為 Categories)出發,對左邊的資料表做 LEFT JOIN。
EF Core 會把這種寫法轉譯成 LEFT JOIN(從 Categories 到 Products),執行的結果將會與 SQL 的 RIGHT JOIN 相同。
以下C#程式碼,改寫上例程式,模擬RIGHT JOIN,把查詢改成從「Categories」開始做 LEFT JOIN 到「Products」(等同於 Products RIGHT JOIN Categories):
using System;
using Microsoft.EntityFrameworkCore;
// 定義Product實體
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; } = string.Empty;
public int? CategoryId { get; set; }
}
// 定義Category實體
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; } = string.Empty;
}
// 定義 DbContext
public class NorthwindContext : DbContext
{
// 定義 DbSet 屬性,代表Product資料表
public DbSet<Product> Products => Set<Product>();
// 定義 DbSet 屬性,代表Category資料表
public DbSet<Category> Categories => Set<Category>();
// 設定資料庫連線(此處使用 SQLite 檔案)
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// 使用 EF Core SQLite 提供者,資料檔案為 northwind.db
optionsBuilder.UseSqlite("Data Source=northwind.db");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 設定Product主鍵為ProductId
modelBuilder.Entity<Product>().HasKey(p => p.ProductId);
// 設定Category主鍵為CategoryId
modelBuilder.Entity<Category>().HasKey(c => c.CategoryId);
}
}
public class Program
{
public static void Main()
{
// 使用 SQLite 範例:如有舊資料庫先刪除,然後建立並寫入範例資料
using var ctx = new NorthwindContext();
// 刪除舊資料庫並建立新資料表
ctx.Database.EnsureDeleted();
ctx.Database.EnsureCreated();
// 寫入Category分類資料
ctx.Categories.AddRange(
new Category { CategoryId = 1, CategoryName = "Beverages" },
new Category { CategoryId = 2, CategoryName = "Condiments" },
new Category { CategoryId = 3, CategoryName = "Confections" }
);
// 寫入Product產品資料(部分 CategoryId 為 null,用於示範 JOIN)
ctx.Products.AddRange(
new Product { ProductId = 1, ProductName = "Chai", CategoryId = 1 },
new Product { ProductId = 2, ProductName = "Chang", CategoryId = 1 },
new Product { ProductId = 3, ProductName = "Aniseed Syrup", CategoryId = 2 },
new Product { ProductId = 4, ProductName = "Chef Anton's Cajun Seasoning", CategoryId = 2 },
new Product { ProductId = 5, ProductName = "Grandma's Boysenberry Spread", CategoryId = 2 },
new Product { ProductId = 6, ProductName = "Uncategorized Sample Product", CategoryId = null }
);
ctx.SaveChanges();
// 模擬 RIGHT JOIN(LINQ 不支援 RIGHT JOIN):從 Categories 開始 LEFT JOIN 到 Products
var query = (
from c in ctx.Categories
join p in ctx.Products on c.CategoryId equals p.CategoryId into gj
from p in gj.DefaultIfEmpty() // LEFT JOIN:包含沒有對應產品的類別(等同於 Products RIGHT JOIN Categories)
orderby c.CategoryId, p.ProductId
select new
{
CategoryId = c.CategoryId,
CategoryName = c.CategoryName,
ProductId = p != null ? p.ProductId : (int?)null,
ProductName = p != null ? p.ProductName : null
}
);
// 輸出產生的 SQL 查詢語句(SQLite 會產生 SQL)
try
{
Console.WriteLine(query.ToQueryString());
}
catch
{
// 若無法產生 SQL 字串則忽略
Console.WriteLine("無法產生查詢的 SQL 字串。");
}
Console.WriteLine();
Console.WriteLine("Category with Products (模擬 RIGHT JOIN) :");
// 執行查詢並輸出結果
foreach (var row in query.ToList())
{
Console.WriteLine($"Category #{row.CategoryId} {row.CategoryName} | Product: #{row.ProductId?.ToString() ?? "(無產品)"} {row.ProductName ?? ""}");
}
}
}
這個範例執行的結果請參考下圖所示:
圖 2:Right Join範例
GitHub Copilot應用程式現代化(GitHub Copilot app
modernization)
應用程式現代化之所以重要,是因為使用較舊的框架可能會導致安全風險、影響效能,並使應用程式難以擴展或遷移到雲端。升級到新版本(例如 .NET 10版)可以帶來更好的執行時間效率、現代 API 和更佳的工具。
GitHub Copilot 應用程式現代化(GitHub Copilot app modernization)是一個內建於 Visual Studio 2026開發工具中的AI 工具。透過 AI 驅動的代理能力(agentic capabilities)。為開發人員提供自動化的支援,以快速、安全地將舊有的 .NET 應用程式升級到現代的、為雲端最佳化的 .NET 平台。
GitHub Copilot 應用程式現代化(GitHub
Copilot app modernization)的目標是將複雜的遷移過程從「可能需要數週」縮短到「只需幾個小時」,聽起來是不是令人驚豔?
GitHub
Copilot 應用程式現代化工具的功能包括以下:
◼繁重工作自動化:引導您升級舊版應用程式到新版本,完成現代化的每一步,處理模糊不清的代碼問題,並自動執行許多繁瑣的升級步驟。
◼升級 .NET 框架:它支援將較舊的應用程式(例如 .NET Framework 4.8 專案)升級到最新的 .NET 版本(如 .NET 10)。它會產生升級計畫,詳細說明所有 NuGet 套件的更新,並將專案現代化到新的 SDK 風格。
◼ 自動產生測試:GitHub
Copilot 測試代理(Testing Agent)是這項現代化功能的一部分,專門為那些缺乏單元測試的舊應用程式自動產生測試程式碼。它能分析程式碼結構和專案設定,生成準確的測試程式作為程式碼可靠性的起點。
◼遷移到雲端的準備:無論您是升級到 .NET 10版還是準備遷移到雲端,此現代化工具都能提供協助。例如,它可以協助將應用程式從 Windows AD 驗證轉換為 Entra ID驗證。
使用範例
以一個.NET
Framework 4.8版存取SQL Server資料庫的Windows Forms專案為例,在Visual Studio 2026開發工具開啟這個專案,然後從「方案總管」> 專案名稱上方按右鍵,選取「現代化」,請參考下圖所示:
圖 3:GitHub Copilot 應用程式現代化(GitHub Copilot app
modernization)
接下來會開啟GitHub Copilot Chat視窗,可以直接點選聊天介面中的「升級至較新版本的.NET」項目,然後按下下方的「傳送」按鈕,請參考下圖所示:
圖 4:「升級至較新版本的.NET」
聊天介面通常會在下方列出選項讓您選擇想繼續的動作,若沒有出現這些選項,可以直接在下方文字方塊輸入提示,要求升級到較新版本的.NET,請參考下圖所示:
圖 5:接受升級設定並繼續
選擇聊天介面下方的「接受升級設定並繼續」項目,按下「送出 」按鈕,請參考下圖所示:
圖 6:接受升級設定並繼續
接著稍候片刻,它就會開始建立升級計畫(Upgrade Plan),請參考下圖所示:
圖 7:建立升級計畫(Upgrade Plan)
然後開始進行升級,請參考下圖所示:
圖 8:專案升級
根據專案的大小,升級所花費的時間也不同。
總結
本文章介紹 .NET 10
在「網頁應用程式安全性」與「資料存取層設計」上的重大升級,並結合 GitHub Copilot 的 AI 現代化工具,說明如何讓開發者在效率與安全性之間取得平衡。整體來看,.NET 10 不僅是一個效能提升的版本,更是推動企業與開發者邁向雲端與無密碼時代的重要里程碑。
在 ASP.NET Core 10部分,全新支援的「通行密鑰(Passkey)登入」功能技術基於「WebAuthn」標準,讓使用者能透過指紋、臉部辨識或 PIN 碼進行無密碼登入,取代傳統帳密機制,大幅提升安全性與便利性。開發者可透過 ASP.NET Core Identity API 輕鬆整合 Passkey,無需手動處理複雜的金鑰與加密機制。這意味著網站與後台系統可更快速地導入企業級身分驗證,降低密碼洩漏風險,並提升使用者體驗。
其次,Entity
Framework Core 10(EF Core 10) 的更新讓資料查詢更貼近真實 SQL 操作。它原生支援 LEFT JOIN 與 RIGHT JOIN,開發者能使用簡潔的 LINQ 語法撰寫關聯查詢,輕鬆讀取包含空值(NULL)的資料列。我們以SQLite資料庫為範例展示如何透過 LINQ 撰寫 LEFT JOIN 取得所有產品及其分類,即使產品無分類仍能顯示,並進一步示範如何「模擬」 RIGHT JOIN,以保持語法一致性與可維護性。這項改進使 EF Core 更接近資料庫層的語意,並大幅減少手動撰寫 SQL 的需求。
最後,文章介紹
GitHub Copilot 應用程式現代化(App
Modernization) 工具。這是 Visual Studio 2026開發工具 內建的 AI 助理,透過智能代理(Agentic
Capabilities)協助開發者自動化舊版應用程式升級流程。它能分析專案結構、生成升級計畫(Upgrade Plan)、更新 NuGet 套件、轉換至 SDK 風格專案,甚至能 自動產生測試程式 以補足缺乏單元測試的舊系統。此外,它也支援雲端遷移準備,例如將 Windows AD 驗證轉換為 Entra ID,幫助開發者快速導入現代化的雲端環境。這項工具能將傳統需要數週的升級流程縮短至數小時,讓開發團隊專注於功能創新而非重複性作業。
綜合而言,.NET 10、EF Core 10 與 GitHub Copilot Modernization 形成一個完整的升級生態系:前端提升登入安全性,中層簡化資料查詢邏輯,後端則由 AI 自動協助重構與升級。這不僅展現出微軟推動開發自動化與安全現代化的決心,也讓開發者能更高效地打造可擴充、可維護、雲端導向的應用程式。
0 意見:
張貼留言