using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; namespace Content.Server.Database { public sealed class PostgresServerDbContext : ServerDbContext { static PostgresServerDbContext() { AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); } public PostgresServerDbContext(DbContextOptions options) : base(options) { } protected override void OnConfiguring(DbContextOptionsBuilder options) { options.ReplaceService(); ((IDbContextOptionsBuilderInfrastructure) options).AddOrUpdateExtension(new SnakeCaseExtension()); options.ConfigureWarnings(x => { x.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning); #if DEBUG // for tests x.Ignore(CoreEventId.SensitiveDataLoggingEnabledWarning); #endif }); #if DEBUG options.EnableSensitiveDataLogging(); #endif } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // ReSharper disable once CommentTypo // ReSharper disable once StringLiteralTypo // Enforce that an address cannot be IPv6-mapped IPv4. // So that IPv4 addresses are consistent between separate-socket and dual-stack socket modes. modelBuilder.Entity() .HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); // ReSharper disable once StringLiteralTypo modelBuilder.Entity() .HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address"); modelBuilder.Entity() .HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); foreach(var entity in modelBuilder.Model.GetEntityTypes()) { foreach(var property in entity.GetProperties()) { if (property.FieldInfo?.FieldType == typeof(DateTime) || property.FieldInfo?.FieldType == typeof(DateTime?)) property.SetColumnType("timestamp with time zone"); } } } } }