.NET Framework

ASP.NET Core, Entity Framework & Modern .NET Development

.NET Basics

.NET CLI Commands

Bash
# Create new project
dotnet new console -n MyApp          # Console app
dotnet new webapi -n MyApi           # Web API
dotnet new mvc -n MyMvc              # MVC app
dotnet new blazorserver -n MyBlazor  # Blazor Server
dotnet new classlib -n MyLib         # Class library

# Run project
dotnet run
dotnet watch run  # Hot reload

# Build
dotnet build
dotnet publish -c Release

# Package management
dotnet add package Newtonsoft.Json
dotnet remove package PackageName
dotnet restore

# Solution management
dotnet new sln -n MySolution
dotnet sln add ./MyProject/MyProject.csproj

# Entity Framework
dotnet tool install --global dotnet-ef
dotnet ef migrations add InitialCreate
dotnet ef database update

Project Structure

Folder Structure
MyWebApi/
├── Controllers/
│   └── WeatherController.cs
├── Models/
│   └── Weather.cs
├── Services/
│   └── WeatherService.cs
├── Data/
│   └── AppDbContext.cs
├── DTOs/
│   └── WeatherDto.cs
├── appsettings.json
├── appsettings.Development.json
├── Program.cs
└── MyWebApi.csproj

ASP.NET Core

Minimal API (Program.cs)

C#
var builder = WebApplication.CreateBuilder(args);

// Add services
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Middleware pipeline
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

// Minimal API endpoints
app.MapGet("/", () => "Hello World!");

app.MapGet("/items", () => new[] { "Item1", "Item2" });

app.MapGet("/items/{id}", (int id) => $"Item {id}");

app.MapPost("/items", (Item item) => Results.Created($"/items/{item.Id}", item));

app.MapPut("/items/{id}", (int id, Item item) => Results.NoContent());

app.MapDelete("/items/{id}", (int id) => Results.Ok());

app.Run();

record Item(int Id, string Name);

Middleware

C#
// Custom middleware
app.Use(async (context, next) =>
{
    // Before request
    Console.WriteLine($"Request: {context.Request.Path}");
    
    await next();  // Call next middleware
    
    // After request
    Console.WriteLine($"Response: {context.Response.StatusCode}");
});

// Custom middleware class
public class LoggingMiddleware
{
    private readonly RequestDelegate _next;
    
    public LoggingMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    
    public async Task InvokeAsync(HttpContext context)
    {
        // Log request
        await _next(context);
        // Log response
    }
}

// Register: app.UseMiddleware();

Web API Controllers

C#
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductService _productService;
    
    public ProductsController(IProductService productService)
    {
        _productService = productService;
    }
    
    // GET api/products
    [HttpGet]
    public async Task<ActionResult<IEnumerable<Product>>> GetAll()
    {
        var products = await _productService.GetAllAsync();
        return Ok(products);
    }
    
    // GET api/products/5
    [HttpGet("{id}")]
    public async Task<ActionResult<Product>> GetById(int id)
    {
        var product = await _productService.GetByIdAsync(id);
        
        if (product == null)
            return NotFound();
            
        return Ok(product);
    }
    
    // POST api/products
    [HttpPost]
    public async Task<ActionResult<Product>> Create([FromBody] CreateProductDto dto)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);
            
        var product = await _productService.CreateAsync(dto);
        return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
    }
    
    // PUT api/products/5
    [HttpPut("{id}")]
    public async Task<IActionResult> Update(int id, [FromBody] UpdateProductDto dto)
    {
        await _productService.UpdateAsync(id, dto);
        return NoContent();
    }
    
    // DELETE api/products/5
    [HttpDelete("{id}")]
    public async Task<IActionResult> Delete(int id)
    {
        await _productService.DeleteAsync(id);
        return NoContent();
    }
}

Data Transfer Objects (DTOs)

C#
public record CreateProductDto(
    [Required] string Name,
    [Range(0, 10000)] decimal Price,
    string? Description
);

public record ProductDto(int Id, string Name, decimal Price);

Entity Framework Core

DbContext

C#
public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options) { }
    
    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Fluent API configuration
        modelBuilder.Entity<Product>()
            .HasKey(p => p.Id);
            
        modelBuilder.Entity<Product>()
            .Property(p => p.Name)
            .IsRequired()
            .HasMaxLength(100);
            
        modelBuilder.Entity<Product>()
            .HasOne(p => p.Category)
            .WithMany(c => c.Products)
            .HasForeignKey(p => p.CategoryId);
    }
}

// Entity
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; }
}

// Register in Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));

CRUD Operations

C#
public class ProductRepository
{
    private readonly AppDbContext _context;
    
    public ProductRepository(AppDbContext context)
    {
        _context = context;
    }
    
    // Read
    public async Task<List<Product>> GetAllAsync()
    {
        return await _context.Products
            .Include(p => p.Category)
            .ToListAsync();
    }
    
