diff --git a/GenericOutbox/DataAccess/Configuration/OutboxEntityConfiguration.cs b/GenericOutbox/DataAccess/Configuration/OutboxEntityConfiguration.cs index 57150b2..b211740 100644 --- a/GenericOutbox/DataAccess/Configuration/OutboxEntityConfiguration.cs +++ b/GenericOutbox/DataAccess/Configuration/OutboxEntityConfiguration.cs @@ -9,12 +9,6 @@ public class OutboxEntityConfiguration : IEntityTypeConfiguration public void Configure(EntityTypeBuilder builder) { builder.HasKey(x => x.Id); - //builder.HasIndex(x => x.ParentId).IsUnique(); todo: nullability problem - - builder.HasOne(x => x.Parent) - .WithOne() - .HasForeignKey(x => x.ParentId) - .IsRequired(false); builder.HasIndex(x => x.Version); builder.HasIndex(x => x.HandlerLock); @@ -30,4 +24,4 @@ public void Configure(EntityTypeBuilder builder) builder.HasKey(x => x.Id); builder.HasIndex(x => x.ScopeId); } -} \ No newline at end of file +} diff --git a/GenericOutbox/DataAccess/Entities/OutboxEntity.cs b/GenericOutbox/DataAccess/Entities/OutboxEntity.cs index 5a456c2..ea687c2 100644 --- a/GenericOutbox/DataAccess/Entities/OutboxEntity.cs +++ b/GenericOutbox/DataAccess/Entities/OutboxEntity.cs @@ -6,9 +6,8 @@ namespace GenericOutbox.DataAccess.Entities; public class OutboxEntity { public int Id { get; set; } - public Guid? Lock { get; set; } + public Guid Lock { get; set; } public Guid? HandlerLock { get; set; } - public int? ParentId { get; set; } public Guid ScopeId { get; set; } public string Action { get; set; } public byte[] Payload { get; set; } @@ -21,7 +20,5 @@ public class OutboxEntity public string? Metadata { get; set; } - public virtual OutboxEntity Parent { get; set; } - public string PayloadString => Encoding.UTF8.GetString(Payload); -} \ No newline at end of file +} diff --git a/GenericOutbox/DataAccess/Services/OutboxDataAccess.cs b/GenericOutbox/DataAccess/Services/OutboxDataAccess.cs index 2928746..4e1ddf3 100644 --- a/GenericOutbox/DataAccess/Services/OutboxDataAccess.cs +++ b/GenericOutbox/DataAccess/Services/OutboxDataAccess.cs @@ -15,7 +15,7 @@ public class OutboxDataAccess(TDbContext dbContext, OutboxOptions ou OutboxRecordStatus.Completed }; - private int _rollingOutboxQueryType = 0; //0 = non-locked, 1 = locked + private int _rollingOutboxQueryType = 0; public async Task CommitExecutionResult(OutboxEntity outboxEntity, ExecutionResult executionResult) { @@ -24,11 +24,13 @@ public async Task CommitExecutionResult(OutboxEntity outboxEntity, ExecutionResu await dbContext .Set() .Where(x => x.Id == outboxEntity.Id) - .ExecuteUpdateAsync(s => s.SetProperty(r => r.RetriesCount, r => r.RetriesCount + 1) - .SetProperty(r => r.Status, executionResult.Status) - .SetProperty(r => r.RetryTimeoutUtc, executionResult.RetryTimeoutUtc) - .SetProperty(r => r.LastUpdatedUtc, now) - .SetProperty(r => r.HandlerLock, (Guid?)null) + .ExecuteUpdateAsync( + s => s + .SetProperty(r => r.RetriesCount, r => r.RetriesCount + 1) + .SetProperty(r => r.Status, executionResult.Status) + .SetProperty(r => r.RetryTimeoutUtc, executionResult.RetryTimeoutUtc) + .SetProperty(r => r.LastUpdatedUtc, now) + .SetProperty(r => r.HandlerLock, (Guid?)null) ); } @@ -38,17 +40,15 @@ public async Task GetOutboxRecords(int maxCount) var recordsToUpdateCount = _rollingOutboxQueryType switch { - 0 => await ReserveNonLockedRetryOutboxRecords(lockId, maxCount), - 1 => await ReserveNonLockedOutboxRecords(lockId, maxCount), - 2 => await ReserveLockedRetryOutboxRecords(lockId, maxCount), - 3 => await ReserveLockedOutboxRecords(lockId, maxCount), + 0 => await ReserveRetryOutboxRecords(lockId, maxCount), + 1 => await ReserveOutboxRecords(lockId, maxCount), _ => await ReserveStuckInProgressRecords(lockId, maxCount) }; - var stuckInProgress = _rollingOutboxQueryType == 4; + var stuckInProgress = _rollingOutboxQueryType == 2; if (recordsToUpdateCount < maxCount) - _rollingOutboxQueryType = (_rollingOutboxQueryType + 1) % 5; + _rollingOutboxQueryType = (_rollingOutboxQueryType + 1) % 3; if (recordsToUpdateCount == 0) return Array.Empty(); @@ -64,39 +64,39 @@ public async Task GetOutboxRecords(int maxCount) .ToArray(); } - private async Task ReserveLockedOutboxRecords(Guid handlerLockId, int maxCount) + private async Task ReserveOutboxRecords(Guid handlerLockId, int maxCount) { var now = DateTime.UtcNow; return await dbContext .Set() - .Where(r => - r.HandlerLock == null - && r.Status == OutboxRecordStatus.ReadyToExecute - && dbContext - .Set() - .Where(x => x.Lock != null - && !dbContext.Set().Any(y => - y.Lock == x.Lock && !s_unlockedStatuses.Contains(y.Status)) - // Note: Using explicit subquery instead of x.Parent.Status navigation property - // because EF Core 10 ExecuteUpdateAsync cannot properly translate queries with navigation properties - && (x.ParentId == null || dbContext.Set().Any(p => - p.Id == x.ParentId && p.Status == OutboxRecordStatus.Completed)) - && x.Status == OutboxRecordStatus.ReadyToExecute - && x.Version == outboxOptions.Version - && x.HandlerLock == null) - .GroupBy(x => x.Lock) - .Select(x => x.OrderBy(x => x.Id).FirstOrDefault().Id) - .OrderBy(x => x) - .Take(maxCount) - .Contains(r.Id)) - .ExecuteUpdateAsync(s => s.SetProperty(r => r.Status, OutboxRecordStatus.InProgress) - .SetProperty(r => r.HandlerLock, handlerLockId) - .SetProperty(r => r.LastUpdatedUtc, now) + .Where( + r => + r.HandlerLock == null + && r.Status == OutboxRecordStatus.ReadyToExecute + && dbContext + .Set() + .Where( + x => !dbContext + .Set() + .Any(y => y.Lock == x.Lock && !s_unlockedStatuses.Contains(y.Status)) + && x.Status == OutboxRecordStatus.ReadyToExecute + && x.Version == outboxOptions.Version + && x.HandlerLock == null) + .GroupBy(x => x.Lock) + .Select(x => x.OrderBy(x => x.Id).FirstOrDefault().Id) + .OrderBy(x => x) + .Take(maxCount) + .Contains(r.Id)) + .ExecuteUpdateAsync( + s => s + .SetProperty(r => r.Status, OutboxRecordStatus.InProgress) + .SetProperty(r => r.HandlerLock, handlerLockId) + .SetProperty(r => r.LastUpdatedUtc, now) ); } - private async Task ReserveLockedRetryOutboxRecords(Guid handlerLockId, int maxCount) + private async Task ReserveRetryOutboxRecords(Guid handlerLockId, int maxCount) { var now = DateTime.UtcNow; @@ -109,8 +109,7 @@ private async Task ReserveLockedRetryOutboxRecords(Guid handlerLockId, int && dbContext .Set() .Where( - x => x.Lock != null - && !dbContext.Set().Any(y => y.Lock == x.Lock && !s_unlockedStatuses.Contains(y.Status)) + x => !dbContext.Set().Any(y => y.Lock == x.Lock && !s_unlockedStatuses.Contains(y.Status)) && x.RetryTimeoutUtc < now && x.Version == outboxOptions.Version && x.HandlerLock == null) @@ -119,51 +118,11 @@ private async Task ReserveLockedRetryOutboxRecords(Guid handlerLockId, int .OrderBy(x => x) .Take(maxCount) .Contains(r.Id)) - .ExecuteUpdateAsync(s => s.SetProperty(r => r.Status, OutboxRecordStatus.InProgress) - .SetProperty(r => r.HandlerLock, handlerLockId) - .SetProperty(r => r.LastUpdatedUtc, now) - ); - } - - private async Task ReserveNonLockedOutboxRecords(Guid handlerLockId, int maxCount) - { - var now = DateTime.UtcNow; - - return await dbContext - .Set() - .Where(x => - x.RetryTimeoutUtc < now - && x.Version == outboxOptions.Version - && x.HandlerLock == null - && x.Lock == null) - .OrderBy(x => x.Id) - .Take(maxCount) - .ExecuteUpdateAsync(s => s.SetProperty(r => r.Status, OutboxRecordStatus.InProgress) - .SetProperty(r => r.HandlerLock, handlerLockId) - .SetProperty(r => r.LastUpdatedUtc, now) - ); - } - - private async Task ReserveNonLockedRetryOutboxRecords(Guid handlerLockId, int maxCount) - { - var now = DateTime.UtcNow; - - return await dbContext - .Set() - .Where( - // Note: Using explicit subquery instead of x.Parent.Status navigation property - // because EF Core 10 ExecuteUpdateAsync cannot properly translate queries with navigation properties - x => (x.ParentId == null || dbContext.Set() - .Any(p => p.Id == x.ParentId && p.Status == OutboxRecordStatus.Completed)) - && x.Status == OutboxRecordStatus.ReadyToExecute - && x.Version == outboxOptions.Version - && x.HandlerLock == null - && x.Lock == null) - .OrderBy(x => x.Id) - .Take(maxCount) - .ExecuteUpdateAsync(s => s.SetProperty(r => r.Status, OutboxRecordStatus.InProgress) - .SetProperty(r => r.HandlerLock, handlerLockId) - .SetProperty(r => r.LastUpdatedUtc, now) + .ExecuteUpdateAsync( + s => s + .SetProperty(r => r.Status, OutboxRecordStatus.InProgress) + .SetProperty(r => r.HandlerLock, handlerLockId) + .SetProperty(r => r.LastUpdatedUtc, now) ); } @@ -183,9 +142,10 @@ private async Task ReserveStuckInProgressRecords(Guid handlerLockId, int ma && r.LastUpdatedUtc < stuckCutoff) .OrderBy(x => x.Id) .Take(maxCount) - .ExecuteUpdateAsync(s => s.SetProperty(r => r.HandlerLock, handlerLockId) - .SetProperty(r => r.LastUpdatedUtc, now) + .ExecuteUpdateAsync( + s => s + .SetProperty(r => r.HandlerLock, handlerLockId) + .SetProperty(r => r.LastUpdatedUtc, now) ); - ; } -} \ No newline at end of file +} diff --git a/GenericOutbox/OutboxCreatorContext.cs b/GenericOutbox/OutboxCreatorContext.cs index 757e7f1..d137191 100644 --- a/GenericOutbox/OutboxCreatorContext.cs +++ b/GenericOutbox/OutboxCreatorContext.cs @@ -13,8 +13,7 @@ class OutboxCreatorContext : IOutboxCreatorContext where TDbContext private readonly LockClearer _lockClearer; - private int? _previousStepId; - private Guid? _lock; + private Guid _lock; private readonly Guid _scopeId; private readonly string _metadata; @@ -23,29 +22,28 @@ public OutboxCreatorContext(TDbContext dbContext, ISerializer serializer, Outbox _dbContext = dbContext; _serializer = serializer; _outboxOptions = outboxOptions; - + var metadataProviderService = serviceProvider.GetService(typeof(IOutboxMetadataProvider)); if (metadataProviderService != null) { _metadata = ((IOutboxMetadataProvider)metadataProviderService).GetMetadata(); } - _lockClearer = new LockClearer(this); + _lock = Guid.NewGuid(); + _lockClearer = new LockClearer(this, _lock); _scopeId = Guid.NewGuid(); } public void CreateOutboxRecord(string action, T model) { - var newRecord = CreateOutboxRecordInternal(action, model); + CreateOutboxRecordInternal(action, model); _dbContext.SaveChanges(); - _previousStepId = newRecord.Id; } public async Task CreateOutboxRecordAsync(string action, T model) { - var newRecord = CreateOutboxRecordInternal(action, model); + CreateOutboxRecordInternal(action, model); await _dbContext.SaveChangesAsync(); - _previousStepId = newRecord.Id; } public IDisposable Lock(string entityName, T entityId) @@ -57,6 +55,9 @@ public IDisposable Lock(string entityName, T entityId) public IDisposable Lock(Guid lockGuid) { + if (_lockClearer.IsLocked) + throw new InvalidOperationException("Lock is already taken."); + _lock = lockGuid; return _lockClearer; } @@ -70,7 +71,6 @@ private OutboxEntity CreateOutboxRecordInternal(string action, T model) Action = action, ScopeId = _scopeId, Payload = _serializer.Serialize(model), - ParentId = _previousStepId, Version = _outboxOptions.Version, Lock = _lock, LastUpdatedUtc = utcNow, @@ -86,15 +86,18 @@ private OutboxEntity CreateOutboxRecordInternal(string action, T model) class LockClearer : IDisposable { private readonly OutboxCreatorContext _outboxCreatorContext; + private readonly Guid _defaultLock; + public bool IsLocked => _defaultLock != _outboxCreatorContext._lock; - public LockClearer(OutboxCreatorContext outboxCreatorContext) + public LockClearer(OutboxCreatorContext outboxCreatorContext, Guid defaultLock) { _outboxCreatorContext = outboxCreatorContext; + _defaultLock = defaultLock; } public void Dispose() { - _outboxCreatorContext._lock = null; + _outboxCreatorContext._lock = _defaultLock; } } -} \ No newline at end of file +} diff --git a/GenericOutbox/OutboxDispatcherHostedService.cs b/GenericOutbox/OutboxDispatcherHostedService.cs index 19d3bb0..c53b9db 100644 --- a/GenericOutbox/OutboxDispatcherHostedService.cs +++ b/GenericOutbox/OutboxDispatcherHostedService.cs @@ -179,4 +179,4 @@ private static ExecutionResult GetRetryStrategyResolution(ILogger +using System; +using IntegrationTests; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace IntegrationTests.Migrations +{ + [DbContext(typeof(IntegrationTestsDbContext))] + [Migration("20260111221535_RemoveParent")] + partial class RemoveParent + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.1"); + + modelBuilder.Entity("GenericOutbox.DataAccess.Entities.OutboxDataEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Data") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ScopeId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ScopeId"); + + b.ToTable("OutboxDataEntity"); + }); + + modelBuilder.Entity("GenericOutbox.DataAccess.Entities.OutboxEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedUtc") + .HasColumnType("TEXT"); + + b.Property("HandlerLock") + .HasColumnType("TEXT"); + + b.Property("LastUpdatedUtc") + .HasColumnType("TEXT"); + + b.Property("Lock") + .HasColumnType("TEXT"); + + b.Property("Metadata") + .HasColumnType("TEXT"); + + b.Property("Payload") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property("RetriesCount") + .HasColumnType("INTEGER"); + + b.Property("RetryTimeoutUtc") + .HasColumnType("TEXT"); + + b.Property("ScopeId") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Version") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("HandlerLock"); + + b.HasIndex("Lock"); + + b.HasIndex("ScopeId"); + + b.HasIndex("Version"); + + b.ToTable("OutboxEntity"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Tests/IntegrationTests/Migrations/20260111221535_RemoveParent.cs b/Tests/IntegrationTests/Migrations/20260111221535_RemoveParent.cs new file mode 100644 index 0000000..92595ad --- /dev/null +++ b/Tests/IntegrationTests/Migrations/20260111221535_RemoveParent.cs @@ -0,0 +1,68 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace IntegrationTests.Migrations +{ + /// + public partial class RemoveParent : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_OutboxEntity_OutboxEntity_ParentId", + table: "OutboxEntity"); + + migrationBuilder.DropIndex( + name: "IX_OutboxEntity_ParentId", + table: "OutboxEntity"); + + migrationBuilder.DropColumn( + name: "ParentId", + table: "OutboxEntity"); + + migrationBuilder.AlterColumn( + name: "Lock", + table: "OutboxEntity", + type: "TEXT", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "TEXT", + oldNullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Lock", + table: "OutboxEntity", + type: "TEXT", + nullable: true, + oldClrType: typeof(Guid), + oldType: "TEXT"); + + migrationBuilder.AddColumn( + name: "ParentId", + table: "OutboxEntity", + type: "INTEGER", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_OutboxEntity_ParentId", + table: "OutboxEntity", + column: "ParentId", + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_OutboxEntity_OutboxEntity_ParentId", + table: "OutboxEntity", + column: "ParentId", + principalTable: "OutboxEntity", + principalColumn: "Id"); + } + } +} diff --git a/Tests/IntegrationTests/Migrations/IntegrationTestsDbContextModelSnapshot.cs b/Tests/IntegrationTests/Migrations/IntegrationTestsDbContextModelSnapshot.cs index d2ff1a0..9ae0785 100644 --- a/Tests/IntegrationTests/Migrations/IntegrationTestsDbContextModelSnapshot.cs +++ b/Tests/IntegrationTests/Migrations/IntegrationTestsDbContextModelSnapshot.cs @@ -15,7 +15,7 @@ partial class IntegrationTestsDbContextModelSnapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "7.0.5"); + modelBuilder.HasAnnotation("ProductVersion", "10.0.1"); modelBuilder.Entity("GenericOutbox.DataAccess.Entities.OutboxDataEntity", b => { @@ -60,15 +60,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("LastUpdatedUtc") .HasColumnType("TEXT"); - b.Property("Lock") + b.Property("Lock") .HasColumnType("TEXT"); b.Property("Metadata") .HasColumnType("TEXT"); - b.Property("ParentId") - .HasColumnType("INTEGER"); - b.Property("Payload") .IsRequired() .HasColumnType("BLOB"); @@ -95,24 +92,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Lock"); - b.HasIndex("ParentId") - .IsUnique(); - b.HasIndex("ScopeId"); b.HasIndex("Version"); b.ToTable("OutboxEntity"); }); - - modelBuilder.Entity("GenericOutbox.DataAccess.Entities.OutboxEntity", b => - { - b.HasOne("GenericOutbox.DataAccess.Entities.OutboxEntity", "Parent") - .WithOne() - .HasForeignKey("GenericOutbox.DataAccess.Entities.OutboxEntity", "ParentId"); - - b.Navigation("Parent"); - }); #pragma warning restore 612, 618 } } diff --git a/Tests/PersonService/Migrations/20260111221643_RemoveParent.Designer.cs b/Tests/PersonService/Migrations/20260111221643_RemoveParent.Designer.cs new file mode 100644 index 0000000..041ae4c --- /dev/null +++ b/Tests/PersonService/Migrations/20260111221643_RemoveParent.Designer.cs @@ -0,0 +1,118 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PersonService.DataAccess; + +#nullable disable + +namespace PersonService.Migrations +{ + [DbContext(typeof(PersonServiceDbContext))] + [Migration("20260111221643_RemoveParent")] + partial class RemoveParent + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.1"); + + modelBuilder.Entity("GenericOutbox.DataAccess.Entities.OutboxDataEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Data") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ScopeId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ScopeId"); + + b.ToTable("OutboxDataEntity"); + }); + + modelBuilder.Entity("GenericOutbox.DataAccess.Entities.OutboxEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Action") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("CreatedUtc") + .HasColumnType("TEXT"); + + b.Property("HandlerLock") + .HasColumnType("TEXT"); + + b.Property("LastUpdatedUtc") + .HasColumnType("TEXT"); + + b.Property("Lock") + .HasColumnType("TEXT"); + + b.Property("Metadata") + .HasColumnType("TEXT"); + + b.Property("Payload") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property("RetriesCount") + .HasColumnType("INTEGER"); + + b.Property("RetryTimeoutUtc") + .HasColumnType("TEXT"); + + b.Property("ScopeId") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Version") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("HandlerLock"); + + b.HasIndex("Lock"); + + b.HasIndex("ScopeId"); + + b.HasIndex("Version"); + + b.ToTable("OutboxEntity"); + }); + + modelBuilder.Entity("PersonService.DataAccess.Entities.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.ToTable("Persons"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Tests/PersonService/Migrations/20260111221643_RemoveParent.cs b/Tests/PersonService/Migrations/20260111221643_RemoveParent.cs new file mode 100644 index 0000000..70ad2ee --- /dev/null +++ b/Tests/PersonService/Migrations/20260111221643_RemoveParent.cs @@ -0,0 +1,68 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PersonService.Migrations +{ + /// + public partial class RemoveParent : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_OutboxEntity_OutboxEntity_ParentId", + table: "OutboxEntity"); + + migrationBuilder.DropIndex( + name: "IX_OutboxEntity_ParentId", + table: "OutboxEntity"); + + migrationBuilder.DropColumn( + name: "ParentId", + table: "OutboxEntity"); + + migrationBuilder.AlterColumn( + name: "Lock", + table: "OutboxEntity", + type: "TEXT", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "TEXT", + oldNullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Lock", + table: "OutboxEntity", + type: "TEXT", + nullable: true, + oldClrType: typeof(Guid), + oldType: "TEXT"); + + migrationBuilder.AddColumn( + name: "ParentId", + table: "OutboxEntity", + type: "INTEGER", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_OutboxEntity_ParentId", + table: "OutboxEntity", + column: "ParentId", + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_OutboxEntity_OutboxEntity_ParentId", + table: "OutboxEntity", + column: "ParentId", + principalTable: "OutboxEntity", + principalColumn: "Id"); + } + } +} diff --git a/Tests/PersonService/Migrations/PersonServiceDbContextModelSnapshot.cs b/Tests/PersonService/Migrations/PersonServiceDbContextModelSnapshot.cs index 4834e38..e13e948 100644 --- a/Tests/PersonService/Migrations/PersonServiceDbContextModelSnapshot.cs +++ b/Tests/PersonService/Migrations/PersonServiceDbContextModelSnapshot.cs @@ -15,7 +15,7 @@ partial class PersonServiceDbContextModelSnapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "7.0.5"); + modelBuilder.HasAnnotation("ProductVersion", "10.0.1"); modelBuilder.Entity("GenericOutbox.DataAccess.Entities.OutboxDataEntity", b => { @@ -60,15 +60,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("LastUpdatedUtc") .HasColumnType("TEXT"); - b.Property("Lock") + b.Property("Lock") .HasColumnType("TEXT"); b.Property("Metadata") .HasColumnType("TEXT"); - b.Property("ParentId") - .HasColumnType("INTEGER"); - b.Property("Payload") .IsRequired() .HasColumnType("BLOB"); @@ -95,9 +92,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Lock"); - b.HasIndex("ParentId") - .IsUnique(); - b.HasIndex("ScopeId"); b.HasIndex("Version"); @@ -115,15 +109,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Persons"); }); - - modelBuilder.Entity("GenericOutbox.DataAccess.Entities.OutboxEntity", b => - { - b.HasOne("GenericOutbox.DataAccess.Entities.OutboxEntity", "Parent") - .WithOne() - .HasForeignKey("GenericOutbox.DataAccess.Entities.OutboxEntity", "ParentId"); - - b.Navigation("Parent"); - }); #pragma warning restore 612, 618 } } diff --git a/dotnet-tools.json b/dotnet-tools.json new file mode 100644 index 0000000..fbb7b76 --- /dev/null +++ b/dotnet-tools.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "10.0.1", + "commands": [ + "dotnet-ef" + ], + "rollForward": false + } + } +} \ No newline at end of file