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!

LecturreAI Assistant

Hello! I'm LecturreAI

What would you like to learn today?