Dotnet basic

November 20, 2025

I. Project / Package Structure

1. Name convention

Project name convention

2. LaunchSettings.json trong Properties

Chỉ Inject ENV VARS

✔️ Khi bạn:

  • Chỉ tồn tại khi chạy local
  • Override mọi appsettings.*.json
  • Bấm F5 / Run trong Visual Studio.
  • Chạy: dotnet run --launch-profile "MyDotNet9App"

KHÔNG dùng khi:

  • Deploy production (Production KHÔNG đọc file này).
  • Docker.
  • Kubernetes.
  • IIS / Nginx thực tế.
{
  "$schema": "https://json.schemastore.org/launchsettings.json",
  "defaultProfile": "MyDotNet9App",
  "profiles": {
    "MyDotNet9App": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,        // open browser when run command line
      "launchUrl": "swagger",       // with /swagger
      "applicationUrl": "https://localhost:7123;http://localhost:5123",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development",   
        "ConnectionStrings__Default": "Server=.;Database=DevDb"
      }
    },

    "IIS": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

vì sao Production không chạy appsettings.json. Vì Best Practice trên PROD là phải run file dll, biến môi trường và tham số

dotnet MyApp.dll \
  --ConnectionStrings:Default="Server=prod;Database=ProdDb" \
  --Jwt:Issuer="mycompany"

3. Order startup

Program.cs - "điểm vào" của ứng dụng

ServiceCollectionExtensions.cs không tự chạy, nó chỉ được gọi từ Startup.cs

Program.cs

Startup.cs
├─ ConfigureServices(...)
└─ Configure(...)

Middleware pipeline

Nhận request HTTP

ConfigureServices làm gì ?

  • ServiceCollectionExtensions là helper cho ConfigureServices, giống extract methods, trành startup.cs viết quá nhiều codes
  • Đằng ký Dependency Inject
  • Add
    • Controllers
    • DBContext
    • Authentication
    • swagger
    • Custum Services

Configure làm gì ?

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
  if (env.IsDevelopment())   {
      app.UseDeveloperExceptionPage();
  }
  app.UseRouting();
  app.UseAuthentication();
  app.UseAuthorization();
  app.UseEndpoints(endpoints =>   {
      endpoints.MapControllers();
  });
}
  • Build Middleware pipeline
  • Request đi qua những middleware nào
  • Thứ tự middleware (rất quan trọng)

Middleware pipeline

Request

UseException

UseRouting

UseAuthentication

UseAuthorization

Controller

Response

Tóm tắt

  1. Program.cs bắt đầu
  2. CreateBuilder()
    • Load appsettings.json
    • Load environment
    • Setup logging
  3. builder.Services.AddXXX()
    • AddControllers
    • AddSwagger
    • AddApplicationServices (extension)
  4. builder.Build()
    • Build DI container
  5. app.UseXXX()
    • Setup middleware pipeline
  6. app.MapControllers()
  7. app.Run()
    • App sẵn sàng nhận request

Program.cs

Project .NET 5, using CreateHostBuilder

public class Program {
    public static void Main(string[] args)    {
        CreateHostBuilder(args).Build().Run();
    }
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder => {
                webBuilder.UseStartup<Startup>();
            });
}

Project .NET 9 (Web API), using WebApplication don't need setup order load appsettings.json or environmentVariables

