Make `ExternalEntitySynchronizer` support non-versioned entities

pull/14197/head
gdlcf88 3 years ago
parent fbf207ea07
commit ddd46a94dd

@ -13,8 +13,8 @@ namespace Volo.Abp.Domain.Entities.Events.Distributed;
public abstract class ExternalEntitySynchronizer<TEntity, TKey, TExternalEntityEto> :
ExternalEntitySynchronizer<TEntity, TExternalEntityEto>
where TEntity : class, IEntity<TKey>, IHasEntityVersion
where TExternalEntityEto : EntityEto, IHasEntityVersion
where TEntity : class, IEntity<TKey>
where TExternalEntityEto : EntityEto
{
private readonly IRepository<TEntity, TKey> _repository;
@ -48,8 +48,8 @@ public abstract class ExternalEntitySynchronizer<TEntity, TExternalEntityEto> :
IDistributedEventHandler<EntityUpdatedEto<TExternalEntityEto>>,
IDistributedEventHandler<EntityDeletedEto<TExternalEntityEto>>,
IUnitOfWorkEnabled
where TEntity : class, IEntity, IHasEntityVersion
where TExternalEntityEto : EntityEto, IHasEntityVersion
where TEntity : class, IEntity
where TExternalEntityEto : EntityEto
{
protected IObjectMapper ObjectMapper { get; }
private readonly IRepository<TEntity> _repository;
@ -108,17 +108,26 @@ public abstract class ExternalEntitySynchronizer<TEntity, TExternalEntityEto> :
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<TEntity, TExternalEntityEto> :
protected virtual Task<bool> 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);
}
}

@ -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<ExternalEntity
{
[Fact]
public async Task Should_Handle_Entity_Created_Event()
{
var authorId = Guid.NewGuid();
var uowManager = GetRequiredService<IUnitOfWorkManager>();
using var uow = uowManager.Begin();
var authorSynchronizer = GetRequiredService<AuthorSynchronizer>();
var repository = GetRequiredService<IRepository<Author, Guid>>();
(await repository.FindAsync(authorId)).ShouldBeNull();
var remoteAuthorEto = new RemoteAuthorEto { KeysAsString = authorId.ToString(), Name = "New" };
await authorSynchronizer.HandleEventAsync(new EntityCreatedEto<RemoteAuthorEto>(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<IUnitOfWorkManager>();
using var uow = uowManager.Begin();
var authorSynchronizer = GetRequiredService<AuthorSynchronizer>();
var repository = GetRequiredService<IRepository<Author, Guid>>();
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>(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<IUnitOfWorkManager>();
using var uow = uowManager.Begin();
var authorSynchronizer = GetRequiredService<AuthorSynchronizer>();
var repository = GetRequiredService<IRepository<Author, Guid>>();
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>(remoteAuthorEto));
(await repository.FindAsync(authorId)).ShouldBeNull();
await Should.NotThrowAsync(() =>
authorSynchronizer.HandleEventAsync(new EntityDeletedEto<RemoteAuthorEto>(remoteAuthorEto)));
}
[Fact]
public async Task Should_Handle_Versioned_Entity_Created_Event()
{
var bookId = Guid.NewGuid();
@ -42,7 +122,7 @@ public class ExternalEntitySynchronizer_Tests : AbpIntegratedTest<ExternalEntity
}
[Fact]
public async Task Should_Handle_Entity_Update_Event()
public async Task Should_Handle_Versioned_Entity_Update_Event()
{
var bookId = Guid.NewGuid();
@ -52,13 +132,18 @@ public class ExternalEntitySynchronizer_Tests : AbpIntegratedTest<ExternalEntity
var bookSynchronizer = GetRequiredService<BookSynchronizer>();
var repository = GetRequiredService<IRepository<Book, Guid>>();
(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>(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<ExternalEntity
}
[Fact]
public async Task Should_Handle_Entity_Deleted_Event()
public async Task Should_Handle_Versioned_Entity_Deleted_Event()
{
var bookId = Guid.NewGuid();
@ -96,7 +181,7 @@ public class ExternalEntitySynchronizer_Tests : AbpIntegratedTest<ExternalEntity
var bookSynchronizer = GetRequiredService<BookSynchronizer>();
var repository = GetRequiredService<IRepository<Book, Guid>>();
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<ExternalEntity
(await repository.FindAsync(bookId)).ShouldBeNull();
await bookSynchronizer.HandleEventAsync(new EntityDeletedEto<RemoteBookEto>(remoteBookEto));
(await repository.FindAsync(bookId)).ShouldBeNull();
await Should.NotThrowAsync(() =>
bookSynchronizer.HandleEventAsync(new EntityDeletedEto<RemoteBookEto>(remoteBookEto)));
}
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
@ -153,7 +237,7 @@ public class ExternalEntitySynchronizer_Tests : AbpIntegratedTest<ExternalEntity
{
public override IReadOnlyList<Type> GetEntityTypes()
{
return new List<Type> { typeof(Book) };
return new List<Type> { typeof(Book), typeof(Author) };
}
}
@ -163,6 +247,8 @@ public class ExternalEntitySynchronizer_Tests : AbpIntegratedTest<ExternalEntity
{
CreateMap<RemoteBookEto, Book>(MemberList.None)
.ForMember(x => x.Id, options => options.MapFrom(x => Guid.Parse(x.KeysAsString)));
CreateMap<RemoteAuthorEto, Author>(MemberList.None)
.ForMember(x => x.Id, options => options.MapFrom(x => Guid.Parse(x.KeysAsString)));
}
}
}

@ -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<Guid>, 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;
}
}

@ -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<Book, Guid, RemoteBookEto>, ITransientDependency
{

@ -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
{

@ -0,0 +1,17 @@
using System;
namespace Volo.Abp.Domain.Entities.Events.Distributed.ExternalEntitySynchronizers.WithoutEntityVersion;
public class Author : Entity<Guid>
{
public virtual string Name { get; set; }
protected Author()
{
}
public Author(Guid id, string name) : base(id)
{
Name = name;
}
}

@ -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<Author, Guid, RemoteAuthorEto>, ITransientDependency
{
public AuthorSynchronizer(IObjectMapper objectMapper, IRepository<Author, Guid> repository)
: base(objectMapper, repository)
{
}
}

@ -0,0 +1,6 @@
namespace Volo.Abp.Domain.Entities.Events.Distributed.ExternalEntitySynchronizers.WithoutEntityVersion;
public class RemoteAuthorEto : EntityEto
{
public string Name { get; set; }
}
Loading…
Cancel
Save