Production-ready ASP.NET Core 10 Web API managing construction materials with deep comparative analysis of LINQ Method vs. Query Syntax.
English | Tiếng Việt
✨ Features • 📁 Structure • 🚀 Quick Start • 📡 Endpoints • 💡 LINQ Analysis • 📄 License
A RESTful Web API managing construction material inventories and suppliers, engineered according to Clean Architecture principles with ASP.NET Core 10 and Entity Framework Core. The project offers a side-by-side benchmark of LINQ Method Syntax and LINQ Query Syntax, supporting dynamic runtime resolution, robust pagination, and centralized error handling.
- Clean Architecture & Separation of Concerns: Explicit modularization into Controllers, Services, DTOs, Domain Entities, and DbContext.
- Dual LINQ Implementations:
MethodSyntaxMaterialService: Implemented via Fluent extension methods (Join,GroupJoin,SelectMany,Where).QuerySyntaxMaterialService: Implemented via declarative query comprehension (from,join ... into,where,select).- Dynamic Resolver: Toggle LINQ execution styles at runtime via query string or HTTP header.
- Syntax Comparison Endpoint (
/syntax-comparison): Concurrently executes both pipelines to verify output equivalence.
- Advanced Relational Joins:
INNER JOIN: Filters exclusively for materials associated with an active supplier.LEFT OUTER JOIN: Preserves all materials; unassigned suppliers default gracefully to"Chưa có nhà cung cấp".
- Safe Pagination & Filtering:
PagingDto<T>: Returns complete metadata:PageIndex,PageSize,TotalPages,TotalItems,HasPreviousPage,HasNextPage.FilterMaterialDto: Auto-trims search queries and enforces non-negative price boundaries (MinPrice <= MaxPrice) viaIValidatableObject.
- Global Exception Handling:
- Intercepts uncaught exceptions via middleware and standardizes envelopes as
ApiResponse<T>. - Auto-captures model state validation errors and emits structured HTTP 400 responses.
- Intercepts uncaught exceptions via middleware and standardizes envelopes as
- Resilient Database Layer:
- Automatically targets SQL Server when configured.
- Fallbacks seamlessly to an In-Memory Database (
TH4_Db) for instant zero-dependency testing. - Single-execution seed initialization (
SeedData) at startup viaDbInitializer.
material-management-api/
├── Controllers/
│ └── MaterialsController.cs # REST API endpoints, routing, OpenAPI annotations
├── Data/
│ ├── ApplicationDbContext.cs # EF Core DbContext, Fluent API mapping
│ ├── DbInitializer.cs # Database startup seeding logic
│ └── SeedData.cs # Mock seed records (4 suppliers, 10 materials)
├── Exceptions/
│ ├── NotFoundException.cs # HTTP 404 domain exception
│ └── UserFriendlyException.cs # HTTP 400 business exception with custom error codes
├── Middleware/
│ └── GlobalExceptionMiddleware.cs # Global exception middleware with camelCase JSON serialization
├── Models/
│ ├── Common/
│ │ ├── ApiResponse.cs # API response envelope
│ │ └── ErrorCodes.cs # Business error code constants
│ ├── Dtos/
│ │ ├── CreateMaterialDto.cs # Material creation DTO
│ │ ├── UpdateMaterialDto.cs # Material update DTO
│ │ ├── MaterialDto.cs # Material list item DTO
│ │ ├── MaterialDetailDto.cs # Detailed material & supplier DTO
│ │ ├── FilterDto.cs # Base pagination DTO
│ │ ├── FilterMaterialDto.cs # Price & keyword search DTO
│ │ └── PagingDto.cs # Generic pagination response metadata
│ └── Entities/
│ ├── Material.cs # Material entity
│ └── Supplier.cs # Supplier entity
├── Services/
│ ├── Interfaces/
│ │ └── IMaterialService.cs # Material service contract
│ └── Implementations/
│ ├── MethodSyntaxMaterialService.cs # LINQ Method Syntax implementation
│ └── QuerySyntaxMaterialService.cs # LINQ Query Syntax implementation
├── GiaiThich-Inner.txt # Comprehensive theoretical comparison on Inner vs Left Joins
└── Program.cs # Bootstrapper, DI container, and middleware pipeline
- .NET 10.0 SDK or higher.
- (Optional) SQL Server instance if persistent storage is desired.
Tip
The application automatically falls back to an In-Memory Database (TH4_Db) if no valid SQL Server connection is supplied. You can clone and run immediately without database setup.
git clone https://github.com/Tuannll/material-management-api.git
cd material-management-api
dotnet restore
dotnet builddotnet runOpen your browser at: http://localhost:5018/swagger or http://localhost:5000/swagger.
| Method | Endpoint | Description | HTTP Status |
|---|---|---|---|
GET |
/api/materials |
Retrieve paginated & filtered materials | 200, 400 |
GET |
/api/materials/{id} |
Get material details by ID | 200, 404 |
GET |
/api/materials/inner-join |
Query materials via INNER JOIN | 200 |
GET |
/api/materials/left-join |
Query materials via LEFT JOIN | 200 |
GET |
/api/materials/syntax-comparison |
Benchmark both LINQ implementations side-by-side | 200 |
POST |
/api/materials |
Create material | 201, 400 |
PUT |
/api/materials/{id} |
Update material | 200, 400, 404 |
DELETE |
/api/materials/{id} |
Delete material | 204, 404 |
Switch between service implementations per request:
- Option 1: Query String: Append
?syntax=queryor?syntax=methodGET /api/materials?syntax=query
- Option 2: Request Header: Pass
X-Syntax-Style: queryorX-Syntax-Style: methodcurl -X GET "http://localhost:5018/api/materials" -H "X-Syntax-Style: query"
The sourceImplementation field in the response envelope indicates "Method Syntax Style" or "Query Syntax Style".
- Sample Data: 10 materials seeded:
- IDs 1 to 8: Assigned to valid suppliers (1, 2, 3, or 4).
- IDs 9 (
Ống nhựa PVC) and 10 (Sơn chống thấm):SupplierId = null.
- Inner Join Test:
-> Returns 8 records (IDs 9 and 10 are excluded).
GET /api/materials/inner-join
- Left Join Test:
-> Returns all 10 records (IDs 9 and 10 output
GET /api/materials/left-join
supplierName: "Chưa có nhà cung cấp").
Distributed under the MIT License.