Introduction
In this article, we will see the Best practice to make your project cleaner in .NET Core.
Let's get started.
1. Project Structure and Layering
- Separate your application into layers/projects:
- API / UI Layer: ASP.NET Core Web API or MVC project.
- Application Layer: Business logic, services, DTOs.
- Domain Layer: Entities, domain logic, interfaces.
- Infrastructure Layer: Data access (EF Core, repositories), external services.
Use solution folders to organize these projects.
/MyCleanProject
/MyCleanProject.Api <-- ASP.NET Core Web API
/MyCleanProject.Application <-- Business Logic, Services, DTOs
/MyCleanProject.Domain <-- Entities, Interfaces
/MyCleanProject.Infrastructure <-- EF Core, Repositories, Data Access
/MyCleanProject.Tests <-- Unit Tests
2. Use Dependency Injection (DI)
- ASP.NET Core has built-in DI, use it to inject:
- Services
- Repositories
- Logging
- Configuration
- Avoid new keyword inside your classes for dependencies.
- Register services with appropriate lifetimes (Scoped, Singleton, Transient).
Example in Startup.cs or Program.cs (for .NET 6+ minimal hosting):
services.AddScoped<IUserService, UserService>();
services.AddSingleton<IMySingletonService, MySingletonService>();
3. Configuration Management
- Use appsettings.json, appsettings.Development.json for environment-based settings.
- Use IConfiguration interface injected wherever you need configuration values.
- For secrets (API keys, passwords), use User Secrets in development and environment variables or Azure Key Vault in production.
4. Logging
- Use ASP.NET Core’s built-in logging abstraction (ILogger<T>).
- Configure structured logging providers like Serilog.
- Write meaningful log messages at proper levels (Information, Warning, Error, etc.).
5. Use EF Core with Best Practices
- Use Code-First or Database-First approach consistently.
- Use DbContext with scoped lifetime.
- Avoid exposing EF Core entities directly in APIs — use DTOs.
- Use AsNoTracking() for read-only queries to improve performance.
- Handle migrations properly with dotnet ef migrations.
6. API Design (if using Web API)
- Use RESTful principles.
- Return appropriate HTTP status codes.
- Use DTOs or ViewModels instead of domain models.
- Validate input using [DataAnnotations] and FluentValidation.
- Use Model Binding and Model Validation built into ASP.NET Core.
- Use ApiController attribute for automatic validation responses.
7. Middleware and Pipeline
- Keep middleware modular and focused.
- Use built-in middlewares for:
- Exception handling (UseExceptionHandler or UseDeveloperExceptionPage)
- Routing (UseRouting)
- Authentication & Authorization
- CORS (UseCors)
- HTTPS redirection (UseHttpsRedirection)
8. Async Programming
- Use async/await for I/O operations (database, HTTP calls).
- Avoid blocking calls (.Result, .Wait()).
9. Testing
- Unit test your business logic, services.
- Use xUnit, Moq for mocking dependencies.
- Integration test your API endpoints with WebApplicationFactory.
10. Security
- Use ASP.NET Core Identity or JWT for authentication.
- Protect sensitive endpoints with [Authorize].
- Use HTTPS.
- Validate all inputs.
- Avoid exposing sensitive information in errors.
Example Minimal Startup Setup in .NET 6+
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddLogging();
// Build the app
var app = builder.Build();
// Configure middleware pipeline
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Conclusion
Here we tried to cover Best practice to make your project cleaner in .NET Core.