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 ý:
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.
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
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ở 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
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
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 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();
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();
Nguồn tin: XuanThuLab