diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizer.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizer.cs index 27260bcee7..58582912de 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizer.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizer.cs @@ -13,8 +13,8 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed; public abstract class ExternalEntitySynchronizer : ExternalEntitySynchronizer - where TEntity : class, IEntity, IHasEntityVersion - where TExternalEntityEto : EntityEto, IHasEntityVersion + where TEntity : class, IEntity + where TExternalEntityEto : EntityEto { private readonly IRepository _repository; @@ -48,8 +48,8 @@ public abstract class ExternalEntitySynchronizer : IDistributedEventHandler>, IDistributedEventHandler>, IUnitOfWorkEnabled - where TEntity : class, IEntity, IHasEntityVersion - where TExternalEntityEto : EntityEto, IHasEntityVersion + where TEntity : class, IEntity + where TExternalEntityEto : EntityEto { protected IObjectMapper ObjectMapper { get; } private readonly IRepository _repository; @@ -108,17 +108,26 @@ public abstract class ExternalEntitySynchronizer : if (localEntity == null) { localEntity = await MapToEntityAsync(eto); - ObjectHelper.TrySetProperty(localEntity, x => x.EntityVersion, () => eto.EntityVersion); + + if (localEntity is IHasEntityVersion versionedLocalEntity && eto is IHasEntityVersion versionedEto) + { + ObjectHelper.TrySetProperty(versionedLocalEntity, x => x.EntityVersion, + () => versionedEto.EntityVersion); + } await _repository.InsertAsync(localEntity, true); } else { - // The version will auto-increment by one when the repository updates the entity. - var entityVersion = eto.EntityVersion - 1; - await MapToEntityAsync(eto, localEntity); - ObjectHelper.TrySetProperty(localEntity, x => x.EntityVersion, () => entityVersion); + + if (localEntity is IHasEntityVersion versionedLocalEntity && eto is IHasEntityVersion versionedEto) + { + // The version will auto-increment by one when the repository updates the entity. + var entityVersion = versionedEto.EntityVersion - 1; + + ObjectHelper.TrySetProperty(versionedLocalEntity, x => x.EntityVersion, () => entityVersion); + } await _repository.UpdateAsync(localEntity, true); } @@ -156,6 +165,11 @@ public abstract class ExternalEntitySynchronizer : protected virtual Task IsEtoNewerAsync(TExternalEntityEto eto, [CanBeNull] TEntity localEntity) { - return Task.FromResult(localEntity == null || eto.EntityVersion > localEntity.EntityVersion); + if (localEntity is IHasEntityVersion versionedLocalEntity && eto is IHasEntityVersion versionedEto) + { + return Task.FromResult(versionedEto.EntityVersion > versionedLocalEntity.EntityVersion); + } + + return Task.FromResult(true); } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/ExternalEntitySynchronizer_Tests.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/ExternalEntitySynchronizer_Tests.cs index c32ea0e513..65ee9bcf9f 100644 --- a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/ExternalEntitySynchronizer_Tests.cs +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/ExternalEntitySynchronizer_Tests.cs @@ -7,6 +7,8 @@ using Shouldly; using Volo.Abp.Autofac; using Volo.Abp.AutoMapper; using Volo.Abp.Data; +using Volo.Abp.Domain.Entities.Events.Distributed.ExternalEntitySynchronizers.WithEntityVersion; +using Volo.Abp.Domain.Entities.Events.Distributed.ExternalEntitySynchronizers.WithoutEntityVersion; using Volo.Abp.Domain.Repositories; using Volo.Abp.MemoryDb; using Volo.Abp.Modularity; @@ -20,6 +22,84 @@ public class ExternalEntitySynchronizer_Tests : AbpIntegratedTest(); + using var uow = uowManager.Begin(); + + var authorSynchronizer = GetRequiredService(); + var repository = GetRequiredService>(); + + (await repository.FindAsync(authorId)).ShouldBeNull(); + + var remoteAuthorEto = new RemoteAuthorEto { KeysAsString = authorId.ToString(), Name = "New" }; + + await authorSynchronizer.HandleEventAsync(new EntityCreatedEto(remoteAuthorEto)); + + var author = await repository.FindAsync(authorId); + author.ShouldNotBeNull(); + author.Name.ShouldBe("New"); + } + + [Fact] + public async Task Should_Handle_Entity_Update_Event() + { + var authorId = Guid.NewGuid(); + + var uowManager = GetRequiredService(); + using var uow = uowManager.Begin(); + + var authorSynchronizer = GetRequiredService(); + var repository = GetRequiredService>(); + + await repository.InsertAsync(new Author(authorId, "Old"), true); + + var author = await repository.FindAsync(authorId); + author.ShouldNotBeNull(); + author.Id.ShouldBe(authorId); + author.Name.ShouldBe("Old"); + + var remoteAuthorEto = new RemoteAuthorEto { KeysAsString = authorId.ToString(), Name = "New" }; + + await authorSynchronizer.HandleEventAsync(new EntityUpdatedEto(remoteAuthorEto)); + + author = await repository.FindAsync(authorId); + author.ShouldNotBeNull(); + author.Id.ShouldBe(authorId); + author.Name.ShouldBe("New"); + } + + [Fact] + public async Task Should_Handle_Entity_Deleted_Event() + { + var authorId = Guid.NewGuid(); + + var uowManager = GetRequiredService(); + using var uow = uowManager.Begin(); + + var authorSynchronizer = GetRequiredService(); + var repository = GetRequiredService>(); + + await repository.InsertAsync(new Author(authorId, "Old"), true); + + var author = await repository.FindAsync(authorId); + author.ShouldNotBeNull(); + author.Id.ShouldBe(authorId); + author.Name.ShouldBe("Old"); + + var remoteAuthorEto = new RemoteAuthorEto { KeysAsString = authorId.ToString(), Name = "New" }; + + await authorSynchronizer.HandleEventAsync(new EntityDeletedEto(remoteAuthorEto)); + + (await repository.FindAsync(authorId)).ShouldBeNull(); + + await Should.NotThrowAsync(() => + authorSynchronizer.HandleEventAsync(new EntityDeletedEto(remoteAuthorEto))); + } + + [Fact] + public async Task Should_Handle_Versioned_Entity_Created_Event() { var bookId = Guid.NewGuid(); @@ -42,7 +122,7 @@ public class ExternalEntitySynchronizer_Tests : AbpIntegratedTest(); var repository = GetRequiredService>(); - (await repository.FindAsync(bookId)).ShouldBeNull(); + await repository.InsertAsync(new Book(bookId, 1), true); - var remoteBookEto = new RemoteBookEto { KeysAsString = bookId.ToString(), EntityVersion = 0, Sold = 1 }; + var book = await repository.FindAsync(bookId); + book.ShouldNotBeNull(); + book.Id.ShouldBe(bookId); + book.EntityVersion.ShouldBe(0); + + var remoteBookEto = new RemoteBookEto { KeysAsString = bookId.ToString(), EntityVersion = 0, Sold = 10 }; await bookSynchronizer.HandleEventAsync(new EntityUpdatedEto(remoteBookEto)); - var book = await repository.FindAsync(bookId); + book = await repository.FindAsync(bookId); book.ShouldNotBeNull(); book.EntityVersion.ShouldBe(0); book.Sold.ShouldBe(1); @@ -86,7 +171,7 @@ public class ExternalEntitySynchronizer_Tests : AbpIntegratedTest(); var repository = GetRequiredService>(); - await repository.InsertAsync(new Book(bookId, 1, 0), true); + await repository.InsertAsync(new Book(bookId, 1), true); var book = await repository.FindAsync(bookId); book.ShouldNotBeNull(); @@ -109,9 +194,8 @@ public class ExternalEntitySynchronizer_Tests : AbpIntegratedTest(remoteBookEto)); - - (await repository.FindAsync(bookId)).ShouldBeNull(); + await Should.NotThrowAsync(() => + bookSynchronizer.HandleEventAsync(new EntityDeletedEto(remoteBookEto))); } protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) @@ -153,7 +237,7 @@ public class ExternalEntitySynchronizer_Tests : AbpIntegratedTest GetEntityTypes() { - return new List { typeof(Book) }; + return new List { typeof(Book), typeof(Author) }; } } @@ -163,6 +247,8 @@ public class ExternalEntitySynchronizer_Tests : AbpIntegratedTest(MemberList.None) .ForMember(x => x.Id, options => options.MapFrom(x => Guid.Parse(x.KeysAsString))); + CreateMap(MemberList.None) + .ForMember(x => x.Id, options => options.MapFrom(x => Guid.Parse(x.KeysAsString))); } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/Book.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithEntityVersion/Book.cs similarity index 63% rename from framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/Book.cs rename to framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithEntityVersion/Book.cs index d263a03fe9..fd2facfe19 100644 --- a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/Book.cs +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithEntityVersion/Book.cs @@ -1,21 +1,22 @@ using System; +using System.Text.Json.Serialization; using Volo.Abp.Auditing; -namespace Volo.Abp.Domain.Entities.Events.Distributed.ExternalEntitySynchronizers; +namespace Volo.Abp.Domain.Entities.Events.Distributed.ExternalEntitySynchronizers.WithEntityVersion; public class Book : Entity, IHasEntityVersion { public virtual int Sold { get; set; } + [JsonInclude] // the memory DB repository requires this or a ctor arg public virtual int EntityVersion { get; protected set; } protected Book() { } - public Book(Guid id, int sold, int entityVersion) : base(id) + public Book(Guid id, int sold) : base(id) { Sold = sold; - EntityVersion = entityVersion; } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/BookSynchronizer.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithEntityVersion/BookSynchronizer.cs similarity index 92% rename from framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/BookSynchronizer.cs rename to framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithEntityVersion/BookSynchronizer.cs index 8470343b5d..94e49d571e 100644 --- a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/BookSynchronizer.cs +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithEntityVersion/BookSynchronizer.cs @@ -3,7 +3,7 @@ using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; using Volo.Abp.ObjectMapping; -namespace Volo.Abp.Domain.Entities.Events.Distributed.ExternalEntitySynchronizers; +namespace Volo.Abp.Domain.Entities.Events.Distributed.ExternalEntitySynchronizers.WithEntityVersion; public class BookSynchronizer : ExternalEntitySynchronizer, ITransientDependency { diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/RemoteBookEto.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithEntityVersion/RemoteBookEto.cs similarity index 86% rename from framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/RemoteBookEto.cs rename to framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithEntityVersion/RemoteBookEto.cs index 886bca36bc..26c0c13965 100644 --- a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/RemoteBookEto.cs +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithEntityVersion/RemoteBookEto.cs @@ -1,6 +1,6 @@ using Volo.Abp.Auditing; -namespace Volo.Abp.Domain.Entities.Events.Distributed.ExternalEntitySynchronizers; +namespace Volo.Abp.Domain.Entities.Events.Distributed.ExternalEntitySynchronizers.WithEntityVersion; public class RemoteBookEto : EntityEto, IHasEntityVersion { diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithoutEntityVersion/Author.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithoutEntityVersion/Author.cs new file mode 100644 index 0000000000..74627ab2ad --- /dev/null +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithoutEntityVersion/Author.cs @@ -0,0 +1,17 @@ +using System; + +namespace Volo.Abp.Domain.Entities.Events.Distributed.ExternalEntitySynchronizers.WithoutEntityVersion; + +public class Author : Entity +{ + public virtual string Name { get; set; } + + protected Author() + { + } + + public Author(Guid id, string name) : base(id) + { + Name = name; + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithoutEntityVersion/AuthorSynchronizer.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithoutEntityVersion/AuthorSynchronizer.cs new file mode 100644 index 0000000000..38d26fa6bf --- /dev/null +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithoutEntityVersion/AuthorSynchronizer.cs @@ -0,0 +1,14 @@ +using System; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.ObjectMapping; + +namespace Volo.Abp.Domain.Entities.Events.Distributed.ExternalEntitySynchronizers.WithoutEntityVersion; + +public class AuthorSynchronizer : ExternalEntitySynchronizer, ITransientDependency +{ + public AuthorSynchronizer(IObjectMapper objectMapper, IRepository repository) + : base(objectMapper, repository) + { + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithoutEntityVersion/RemoteAuthorEto.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithoutEntityVersion/RemoteAuthorEto.cs new file mode 100644 index 0000000000..dd3f10635b --- /dev/null +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/Events/Distributed/ExternalEntitySynchronizers/WithoutEntityVersion/RemoteAuthorEto.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.Domain.Entities.Events.Distributed.ExternalEntitySynchronizers.WithoutEntityVersion; + +public class RemoteAuthorEto : EntityEto +{ + public string Name { get; set; } +} \ No newline at end of file