C# & ASP.NET Topic
Building Applications with .NET Core in C#
Overview of .NET Core
.NET Core is a cross-platform, open-source framework for building modern applications. It's the evolution of the .NET Framework, designed to work on Windows, Linux, and macOS.
Key Characteristics
-
Cross-platform: Runs on Windows, Linux, and macOS
-
High performance: Optimized for speed and efficiency
-
Modular: Lightweight with NuGet package dependencies
-
Open source: MIT license, community-driven development
-
Container-ready: Ideal for Docker and microservices
Project Structure
Typical .NET Core Project
text
MyProject/
├── Program.cs # Application entry point
├── Startup.cs # Configuration and services (for older templates)
├── appsettings.json # Configuration file
├── Controllers/ # MVC/Web API controllers
├── Models/ # Data models
├── Views/ # Razor views (for MVC)
├── Services/ # Business logic
├── Data/ # Data access layer
└── MyProject.csproj # Project file
Creating a .NET Core Application
1. Install Prerequisites
bash
# Install .NET SDK
# Download from https://dotnet.microsoft.com/download
# Verify installation
dotnet --version
2. Create a New Project
bash
# Create a web API
dotnet new webapi -n MyWebApi
# Create a web app (MVC)
dotnet new mvc -n MyWebApp
# Create a console app
dotnet new console -n MyConsoleApp
# Create a class library
dotnet new classlib -n MyClassLibrary
3. Program.cs (Modern .NET 6+ Template)
csharp
// Minimal API approach (for simple APIs)
var builder = WebApplication.CreateBuilder(args);
// Add services to the container
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Dependency Injection (DI)
Built-in DI Container
csharp
public interface IMyService
{
string GetData();
}
public class MyService : IMyService
{
public string GetData() => "Hello from service!";
}
// Registration in Program.cs or Startup.cs
builder.Services.AddScoped<IMyService, MyService>();
builder.Services.AddSingleton<IMyService, MyService>(); // Singleton
builder.Services.AddTransient<IMyService, MyService>(); // Transient
// Injection into controllers
public class MyController : ControllerBase
{
private readonly IMyService _service;
public MyController(IMyService service)
{
_service = service;
}
}
Configuration Management
appsettings.json
json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=mydb;Trusted_Connection=True;"
},
"AppSettings": {
"ApiKey": "your-api-key",
"MaxItems": 100
}
}
Access Configuration
csharp
public class MyService
{
private readonly IConfiguration _configuration;
public MyService(IConfiguration configuration)
{
_configuration = configuration;
}
public void UseConfig()
{
var connectionString = _configuration.GetConnectionString("DefaultConnection");
var apiKey = _configuration["AppSettings:ApiKey"];
var maxItems = _configuration.GetValue<int>("AppSettings:MaxItems");
}
}
Entity Framework Core
Setup and Basic Usage
csharp
// Install packages:
// Microsoft.EntityFrameworkCore.SqlServer
// Microsoft.EntityFrameworkCore.Tools
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options) { }
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
// Registration
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// Models
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int CategoryId { get; set; }
public Category Category { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public List<Product> Products { get; set; }
}
Building Web APIs
Controllers
csharp
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly ApplicationDbContext _context;
public ProductsController(ApplicationDbContext context)
{
_context = context;
}
// GET: api/products
[HttpGet]
public async Task<ActionResult<IEnumerable<Product>>> GetProducts()
{
return await _context.Products.ToListAsync();
}
// GET: api/products/5
[HttpGet("{id}")]
public async Task<ActionResult<Product>> GetProduct(int id)
{
var product = await _context.Products.FindAsync(id);
if (product == null)
{
return NotFound();
}
return product;
}
// POST: api/products
[HttpPost]
public async Task<ActionResult<Product>> CreateProduct(Product product)
{
_context.Products.Add(product);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product);
}
// PUT: api/products/5
[HttpPut("{id}")]
public async Task<IActionResult> UpdateProduct(int id, Product product)
{
if (id != product.Id)
{
return BadRequest();
}
_context.Entry(product).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(id))
{
return NotFound();
}
throw;
}
return NoContent();
}
// DELETE: api/products/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteProduct(int id)
{
var product = await _context.Products.FindAsync(id);
if (product == null)
{
return NotFound();
}
_context.Products.Remove(product);
await _context.SaveChangesAsync();
return NoContent();
}
private bool ProductExists(int id)
{
return _context.Products.Any(e => e.Id == id);
}
}
Authentication and Authorization
JWT Authentication
csharp
// Add authentication services
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
};
});
// Add authorization
builder.Services.AddAuthorization();
// In app configuration
app.UseAuthentication();
app.UseAuthorization();
// Protect endpoints
[Authorize]
[ApiController]
[Route("api/[controller]")]
public class SecureController : ControllerBase
{
[HttpGet("protected")]
public IActionResult GetProtectedData()
{
var username = User.Identity.Name;
return Ok($"Protected data for {username}");
}
[Authorize(Roles = "Admin")]
[HttpGet("admin-only")]
public IActionResult AdminOnly()
{
return Ok("Admin access granted");
}
}
Logging
Built-in Logging
csharp
public class MyService
{
private readonly ILogger<MyService> _logger;
public MyService(ILogger<MyService> logger)
{
_logger = logger;
}
public void DoWork()
{
_logger.LogInformation("Starting work");
try
{
// Some work
_logger.LogDebug("Processing item {ItemId}", 123);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error doing work");
}
_logger.LogInformation("Work completed");
}
}
Testing
Unit Testing with xUnit
csharp
// Test class
public class CalculatorTests
{
private readonly Calculator _calculator;
public CalculatorTests()
{
_calculator = new Calculator();
}
[Fact]
public void Add_TwoNumbers_ReturnsSum()
{
// Arrange
var a = 5;
var b = 3;
// Act
var result = _calculator.Add(a, b);
// Assert
Assert.Equal(8, result);
}
[Theory]
[InlineData(5, 3, 8)]
[InlineData(0, 0, 0)]
[InlineData(-5, 3, -2)]
public void Add_VariousInputs_ReturnsCorrectSum(int a, int b, int expected)
{
// Act
var result = _calculator.Add(a, b);
// Assert
Assert.Equal(expected, result);
}
}
// Calculator class
public class Calculator
{
public int Add(int a, int b) => a + b;
public int Subtract(int a, int b) => a - b;
}
Middleware
Custom Middleware
csharp
// Custom middleware class
public class RequestLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public RequestLoggingMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)
{
_next = next;
_logger = loggerFactory.CreateLogger<RequestLoggingMiddleware>();
}
public async Task InvokeAsync(HttpContext context)
{
var startTime = DateTime.UtcNow;
_logger.LogInformation($"Request started: {context.Request.Method} {context.Request.Path}");
await _next(context);
var duration = DateTime.UtcNow - startTime;
_logger.LogInformation($"Request completed: {context.Response.StatusCode} in {duration.TotalMilliseconds}ms");
}
}
// Extension method for easy registration
public static class RequestLoggingMiddlewareExtensions
{
public static IApplicationBuilder UseRequestLogging(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestLoggingMiddleware>();
}
}
// In Program.cs
app.UseRequestLogging();
Deployment
Publish for Different Environments
bash
# Development build
dotnet publish -c Debug -o ./publish
# Release build for specific runtime
dotnet publish -c Release -r win-x64 --self-contained true -o ./publish
# Docker container
docker build -t myapp .
docker run -p 8080:80 myapp
Dockerfile Example
dockerfile
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["MyWebApi.csproj", "./"]
RUN dotnet restore "MyWebApi.csproj"
COPY . .
RUN dotnet publish "MyWebApi.csproj" -c Release -o /app/publish
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyWebApi.dll"]
Best Practices
1. Use Async/Await Properly
csharp
public async Task<ActionResult<IEnumerable<Product>>> GetProducts()
{
// Use async version of EF Core methods
return await _context.Products.ToListAsync();
}
2. Implement Repository Pattern
csharp
public interface IRepository<T> where T : class
{
Task<T> GetByIdAsync(int id);
Task<IEnumerable<T>> GetAllAsync();
Task AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(T entity);
}
public class Repository<T> : IRepository<T> where T : class
{
protected readonly ApplicationDbContext _context;
protected readonly DbSet<T> _dbSet;
public Repository(ApplicationDbContext context)
{
_context = context;
_dbSet = context.Set<T>();
}
public async Task<T> GetByIdAsync(int id)
{
return await _dbSet.FindAsync(id);
}
}
3. Use AutoMapper for Object Mapping
csharp
// DTOs
public class ProductDto
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
// Mapping profile
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Product, ProductDto>();
CreateMap<ProductDto, Product>();
}
}
// Usage in controller
public class ProductsController : ControllerBase
{
private readonly IMapper _mapper;
public ProductsController(IMapper mapper)
{
_mapper = mapper;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<ProductDto>>> GetProducts()
{
var products = await _context.Products.ToListAsync();
return _mapper.Map<List<ProductDto>>(products);
}
}
4. Implement Health Checks
csharp
// Add health checks
builder.Services.AddHealthChecks()
.AddDbContextCheck<ApplicationDbContext>()
.AddUrlGroup(new Uri("https://api.example.com"), "External API");
// Map health check endpoint
app.MapHealthChecks("/health");
// Custom health check
public class CustomHealthCheck : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
// Check custom logic
var isHealthy = true;
return isHealthy
? Task.FromResult(HealthCheckResult.Healthy("All good!"))
: Task.FromResult(HealthCheckResult.Unhealthy("Something's wrong"));
}
}
Project File (.csproj) Example
xml
Run
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="11.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.0" />
</ItemGroup>
</Project>
This extensive guide covers the essentials of building applications with .NET Core in C#. Start with simple console apps or APIs, then gradually incorporate more advanced patterns and practices as your needs grow.
Comments (0)
No comments yet. Be the first to comment!