fix(dir): 🐛 capture new ViewUrlId in Change using hi/lo to predetermine id

This commit is contained in:
Collin M. Barrett 2021-12-05 11:58:27 -06:00
parent ccb7ca3c03
commit 721926c8b5
10 changed files with 38 additions and 208 deletions

View file

@ -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<long>("Id")
@ -676,7 +680,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder)
.HasColumnType("bigint")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property<long>("Id"));
NpgsqlPropertyBuilderExtensions.UseHiLo(b1.Property<long>("Id"), "EntityFrameworkHiLoSequence-FilterListViewUrl");
b1.Property<short>("Primariness")
.ValueGeneratedOnAdd()

View file

@ -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<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
id = table.Column<long>(type: "bigint", nullable: false),
filter_list_id = table.Column<long>(type: "bigint", nullable: false),
segment_number = table.Column<short>(type: "smallint", nullable: false, defaultValue: (short)1),
primariness = table.Column<short>(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");
}
}
}

View file

@ -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<long>("Id")
@ -66050,7 +66054,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder)
.HasColumnType("bigint")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property<long>("Id"));
NpgsqlPropertyBuilderExtensions.UseHiLo(b1.Property<long>("Id"), "EntityFrameworkHiLoSequence-FilterListViewUrl");
b1.Property<short>("Primariness")
.ValueGeneratedOnAdd()

View file

@ -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<long>("Id")
@ -66048,7 +66052,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.HasColumnType("bigint")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property<long>("Id"));
NpgsqlPropertyBuilderExtensions.UseHiLo(b1.Property<long>("Id"), "EntityFrameworkHiLoSequence-FilterListViewUrl");
b1.Property<short>("Primariness")
.ValueGeneratedOnAdd()

View file

@ -52,6 +52,8 @@ public virtual void Configure(EntityTypeBuilder<FilterList> 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)

View file

@ -37,6 +37,9 @@ public override Task<int> 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);

View file

@ -53,6 +53,8 @@ public override void Configure(EntityTypeBuilder<FilterList> 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)

View file

@ -1,79 +1,8 @@
namespace FilterLists.SharedKernel.Domain.SeedWork;
/// <summary>
/// https://github.com/vkhorikov/CSharpFunctionalExtensions/blob/master/CSharpFunctionalExtensions/Entity/Entity.cs
/// </summary>
public abstract class Entity<TId>
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<TId> 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<TId>? a, Entity<TId>? 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<TId>? a, Entity<TId>? b)
{
return !(a == b);
}
public override int GetHashCode()
{
return (ValueObject.GetUnproxiedType(this)?.ToString() + Id).GetHashCode();
}
}
public abstract class Entity : Entity<long>
{
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).
}

View file

@ -1,132 +1,7 @@
namespace FilterLists.SharedKernel.Domain.SeedWork;
/// <summary>
/// https://github.com/vkhorikov/CSharpFunctionalExtensions/blob/master/CSharpFunctionalExtensions/ValueObject/ValueObject.cs
/// </summary>
[Serializable]
public abstract class ValueObject : IComparable, IComparable<ValueObject>
// 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<object?>();
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<object?> 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;
}
}