    public async Task<Product?> GetByIdAsync(int id)
    {
        return await _context.Products
            .FirstOrDefaultAsync(p => p.Id == id);
    }
    
    // Create
    public async Task<Product> CreateAsync(Product product)
    {
        _context.Products.Add(product);
        await _context.SaveChangesAsync();
        return product;
    }
    
    // Update
    public async Task UpdateAsync(Product product)
    {
        _context.Entry(product).State = EntityState.Modified;
        await _context.SaveChangesAsync();
    }
    
    // Delete
    public async Task DeleteAsync(int id)
    {
        var product = await _context.Products.FindAsync(id);
        if (product != null)
        {
            _context.Products.Remove(product);
            await _context.SaveChangesAsync();
        }
    }
    
    // LINQ Queries
    public async Task<List<Product>> SearchAsync(string term)
    {
        return await _context.Products
            .Where(p => p.Name.Contains(term))
            .OrderBy(p => p.Price)
            .Take(10)
            .ToListAsync();
    }
}

Dependency Injection

C#
// Interface
public interface IEmailService
{
    Task SendEmailAsync(string to, string subject, string body);
}

// Implementation
public class EmailService : IEmailService
{
    private readonly ILogger<EmailService> _logger;
    
    public EmailService(ILogger<EmailService> logger)
    {
        _logger = logger;
    }
    
    public async Task SendEmailAsync(string to, string subject, string body)
    {
        _logger.LogInformation($"Sending email to {to}");
        // Send email logic
    }
}

// Register services in Program.cs
builder.Services.AddScoped<IEmailService, EmailService>();
builder.Services.AddTransient<ITransientService, TransientService>();
builder.Services.AddSingleton<ISingletonService, SingletonService>();

// Lifetimes:
// Transient - Created each time requested
// Scoped    - Created once per HTTP request
// Singleton - Created once for application lifetime

// Use in controller
public class NotificationController : ControllerBase
{
    private readonly IEmailService _emailService;
    
    public NotificationController(IEmailService emailService)
    {
        _emailService = emailService;
    }
}

Authentication & Authorization

JWT Authentication

C#
// Program.cs - Configure JWT
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"]))
        };
    });

builder.Services.AddAuthorization();

// Middleware
app.UseAuthentication();
app.UseAuthorization();

// Generate token
public string GenerateToken(User user)
{
    var claims = new[]
    {
        new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
        new Claim(ClaimTypes.Email, user.Email),
        new Claim(ClaimTypes.Role, user.Role)
    };
    
    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
    var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    
    var token = new JwtSecurityToken(
        issuer: _config["Jwt:Issuer"],
        audience: _config["Jwt:Audience"],
        claims: claims,
        expires: DateTime.Now.AddHours(1),
        signingCredentials: creds);
    
    return new JwtSecurityTokenHandler().WriteToken(token);
}

// Protect endpoints
[Authorize]
[HttpGet("profile")]
public IActionResult GetProfile() { }

[Authorize(Roles = "Admin")]
[HttpDelete("{id}")]
public IActionResult Delete(int id) { }

Unit Testing

C#
using Xunit;
using Moq;

public class ProductServiceTests
{
    private readonly Mock<IProductRepository> _mockRepo;
    private readonly ProductService _service;
    
    public ProductServiceTests()
    {
        _mockRepo = new Mock<IProductRepository>();
        _service = new ProductService(_mockRepo.Object);
    }
    
    [Fact]
    public async Task GetById_ExistingId_ReturnsProduct()
    {
        // Arrange
        var expectedProduct = new Product { Id = 1, Name = "Test" };
        _mockRepo.Setup(r => r.GetByIdAsync(1))
            .ReturnsAsync(expectedProduct);
        
        // Act
        var result = await _service.GetByIdAsync(1);
        
        // Assert
        Assert.NotNull(result);
        Assert.Equal("Test", result.Name);
    }
    
    [Fact]
    public async Task GetById_NonExistingId_ReturnsNull()
    {
        // Arrange
        _mockRepo.Setup(r => r.GetByIdAsync(999))
            .ReturnsAsync((Product?)null);
        
        // Act
        var result = await _service.GetByIdAsync(999);
        
        // Assert
        Assert.Null(result);
    }
    
    [Theory]
    [InlineData(1)]
    [InlineData(2)]
    [InlineData(3)]
    public async Task Delete_ValidId_CallsRepository(int id)
    {
        // Act
        await _service.DeleteAsync(id);
        
        // Assert
        _mockRepo.Verify(r => r.DeleteAsync(id), Times.Once);
    }
}
.NET Best Practices:
• Use async/await for I/O operations
• Follow Repository pattern for data access
• Use DTOs to separate API models from entities
• Implement proper error handling middleware
• Use configuration for environment-specific settings