var builder = WebApplication.CreateBuilder(args);
// ==========================
// 1. CONFIGURATION & SERVICES
// ==========================
// Add framework services
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Add custom services (extension)
builder.Services.AddApplicationServices();
// ==========================
// 2. BUILD APP
// ==========================
var app = builder.Build();
// ==========================
// 3. MIDDLEWARE PIPELINE
// ==========================
if (app.Environment.IsDevelopment()) {
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
// ==========================
// 4. RUN APP
// ==========================
app.Run();

Mapping sang Startup.cs

Mapping startup.cs version 5 and 9

II. Nuget offline

  1. Online: copy '%userprofile%\.nuget\package'
  2. Offline: place in a folder: c:\nugetrepo
  3. Offline: update 'nuget.config'
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="MyOfflineRepo" value="C:\nugetrepo" />
  </packageSources>
</configuration>
  1. Offline: run 'dotnet restore --source c:\nugetrepo'
  2. Offline: run dotnet build

III. Localization with .resx

Cách tốt nhất để tạo và sử dụng tệp Resources/AppStrings.resx là thông qua Visual Studio (hoặc Rider) để tận dụng tính năng tạo lớp truy cập chuỗi (Strongly-Typed Resource Accessor) tự động. Phức tạp và rắc rối hơn Java, bên Java chỉ cần file text .properties và nội dung là name = value.

File mặc định sẽ là English, có thể đặt tên AppStrings.en.resx, Restful nên dùng IStringLocalizer

- AppStrings.resx
- AppStrings.de.resx
- AppStrings.fr.resx
- AppStrings.it.resx

CáchĐề xuấtƯu điểmNhược điểm
IStringLocalizer + Factoryhỗ trợ DI, tự động cultureCần config chút
IStringLocalizer<T>Rất sạch, dễ testPhải tạo class dummy
Strongly-typed classKhôngIntellisense tốtPhải set Culture thủ công
ResourceManagerKhôngKhông cần DICũ, khó test

Cách dùng thì có 4 cách nhưng khuyến khích 2 cách đầu

  • Add thư mục Resources vào context, chứ không mặc định như Java và set default language trong startup.cs

IV. Extension Methods

Extension Method (Phương thức mở rộng):

Là một loại phương thức đặc biệt trong C# (và .NET) cho phép bạn thêm các phương thức mới vào một lớp (class), cấu trúc (struct), hoặc giao diện (interface) hiện có mà không cần phải:

  1. Sửa đổi mã nguồn gốc của lớp đó.
  2. Tạo lớp con kế thừa từ lớp đó.
  3. Biên dịch lại lớp gốc.
using System;
using System.Text.RegularExpressions;
// Step 1: Create a static class
public static class StringExtensions {
    // Step 2: Create a static method with 'this' keyword
    public static bool IsEmail(this string input)   {
        if (string.IsNullOrWhiteSpace(input)) {
            return false;
        }
        string pattern = @"^[^@\s]+@[^@\s]+\.[^@\s]+$";
        return Regex.IsMatch(input.Trim(), pattern);
    }

    // Another useful extension: Capitalize first letter
    public static string Capitalize(this string input) {
        if (string.IsNullOrEmpty(input)) {
            return input;
        }
        return char.ToUpper(input[0]) + input.Substring(1).ToLower();
    }
}

Cách Dùng

class Program {
  static void Main()  {
    string email = "john.doe@example.com";
    string name = "hello world";
    // These methods don't exist in System.String, but we can call them!
    Console.WriteLine(email.IsEmail());        // True
    Console.WriteLine("bad-email".IsEmail());  // False
    Console.WriteLine(name.Capitalize());      // Hello world
    // Works with null (be careful!)
    string nullString = null;
    Console.WriteLine(nullString.IsEmail()); // False (no NullReferenceException)
  }
}

V. Either, EitherAsync, Task<Either<...>>, Validation and LINQ

1. Either

Either<Left, Right>, Either<IError, Unit>

Left, Right chỉ là 2 giá trị nhưng có thể tận dụng vào validation

Left: false, Right: true (pass validation)

Either,Unit của thư viện LanguageExt.

IError: là Interface tự định nghĩa.

1.1 Unit = "void nhưng có thể truyền đi"

  • Định nghĩa
    • Chỉ có 1 giá trị duy nhất
    • Không mang data
    • Chỉ mang ý nghĩa: “thành công”
  • Khi nào dùng Unit
    • Không nên:
        Either<IError, void> // không compile
      
    • Đúng cách:
        Either<IError, Unit>
      
  • Example:
      Either<Error, Unit> ValidateUser(string userId)
      {
        if (string.IsNullOrEmpty(userId))
          return Error.New("Invalid user");
        return Unit.Default;
      }
    

2. LINQ: Select ... from ... where

var result = await (
          from _ in ValidationHelper.ValidateMandatoryParameter(query.UniqueId, query.RepositoryUniqueId).ToAsync()
          from _2 in SecurityHelper.ValidateId(query.UniqueId).ToAsync()
          from _3 in SecurityHelper.ValidateId(query.RepositoryUniqueId).ToAsync()            
          from binaryResponse in service.GetDocument(query).ToAsync()
          select binaryResponse);
  • _ là discard
  • Unit chỉ mang ý nghĩa PASS
  • select binaryResponse quyết định kết quả cuối

Model Right ──▶ Right ──▶ Right ──▶ Right(binaryResponse) │
└── Left(error) ──▶ STOP

  • result: right khi tất cả là right
  • result: left, chỉ cần một bước trả left thì pipeline dừng lại và resultleft với lỗi đó.
  • pipeline: một chuỗi các bước xử lý dữ liệu, chạy theo thứ tự, đầu ra của bước trước là đầu vào của bước sau (giống như dây chuyền sản xuất), fail fast (bước nào fail thì dừng ngay)