ASP.NET Core: Authentication, EF Core & Background Services
JWT Authentication
// dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
// Program.cs
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:Secret"]!))
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
options.AddPolicy("PremiumUser", policy =>
policy.RequireClaim("subscription", "premium", "enterprise"));
});
// Token generation
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:Secret"]!));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _config["Jwt:Issuer"],
audience: _config["Jwt:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddMinutes(60),
signingCredentials: credentials
);
return new JwtSecurityTokenHandler().WriteToken(token);
}Entity Framework Core
// DbContext
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<Article> Articles => Set<Article>();
public DbSet<User> Users => Set<User>();
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Article>(entity =>
{
entity.HasKey(a => a.Id);
entity.Property(a => a.Title).HasMaxLength(200).IsRequired();
entity.HasOne(a => a.Author)
.WithMany(u => u.Articles)
.HasForeignKey(a => a.AuthorId)
.OnDelete(DeleteBehavior.Cascade);
entity.HasIndex(a => a.Slug).IsUnique();
});
}
}
// Queries
var articles = await _db.Articles
.Where(a => a.Status == "published")
.Include(a => a.Author)
.OrderByDescending(a => a.CreatedAt)
.Skip(page * pageSize).Take(pageSize)
.AsNoTracking() // faster for read-only queries
.ToListAsync();
// Create/Update/Delete
var article = new Article { Title = "New Article", AuthorId = userId };
_db.Articles.Add(article);
await _db.SaveChangesAsync();
// Migrations
// dotnet ef migrations add InitialCreate
// dotnet ef database updateBackground Services
// IHostedService / BackgroundService
public class DataSyncService : BackgroundService
{
private readonly IServiceScopeFactory _scopeFactory;
private readonly ILogger<DataSyncService> _logger;
public DataSyncService(IServiceScopeFactory scopeFactory, ILogger<DataSyncService> logger)
{
_scopeFactory = scopeFactory;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
// Use scope to resolve scoped services (DbContext etc.)
using var scope = _scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await SyncData(db, stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Sync failed");
}
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
}
}
}
// Register
builder.Services.AddHostedService<DataSyncService>();Key Libraries & Patterns
MediatR: CQRS pattern — Commands/Queries/Notifications decouple controllers from business logic.
Carter: lightweight Minimal API module system — organize endpoints into ICarterModule classes.
Serilog: structured logging with sinks (file, Seq, Elastic, Application Insights).
Polly: resilience — retry, circuit breaker, timeout, bulkhead for HttpClient calls.
Health checks: app.MapHealthChecks("/health") with database/redis/external dependency checks.
Response caching: [ResponseCache] attribute or IOutputCacheStore for API response caching.
Rate limiting: AddRateLimiter() with fixed/sliding/token bucket/concurrency limiters (built-in .NET 7+).
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free