R3 Slice 3: CongressTrading-Modul + Worker-Log + core_settings auf EF Core

- Modul: CongressTradingDbContext (ct_congressMember/ct_trade) + Design-Time-Factory;
  CongressRepository von Dapper auf EF; CongressTrade.DetailsFetched ergaenzt; Dapper entfernt
- Core: CoreSettingsService (core_settings via EF); WorkerBase-Log auf EF (core_worker_log);
  7 Worker-Ctors DatabaseService -> IDbContextFactory<CoreDbContext>
- CoreMigrations + CongressMigrations (Dapper) entfernt; core_-Schema nun rein EF
- EF-Migration InitialCongressTrading; AddCorePersistence-Fallback fuer leeren Connection-String
  (App startet ohne DB); Connection aus appsettings.Local.json (gitignored)
- Tests: +4 CongressRepository (EF-InMemory) -> 39/39 gruen; Build + smoke-ui + App-Start ok

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
This commit is contained in:
Richard
2026-07-28 11:34:42 +02:00
parent 7e3791b82d
commit 6c8e36dc3c
27 changed files with 832 additions and 323 deletions
@@ -0,0 +1,52 @@
using IBKRTrader.Modules.CongressTrading.Models;
using Microsoft.EntityFrameworkCore;
namespace IBKRTrader.Modules.CongressTrading.Persistence.Ef;
/// <summary>
/// EF-Core-Kontext des CongressTrading-Moduls (ct_-Tabellen in derselben MariaDB wie der Core).
/// </summary>
public class CongressTradingDbContext : DbContext
{
public CongressTradingDbContext(DbContextOptions<CongressTradingDbContext> options) : base(options) { }
public DbSet<CongressMember> Members => Set<CongressMember>();
public DbSet<CongressTrade> Trades => Set<CongressTrade>();
protected override void OnModelCreating(ModelBuilder b)
{
b.Entity<CongressMember>(e =>
{
e.ToTable("ct_congressMember");
e.HasKey(x => x.Id);
e.HasIndex(x => x.BioId).IsUnique();
e.Property(x => x.BioId).HasMaxLength(20);
e.Property(x => x.Name).HasMaxLength(200);
e.Property(x => x.Party).HasMaxLength(50);
e.Property(x => x.State).HasMaxLength(50);
e.Property(x => x.Chamber).HasMaxLength(10);
e.Property(x => x.ProfileUrl).HasMaxLength(500);
});
b.Entity<CongressTrade>(e =>
{
e.ToTable("ct_trade");
e.HasKey(x => x.Id);
e.Ignore(x => x.MemberSnapshot); // transient nicht persistieren
e.HasIndex(x => x.TradeId).IsUnique();
e.HasIndex(x => x.MemberBioId);
e.HasIndex(x => x.Ticker);
e.HasIndex(x => x.DetailsFetched);
e.Property(x => x.TradeId).HasMaxLength(20);
e.Property(x => x.MemberBioId).HasMaxLength(20);
e.Property(x => x.IssuerName).HasMaxLength(500);
e.Property(x => x.IssuerId).HasMaxLength(20);
e.Property(x => x.Ticker).HasMaxLength(20);
e.Property(x => x.TradeType).HasMaxLength(50);
e.Property(x => x.Chamber).HasMaxLength(20);
e.Property(x => x.Owner).HasMaxLength(50);
e.Property(x => x.Value).HasPrecision(18, 2);
e.Property(x => x.DetailUrl).HasMaxLength(500);
});
}
}