Production-ready ASP.NET Core 10 Web API quản lý vật tư xây dựng & so sánh chuyên sâu LINQ Method vs. Query Syntax.
Tính năng • Cấu trúc • Khởi chạy • API • Kiểm thử LINQ • Đóng góp • License
RESTful Web API quản lý danh mục vật tư xây dựng và nhà cung cấp, thiết kế theo tiêu chuẩn Clean Architecture với ASP.NET Core 10 và Entity Framework Core. Dự án cung cấp triển khai song song giữa LINQ Method Syntax và LINQ Query Syntax, hỗ trợ phân giải động runtime, kết hợp phân trang chuẩn, lọc nâng cao và cơ chế xử lý lỗi tập trung.
- Kiến trúc rõ ràng (Separation of Concerns): Tách biệt Controller, Service, DTO, Entity và DbContext.
- Đối chiếu LINQ song song:
MethodSyntaxMaterialService: Triển khai qua Fluent extension methods (Join,GroupJoin,SelectMany,Where).QuerySyntaxMaterialService: Triển khai qua từ khóa declarative query comprehension (from,join ... into,where,select).- Dynamic Resolver: Chuyển đổi cú pháp LINQ tức thời qua query parameter hoặc request header.
- Endpoint đối chiếu trực tiếp (
/syntax-comparison): Kiểm tra đồng thời 2 service để chứng minh tính nhất quán của dữ liệu.
- Phép kết nối dữ liệu nâng cao:
INNER JOIN: Chỉ lấy vật tư có nhà cung cấp hợp lệ.LEFT OUTER JOIN: Giữ lại toàn bộ vật tư (vật tư chưa gán nhà cung cấp được gán giá trị mặc định"Chưa có nhà cung cấp").
- Phân trang & Lọc an toàn:
PagingDto<T>: Trả về đầy đủPageIndex,PageSize,TotalPages,TotalItems,HasPreviousPage,HasNextPage.FilterMaterialDto: Tự động trim từ khóa tìm kiếm, kiểm tra logic khoảng giá hợp lệ (MinPrice <= MaxPrice, không âm) quaIValidatableObject.
- Xử lý lỗi tập trung (Global Exception Handling):
- Bắt lỗi qua middleware, chuẩn hóa mọi phản hồi theo cấu trúc
ApiResponse<T>. - Tự động bắt lỗi Data Annotations và trả về mã lỗi 400 có cấu trúc.
- Bắt lỗi qua middleware, chuẩn hóa mọi phản hồi theo cấu trúc
- Cơ sở dữ liệu linh hoạt:
- Tự động sử dụng SQL Server nếu có connection string.
- Tự động fallback về In-Memory Database (
TH4_Db) nếu không có SQL Server để chạy thử nghiệm ngay lập tức. - Khởi tạo dữ liệu mẫu (
SeedData) 1 lần duy nhất lúc startup thông quaDbInitializer.
material-management-api/
├── Controllers/
│ └── MaterialsController.cs # REST API endpoints, routing, OpenAPI annotations
├── Data/
│ ├── ApplicationDbContext.cs # EF Core DbContext, Fluent API mapping
│ ├── DbInitializer.cs # Database seeding logic lúc khởi động
│ └── SeedData.cs # Dữ liệu mẫu (4 nhà cung cấp, 10 vật tư)
├── Exceptions/
│ ├── NotFoundException.cs # Ngoại lệ 404
│ └── UserFriendlyException.cs # Ngoại lệ 400 kèm business error code
├── Middleware/
│ └── GlobalExceptionMiddleware.cs # Bắt lỗi toàn cục, trả về JSON camelCase
├── Models/
│ ├── Common/
│ │ ├── ApiResponse.cs # Chuẩn hóa vỏ phản hồi API
│ │ └── ErrorCodes.cs # Hằng số mã lỗi nghiệp vụ
│ ├── Dtos/
│ │ ├── CreateMaterialDto.cs # DTO tạo mới vật tư
│ │ ├── UpdateMaterialDto.cs # DTO cập nhật vật tư
│ │ ├── MaterialDto.cs # DTO danh sách vật tư
│ │ ├── MaterialDetailDto.cs # DTO chi tiết vật tư & nhà cung cấp
│ │ ├── FilterDto.cs # DTO lọc & phân trang cơ bản
│ │ ├── FilterMaterialDto.cs # DTO lọc giá & từ khóa
│ │ └── PagingDto.cs # Generic pagination response metadata
│ └── Entities/
│ ├── Material.cs # Entity vật tư
│ └── Supplier.cs # Entity nhà cung cấp
├── Services/
│ ├── Interfaces/
│ │ └── IMaterialService.cs # Hợp đồng nghiệp vụ vật tư
│ └── Implementations/
│ ├── MethodSyntaxMaterialService.cs # Triển khai LINQ Method Syntax
│ └── QuerySyntaxMaterialService.cs # Triển khai LINQ Query Syntax
├── GiaiThich-Inner.txt # Báo cáo lý thuyết chi tiết về phép Inner/Left Join
└── Program.cs # Bootstrapper, DI container, Middleware pipeline
- .NET 10.0 SDK trở lên.
- (Tùy chọn) SQL Server nếu muốn lưu dữ liệu thực tế.
Tip
Ứng dụng tự động fallback sang In-Memory Database (TH4_Db) nếu không tìm thấy cấu hình SQL Server hợp lệ. Bạn có thể clone và chạy thử nghiệm ngay lập tức mà không cần cài đặt thêm bất kỳ hệ quản trị cơ sở dữ liệu nào.
-
Clone repository:
git clone https://github.com/Tuannll/material-management-api.git cd material-management-api -
Restore dependencies & Build:
dotnet restore dotnet build
-
Chạy ứng dụng:
dotnet run
-
Truy cập Swagger UI: Mở trình duyệt tại:
http://localhost:5018/swaggerhoặchttp://localhost:5000/swagger.
| Method | Endpoint | Mô tả | Mã HTTP |
|---|---|---|---|
GET |
/api/materials |
Lấy danh sách vật tư phân trang & lọc | 200, 400 |
GET |
/api/materials/{id} |
Lấy chi tiết vật tư theo ID | 200, 404 |
GET |
/api/materials/inner-join |
Lấy danh sách qua phép INNER JOIN | 200 |
GET |
/api/materials/left-join |
Lấy danh sách qua phép LEFT JOIN | 200 |
GET |
/api/materials/syntax-comparison |
Đối chiếu song song kết quả 2 cú pháp LINQ | 200 |
POST |
/api/materials |
Tạo mới vật tư | 201, 400 |
PUT |
/api/materials/{id} |
Cập nhật thông tin vật tư | 200, 400, 404 |
DELETE |
/api/materials/{id} |
Xóa vật tư theo ID | 204, 404 |
Hệ thống cho phép đổi service trực tiếp trên từng request:
- Cách 1: Query String: Thêm
?syntax=queryhoặc?syntax=methodGET /api/materials?syntax=query
- Cách 2: Request Header: Truyền header
X-Syntax-Style: queryhoặcX-Syntax-Style: methodcurl -X GET "http://localhost:5018/api/materials" -H "X-Syntax-Style: query"
Trường sourceImplementation trong dữ liệu trả về sẽ thể hiện rõ: "Method Syntax Style" hoặc "Query Syntax Style".
- Dữ liệu mẫu: Gồm 10 vật tư. Trong đó:
- Vật tư ID 1 đến 8: Có
SupplierIdhợp lệ (1, 2, 3, hoặc 4). - Vật tư ID 9 (
Ống nhựa PVC) và ID 10 (Sơn chống thấm): CóSupplierId = null.
- Vật tư ID 1 đến 8: Có
- Kiểm tra Inner Join:
-> Trả về 8 bản ghi (ID 9 và ID 10 bị loại vì
GET /api/materials/inner-join
SupplierIdlànull). - Kiểm tra Left Join:
-> Trả về đủ 10 bản ghi (ID 9 và ID 10 hiển thị
GET /api/materials/left-join
supplierName: "Chưa có nhà cung cấp").
{
"success": true,
"statusCode": 200,
"message": "Retrieved materials successfully.",
"data": {
"items": [
{
"id": 1,
"name": "Xi măng PC40",
"unit": "Tấn",
"unitPrice": 1500000,
"supplierName": "Xi măng Xuân Thành",
"supplierAddress": "Hà Nam",
"sourceImplementation": "Method Syntax Style"
}
],
"totalItems": 10,
"pageIndex": 1,
"pageSize": 10,
"totalPages": 1,
"hasPreviousPage": false,
"hasNextPage": false
},
"errors": []
}{
"success": false,
"statusCode": 400,
"message": "Giá nhỏ nhất (minPrice) không được lớn hơn giá lớn nhất (maxPrice).",
"data": null,
"errors": [
"MinPrice",
"MaxPrice"
]
}Mọi đóng góp nhằm cải thiện và phát triển dự án đều được hoan nghênh:
- Fork repository.
- Tạo nhánh tính năng (
git checkout -b feature/AmazingFeature). - Commit thay đổi (
git commit -m "feat: add some AmazingFeature"). - Push lên branch (
git push origin feature/AmazingFeature). - Mở một Pull Request.
Phát hành theo giấy phép MIT License.
