C# cơ bản .NET Core

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.csProgram.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 builderapp

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();
When using the minimal hosting model, the endpoint routing middleware wraps the entire middleware pipeline, therefore there's no need to have explicit calls to UseRouting or UseEndpoints to register routes. UseRouting can still be used to specify where route matching happens, but UseRouting doesn't need to be explicitly called if routes should be matched at the beginning of the middleware pipeline.

Đăng ký nhận bài viết mới