using BlueLaminate.EFCore.Entities; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace BlueLaminate.EFCore.Configurations; public class SkinConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder entity) { // Nullable: null means the catalogue gives no wear range (e.g. vanilla // knives), distinct from a genuine 0.0–1.0 range. entity.Property(e => e.FloatMin).HasColumnType("numeric(10,9)"); entity.Property(e => e.FloatMax).HasColumnType("numeric(10,9)"); entity.Property(e => e.TrueFloat) .HasComputedColumnSql("float_min = 0.0 AND float_max = 1.0", stored: true); entity.HasIndex(e => e.TrueFloat); // Slug is the natural key the sync upserts against. entity.HasIndex(e => e.Slug).IsUnique(); // Market listings join back to a skin by (def_index, paint_index). Unique // among populated rows; filtered so the many catalogue rows that predate // these columns (null) don't collide. Postgres treats nulls as distinct // anyway, but the filter makes the intent explicit and the index smaller. entity.HasIndex(e => new { e.DefIndex, e.PaintIndex }) .IsUnique() .HasFilter("def_index IS NOT NULL AND paint_index IS NOT NULL"); // Per-site "last swept" checkpoints live in skin_sweeps (one row per site); // see SkinSweepConfiguration for the indexes that order them. entity.HasOne(e => e.Weapon) .WithMany(w => w.Skins) .HasForeignKey(e => e.WeaponId); // A skin can come from many collections and containers, and each of those // holds many skins. entity.HasMany(e => e.Collections) .WithMany(c => c.Skins) .UsingEntity(join => join.ToTable("skin_collections")); } }