Do hiện nay đã có đến .NET 5, .NET 6 và ngôn ngữ lập trình C# 9 (.net5), C# 10 (.net6) mà các bài viết trong chủ đề về C# tác giả mới test trên .NET 3 và một phần .NET 5 nên có một số cập nhật dưới đây để khi đọc các bài viết bạn có thể tự thay đổi, cập nhật mà nà vẫn có thể thực hành theo code trong các ví dụ.
Những mệnh đề cấp cao : top-level statements
Như đã biết, mỗi chương trình C# đều có điểm mồi là hàm Main
.
Nhưng bắt đầu từ C#9 bạn không cần khai báo hàm này nữa (còn cứ khai báo như cũ
cũng không sao). Toàn bộ những chỉ thị lệnh (mệnh đề, statement) trong hàm Main cũ
bạn đưa vào một file, các chỉ thị lệnh này gọi là top-level (nó không viết trong
namespace nào, không viết trong lớp nào), trình biên dịch sẽ tự động nhận biết đây là
các mã lệnh của hàm Main và tự động sinh ra hàm Main.
Ví dụ, một chương trình console mẫu khởi tạo từ lệnh:
dotnet new console
Nếu phiên bản cũ, nó sẽ sinh ra file Program.cs
với nội dung
using System; namespace AppName { internal class Program { static void Main(string[] args) { Console.WriteLine("Hello World!"); } } }
Phiên bản C# mới , file Program.cs
đó chỉ có nội dung
// See https://aka.ms/new-console-template for more information Console.WriteLine("Hello, World!");
Ở file mới này, có dòng Console.WriteLine("Hello, World!")
viết không thuộc namespace nào, không thuộc lớp nào => Vậy trình biên dịch
sẽ coi đây là top-level statement và thuộc hàm Main, nó sẽ tự phát sinh hàm này.
Khi viết code bạn cần lưu ý:
Chỉ có một file để chứa các top-level statement
Nếu bạn viết top-level statement ở nhiều file, sẽ phát sinh lỗi biên dịch. File chứa các top-level statement từ đây tạm gọi là file top-level.
Dùng chỉ thị using, khai báo lớp, truy cập tham số trong file top-level
Nếu cần nạp namespace, thư viện vào file top-level bạn vẫn sử dụng chỉ thị
using
chú ý viết nó ở đầu file.
using System.Text.Json; // using phải sử dụng ở đầu file /* Đây là các namespace tự động nạp (có sẵn mà không cần using) Nếu muốn bỏ tự động nạp hãy sửa (file .csproj): <ImplicitUsings>enable</ImplicitUsings> thành <ImplicitUsings>disable</ImplicitUsings> using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; */ // Sau phần using là các top-level statements Console.WriteLine("Hello, World!"); XYZ.Abc.TestAbc(); // Có thể truy cập lấy tham số truyền cho Main qua biến // có sẵn args Console.WriteLine("Số thám số: " + args.Count()); // Có thể viết mệnh đề return, code thoát chương trình.s return 1; // Phần cuối của file có thể khai báo các lớp namespace XYZ { class Abc { public static void TestAbc() { } } }
Nếu một lệnh dotnet sử dụng các template truyền
thống để phát sinh các bộ khung dự án, khi thực hiện lệnh hãy cho thêm
tham số --framework net5.0
, trên máy cũng cần cài đặt .NET 5 SDK
dotnet new console --framework net5.0
Dịch chuyển asp.net lên .NET 6
Trong các mẫu template khởi tạo từ lệnh dotnet
nó cũng sử dụng top-level statement, nên ở đây
lưu ý một số điểm nếu bạn muốn sử dụng kiểu mới này (bạn vẫn có thể sử dụng kiểu cũ nếu muốn, chỉ việc
cho biết sẽ sử dụng .NET 6 bằng cách cập nhật file project .csproj, sửa TargetFramework thành net6.0)
Mô hình hosting mới
Mở mẫu ứng dụng asp.net mới, nó tạo ra cơ chế khởi tạo hosting mới. Nó hợp nhất toàn bộ code
của Startup.cs
và Program.cs
vào thành một file top-level là Program.cs
.
Bây giờ trong không còn Startup.cs, còn Program.cs chỉ có nội dụng rất đơn giản:
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.UseRouting(); app.MapGet("/", () => "Hello World!"); app.Run();
Trong file .csproj của dự án, có thiết lập Sdk là Microsoft.NET.Sdk.Web và có sử dụng ImplicitUsings (enabel), nên trong file top-level của asp.net mặc định tự động có sẵn các namespace:
System.Net.Http.Json Microsoft.AspNetCore.Builder Microsoft.AspNetCore.Hosting Microsoft.AspNetCore.Http Microsoft.AspNetCore.Routing Microsoft.Extensions.Configuration Microsoft.Extensions.DependencyInjection Microsoft.Extensions.Hosting Microsoft.Extensions.Logging
Do không còn Startup.cs, nên các cấu hình trước đây bạn khai báo bên trong các phương thức như
ConfigureServices, Configure sẽ viết hết ở file top-level (Program.cs)
bằng cách sử dụng đối tượng builder
và app
Di chuyển code cấu hình host trong Program.CreateHostBuilder
Xem xét toàn bộ code trong phương thức này, viết ngay sau khi tạo builder
trong top-level, ví
dụ:
var builder = WebApplication.CreateBuilder(args); /* Code trong Program.CreateHostBuilder public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { // Chỉ nhận http (không https) webBuilder.UseUrls("http://0.0.0.0:5000"); webBuilder.UseStartup<Startup>(); }); Thay bằng: */ builder.WebHost.UseUrls("http://0.0.0.0:5005"); // .. các code khác
Code trong phương thức khởi tạo của Startup cũ
Thường code truy cập một số đối tượng khi khởi động host, như IConfiguration ..., thì toàn bộ các đối tượng đó đều lấy qua builder, ví dụ đọc config
var builder = WebApplication.CreateBuilder(args); builder.WebHost.UseUrls("http://0.0.0.0:5005"); // Xem xét đặt Startup.Startup ở đây // đọc config var testoptions = builder.Configuration.GetSection ("TestOptions");
Code trong Startup.ConfigureServices
Code trong thương thức này cơ bản là để đăng ký, inject các dịch vụ mới vào ứng dụng, thì giờ đây
các dịch vụ được đăng ký thông qua đối tượng builder.Services
, xử lý trước khi gọi
builder.Build()
var builder = WebApplication.CreateBuilder(args); // ... // Thêm vào dòng lấy IServiceCollection var services = builder.Services; /* ============================================================ Copy code cũ trong Startup.ConfigureServices vào đây, ví dụ =========================================================== */ services.AddControllersWithViews(); services.AddDistributedMemoryCache(); services.AddSession(cfg => { cfg.Cookie.Name = "xuanthulab"; cfg.IdleTimeout = new TimeSpan(0,30, 0); }); //... var app = builder.Build();
Code trong Startup.Configure
Tại đây chủ yếu để thêm các middleware vào pileline, cấu hình routing ... Toàn bộ code này
giờ đây sẽ đặt sau đoạn code builder.Build()
var builder = WebApplication.CreateBuilder(args); // ... // ... // ... var app = builder.Build(); /* ============================================================ Code viết trong Configure cũ đặt tại đay, ví dụ: =========================================================== */ if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();