Backend-utveckling
C# & .NET
Modern C#-utveckling med .NET: dependency injection, design patterns och praktiska exempel.
01 · Översikt
Vad du hittar här
Utvecklingsmönster i C# och .NET, från dependency injection till design patterns, med kodexempel och lösningar på problem som dyker upp i backend-kod.
02 · Kod
Kodexempel
Dependency Injection Setup
Configure dependency injection container in .NET applications.
// Program.cs (.NET 6+)
var builder = WebApplication.CreateBuilder(args);
// Register services
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddSingleton<ICacheService, MemoryCacheService>();
builder.Services.AddTransient<IEmailService, EmailService>();
// Add Entity Framework
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
var app = builder.Build();Async/Await Pattern
Proper async/await implementation for database operations.
public async Task<User> GetUserAsync(int userId)
{
try
{
using var connection = new SqlConnection(connectionString);
await connection.OpenAsync();
var sql = "SELECT * FROM Users WHERE Id = @UserId";
var user = await connection.QueryFirstOrDefaultAsync<User>(sql,
new { UserId = userId });
return user;
}
catch (SqlException ex)
{
_logger.LogError(ex, "Database error while fetching user {UserId}", userId);
throw;
}
}Configuration Pattern
Strongly-typed configuration using IOptions pattern.
// appsettings.json
{
"EmailSettings": {
"SmtpHost": "smtp.example.com",
"SmtpPort": 587,
"Username": "user@example.com",
"EnableSsl": true
}
}
// EmailSettings.cs
public class EmailSettings
{
public string SmtpHost { get; set; }
public int SmtpPort { get; set; }
public string Username { get; set; }
public bool EnableSsl { get; set; }
}
// Program.cs
builder.Services.Configure<EmailSettings>(
builder.Configuration.GetSection("EmailSettings"));
// Usage in service
public class EmailService
{
private readonly EmailSettings _emailSettings;
public EmailService(IOptions<EmailSettings> emailSettings)
{
_emailSettings = emailSettings.Value;
}
}03 · Mönster
Design patterns
Repository Pattern
Abstracts data access logic and provides a consistent interface.
public interface IUserRepository
{
Task<User> GetByIdAsync(int id);
Task<IEnumerable<User>> GetAllAsync();
Task<User> CreateAsync(User user);
Task UpdateAsync(User user);
Task DeleteAsync(int id);
}
public class UserRepository : IUserRepository
{
private readonly ApplicationDbContext _context;
public UserRepository(ApplicationDbContext context)
{
_context = context;
}
public async Task<User> GetByIdAsync(int id)
{
return await _context.Users
.FirstOrDefaultAsync(u => u.Id == id);
}
public async Task<User> CreateAsync(User user)
{
_context.Users.Add(user);
await _context.SaveChangesAsync();
return user;
}
}Fördelar
- Testability
- Separation of concerns
- Consistent data access API
Unit of Work Pattern
Manages transactions and coordinates multiple repository operations.
public interface IUnitOfWork : IDisposable
{
IUserRepository Users { get; }
IOrderRepository Orders { get; }
Task<int> SaveChangesAsync();
Task BeginTransactionAsync();
Task CommitTransactionAsync();
Task RollbackTransactionAsync();
}
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationDbContext _context;
private IDbContextTransaction _transaction;
public UnitOfWork(ApplicationDbContext context)
{
_context = context;
Users = new UserRepository(context);
Orders = new OrderRepository(context);
}
public IUserRepository Users { get; }
public IOrderRepository Orders { get; }
public async Task<int> SaveChangesAsync()
{
return await _context.SaveChangesAsync();
}
public void Dispose()
{
_transaction?.Dispose();
_context.Dispose();
}
}Fördelar
- Transaction management
- Consistency across repositories
- Resource cleanup
04 · Praxis
Best practices
Use Nullable Reference Types
Enable nullable reference types to catch null reference exceptions at compile time
Exempel:
<Nullable>enable</Nullable> in project file and use string? for nullable stringsImplement Proper Logging
Use structured logging with appropriate log levels and contexts
Exempel:
_logger.LogInformation("User {UserId} performed action {Action}", userId, action)Follow SOLID Principles
Design classes with single responsibility and dependency inversion
Exempel:
Use interfaces for dependencies and keep classes focused on one responsibilityUse Configuration Validation
Validate configuration values at startup to fail fast
Exempel:
Use IValidateOptions<T> to validate configuration objects during registration
05 · Prestanda
Prestanda
Database Connection Pooling
Optimize database connections by using connection pooling properly
Lösning: Use connection strings with appropriate pooling settings and avoid holding connections too long
Async All The Way
Use async/await throughout the call chain to avoid thread pool starvation
Lösning: Never mix async and sync code (avoid .Result or .Wait())
Memory Management
Minimize allocations and implement IDisposable for resources
Lösning: Use object pooling, span/memory for large data, and proper disposal patterns
Caching Strategies
Implement appropriate caching for frequently accessed data
Lösning: Use IMemoryCache for local caching and distributed cache for scalable solutions
06 · Felsökning
Vanliga problem och lösningar
NullReferenceException
Most common runtime exception in C# applications
Lösning: Enable nullable reference types and use null-conditional operators (?. and ??)
Memory Leaks
Objects not being garbage collected due to event handlers or static references
Lösning: Properly unsubscribe from events and avoid keeping references in static collections
Deadlocks in Async Code
Mixing synchronous and asynchronous code can cause deadlocks
Lösning: Use ConfigureAwait(false) in library code and avoid .Result/.Wait()
Entity Framework N+1 Problem
Executing multiple queries when one would suffice
Lösning: Use Include() for related data or projection with Select() to avoid lazy loading issues
07 · Tips
Tips för C#-utveckling
- Använd Code Analysis
- Aktivera kod-analys och behandla warnings som errors för att fånga problem tidigt.
- Skriv enhetstester
- Använd xUnit eller NUnit för att skriva enhetstester och säkerställa kodkvalitet.
- Följ .NET conventions
- Använd PascalCase för publika medlemmar och camelCase för privata fält.