diff --git a/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205154614_Initial.Designer.cs b/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205175314_Initial.Designer.cs similarity index 99% rename from services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205154614_Initial.Designer.cs rename to services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205175314_Initial.Designer.cs index d5c08cc1e..278e52677 100644 --- a/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205154614_Initial.Designer.cs +++ b/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205175314_Initial.Designer.cs @@ -14,7 +14,7 @@ namespace FilterLists.Directory.Infrastructure.Migrations.Migrations { [DbContext(typeof(QueryDbContext))] - [Migration("20211205154614_Initial")] + [Migration("20211205175314_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -27,6 +27,10 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "aggregate_type", new[] { "filter_list", "language", "license", "maintainer", "software", "syntax", "tag" }); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.HasSequence("EntityFrameworkHiLoSequence-FilterListViewUrl") + .StartsAt(3000L) + .IncrementsBy(3); + modelBuilder.Entity("FilterLists.Directory.Infrastructure.Persistence.Queries.Entities.Change", b => { b.Property("Id") @@ -676,7 +680,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("bigint") .HasColumnName("id"); - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + NpgsqlPropertyBuilderExtensions.UseHiLo(b1.Property("Id"), "EntityFrameworkHiLoSequence-FilterListViewUrl"); b1.Property("Primariness") .ValueGeneratedOnAdd() diff --git a/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205154614_Initial.cs b/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205175314_Initial.cs similarity index 98% rename from services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205154614_Initial.cs rename to services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205175314_Initial.cs index f03460883..bb7123ca2 100644 --- a/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205154614_Initial.cs +++ b/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205175314_Initial.cs @@ -15,6 +15,11 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.AlterDatabase() .Annotation("Npgsql:Enum:aggregate_type", "filter_list,language,license,maintainer,software,syntax,tag"); + migrationBuilder.CreateSequence( + name: "EntityFrameworkHiLoSequence-FilterListViewUrl", + startValue: 3000L, + incrementBy: 3); + migrationBuilder.CreateTable( name: "languages", columns: table => new @@ -355,8 +360,7 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "filter_list_view_urls", columns: table => new { - id = table.Column(type: "bigint", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + id = table.Column(type: "bigint", nullable: false), filter_list_id = table.Column(type: "bigint", nullable: false), segment_number = table.Column(type: "smallint", nullable: false, defaultValue: (short)1), primariness = table.Column(type: "smallint", nullable: false, defaultValue: (short)1), @@ -608,6 +612,9 @@ protected override void Down(MigrationBuilder migrationBuilder) migrationBuilder.DropTable( name: "licenses"); + + migrationBuilder.DropSequence( + name: "EntityFrameworkHiLoSequence-FilterListViewUrl"); } } } diff --git a/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205154639_SeedData.Designer.cs b/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205175350_SeedData.Designer.cs similarity index 99% rename from services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205154639_SeedData.Designer.cs rename to services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205175350_SeedData.Designer.cs index c57ad55a6..fcda319e5 100644 --- a/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205154639_SeedData.Designer.cs +++ b/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205175350_SeedData.Designer.cs @@ -14,7 +14,7 @@ namespace FilterLists.Directory.Infrastructure.Migrations.Migrations { [DbContext(typeof(QueryDbContext))] - [Migration("20211205154639_SeedData")] + [Migration("20211205175350_SeedData")] partial class SeedData { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -27,6 +27,10 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "aggregate_type", new[] { "filter_list", "language", "license", "maintainer", "software", "syntax", "tag" }); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.HasSequence("EntityFrameworkHiLoSequence-FilterListViewUrl") + .StartsAt(3000L) + .IncrementsBy(3); + modelBuilder.Entity("FilterLists.Directory.Infrastructure.Persistence.Queries.Entities.Change", b => { b.Property("Id") @@ -66050,7 +66054,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("bigint") .HasColumnName("id"); - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + NpgsqlPropertyBuilderExtensions.UseHiLo(b1.Property("Id"), "EntityFrameworkHiLoSequence-FilterListViewUrl"); b1.Property("Primariness") .ValueGeneratedOnAdd() diff --git a/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205154639_SeedData.cs b/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205175350_SeedData.cs similarity index 100% rename from services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205154639_SeedData.cs rename to services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/20211205175350_SeedData.cs diff --git a/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/QueryDbContextModelSnapshot.cs b/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/QueryDbContextModelSnapshot.cs index 13c267d3c..d19aa93b5 100644 --- a/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/QueryDbContextModelSnapshot.cs +++ b/services/Directory/FilterLists.Directory.Infrastructure.Migrations/Migrations/QueryDbContextModelSnapshot.cs @@ -25,6 +25,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "aggregate_type", new[] { "filter_list", "language", "license", "maintainer", "software", "syntax", "tag" }); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.HasSequence("EntityFrameworkHiLoSequence-FilterListViewUrl") + .StartsAt(3000L) + .IncrementsBy(3); + modelBuilder.Entity("FilterLists.Directory.Infrastructure.Persistence.Queries.Entities.Change", b => { b.Property("Id") @@ -66048,7 +66052,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("bigint") .HasColumnName("id"); - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + NpgsqlPropertyBuilderExtensions.UseHiLo(b1.Property("Id"), "EntityFrameworkHiLoSequence-FilterListViewUrl"); b1.Property("Primariness") .ValueGeneratedOnAdd() diff --git a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/FilterListTypeConfiguration.cs b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/FilterListTypeConfiguration.cs index 6419730e1..f0d97a86c 100644 --- a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/FilterListTypeConfiguration.cs +++ b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Commands/EntityTypeConfigurations/FilterListTypeConfiguration.cs @@ -52,6 +52,8 @@ public virtual void Configure(EntityTypeBuilder builder) b => { b.ToTable($"{nr.RewriteName(nameof(FilterListViewUrl))}s"); + b.Property(u => u.Id) + .UseHiLo($"EntityFrameworkHiLoSequence-{nameof(FilterListViewUrl)}"); b.Property(u => u.SegmentNumber) .HasDefaultValue(1); b.Property(u => u.Primariness) diff --git a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Queries/Context/QueryDbContext.cs b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Queries/Context/QueryDbContext.cs index 30dee42db..3fcefb0c7 100644 --- a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Queries/Context/QueryDbContext.cs +++ b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Queries/Context/QueryDbContext.cs @@ -37,6 +37,9 @@ public override Task SaveChangesAsync( protected override void OnModelCreating(ModelBuilder modelBuilder) { + modelBuilder.HasSequence($"EntityFrameworkHiLoSequence-{nameof(FilterListViewUrl)}") + .StartsAt(3000) + .IncrementsBy(3); modelBuilder.ApplyConfigurationsFromAssembly( GetType().Assembly, type => type.Namespace == typeof(FilterListTypeConfiguration).Namespace); diff --git a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Queries/Entities/FilterList.cs b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Queries/Entities/FilterList.cs index 808f6361f..3b9e7f1f7 100644 --- a/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Queries/Entities/FilterList.cs +++ b/services/Directory/FilterLists.Directory.Infrastructure/Persistence/Queries/Entities/FilterList.cs @@ -53,6 +53,8 @@ public override void Configure(EntityTypeBuilder builder) b => { b.ToTable($"{nr.RewriteName(nameof(FilterListViewUrl))}s"); + b.Property(u => u.Id) + .UseHiLo($"EntityFrameworkHiLoSequence-{nameof(FilterListViewUrl)}"); b.Property(u => u.SegmentNumber) .HasDefaultValue(1); b.Property(u => u.Primariness) diff --git a/services/SharedKernel/FilterLists.SharedKernel.Domain.SeedWork/Entity.cs b/services/SharedKernel/FilterLists.SharedKernel.Domain.SeedWork/Entity.cs index 61357e42f..d8bc11955 100644 --- a/services/SharedKernel/FilterLists.SharedKernel.Domain.SeedWork/Entity.cs +++ b/services/SharedKernel/FilterLists.SharedKernel.Domain.SeedWork/Entity.cs @@ -1,79 +1,8 @@ namespace FilterLists.SharedKernel.Domain.SeedWork; -/// -/// https://github.com/vkhorikov/CSharpFunctionalExtensions/blob/master/CSharpFunctionalExtensions/Entity/Entity.cs -/// -public abstract class Entity +public abstract class Entity { - protected Entity() { } + public long Id { get; protected init; } - protected Entity(TId id) - { - Id = id; - } - - public virtual TId Id { get; protected set; } = default!; - - public override bool Equals(object? obj) - { - if (obj is not Entity other) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - if (ValueObject.GetUnproxiedType(this) != ValueObject.GetUnproxiedType(other)) - { - return false; - } - - if (IsTransient() || other.IsTransient()) - { - return false; - } - - return Id is not null && Id.Equals(other.Id); - } - - private bool IsTransient() - { - return Id is null || Id.Equals(default(TId)); - } - - public static bool operator ==(Entity? a, Entity? b) - { - if (a is null && b is null) - { - return true; - } - - if (a is null || b is null) - { - return false; - } - - return a.Equals(b); - } - - public static bool operator !=(Entity? a, Entity? b) - { - return !(a == b); - } - - public override int GetHashCode() - { - return (ValueObject.GetUnproxiedType(this)?.ToString() + Id).GetHashCode(); - } -} - -public abstract class Entity : Entity -{ - protected Entity() { } - - protected Entity(long id) - : base(id) { } + // TODO: More fully implement (e.g., https://enterprisecraftsmanship.com/posts/entity-base-class/). Make sure equality doesn't break duplicate ViewUrls when adding newly created FilterList to DbContext (related to hi/lo and/or NRTs). } diff --git a/services/SharedKernel/FilterLists.SharedKernel.Domain.SeedWork/ValueObject.cs b/services/SharedKernel/FilterLists.SharedKernel.Domain.SeedWork/ValueObject.cs index 97f64cdca..fccada865 100644 --- a/services/SharedKernel/FilterLists.SharedKernel.Domain.SeedWork/ValueObject.cs +++ b/services/SharedKernel/FilterLists.SharedKernel.Domain.SeedWork/ValueObject.cs @@ -1,132 +1,7 @@ namespace FilterLists.SharedKernel.Domain.SeedWork; -/// -/// https://github.com/vkhorikov/CSharpFunctionalExtensions/blob/master/CSharpFunctionalExtensions/ValueObject/ValueObject.cs -/// -[Serializable] -public abstract class ValueObject : IComparable, IComparable +// TODO: implement value equality (e.g., https://enterprisecraftsmanship.com/posts/value-object-better-implementation/) +public abstract class ValueObject { - private int? _cachedHashCode; - - public virtual int CompareTo(object? obj) - { - var thisType = GetUnproxiedType(this); - var otherType = GetUnproxiedType(obj); - - if (thisType != otherType) - { - return string.CompareOrdinal(thisType?.ToString(), otherType?.ToString()); - } - - var other = (ValueObject?)obj; - - var components = GetEqualityComponents().ToArray(); - var otherComponents = other?.GetEqualityComponents().ToArray() ?? Array.Empty(); - - return components.Select((t, i) => CompareComponents(t, otherComponents[i])) - .FirstOrDefault(comparison => comparison != 0); - } - - public virtual int CompareTo(ValueObject? other) - { - return CompareTo(other as object); - } - protected abstract IEnumerable GetEqualityComponents(); - - public override bool Equals(object? obj) - { - if (obj == null) - { - return false; - } - - if (GetUnproxiedType(this) != GetUnproxiedType(obj)) - { - return false; - } - - var valueObject = (ValueObject)obj; - - return GetEqualityComponents() - .SequenceEqual(valueObject.GetEqualityComponents()); - } - - public override int GetHashCode() - { - _cachedHashCode ??= GetEqualityComponents() - .Aggregate( - 1, - (current, obj) => - { - unchecked - { - return (current * 23) + (obj?.GetHashCode() ?? 0); - } - }); - - return _cachedHashCode.Value; - } - - private static int CompareComponents(object? object1, object? object2) - { - if (object1 is null && object2 is null) - { - return 0; - } - - if (object1 is null) - { - return -1; - } - - if (object2 is null) - { - return 1; - } - - if (object1 is IComparable comparable1 && object2 is IComparable comparable2) - { - return comparable1.CompareTo(comparable2); - } - - return object1.Equals(object2) ? 0 : -1; - } - - public static bool operator ==(ValueObject? a, ValueObject? b) - { - if (a is null && b is null) - { - return true; - } - - if (a is null || b is null) - { - return false; - } - - return a.Equals(b); - } - - public static bool operator !=(ValueObject? a, ValueObject? b) - { - return !(a == b); - } - - internal static Type? GetUnproxiedType(object? obj) - { - const string EFCoreProxyPrefix = "Castle.Proxies."; - const string NHibernateProxyPostfix = "Proxy"; - - var type = obj?.GetType(); - var typeString = type?.ToString(); - - if (typeString is not null && - (typeString.Contains(EFCoreProxyPrefix) || typeString.EndsWith(NHibernateProxyPostfix))) - { - return type?.BaseType; - } - - return type; - } }