ASP.NET Core
03 / 03

Authentication, EF Core & Background Services

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 update

Background 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