From e1ea80495c6d4269a527bbdaa85fb653c996088b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 20 Aug 2025 16:06:30 +0300 Subject: [PATCH 1/5] Add support for custom entity names in repositories Introduces the ability to set a custom entity name for repositories by adding a SetCustomEntityName method to IRepository and BasicRepositoryBase. Updates EfCoreRepository to use the custom entity name when accessing DbSet, and extends IEfCoreDbContext with an overload of Set that accepts a name. Test stubs are updated to implement the new method. --- .../Repositories/BasicRepositoryBase.cs | 7 +++++ .../Abp/Domain/Repositories/IRepository.cs | 2 ++ .../EntityFrameworkCore/EfCoreRepository.cs | 31 ++++++++++++------- .../EntityFrameworkCore/IEfCoreDbContext.cs | 3 ++ .../RepositoryRegistration_Tests.cs | 5 +++ 5 files changed, 37 insertions(+), 11 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs index cd85acce50..1d058d19b9 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs @@ -43,6 +43,13 @@ public abstract class BasicRepositoryBase : public IEntityChangeTrackingProvider EntityChangeTrackingProvider => LazyServiceProvider.LazyGetRequiredService(); public bool? IsChangeTrackingEnabled { get; protected set; } + + protected string? CustomEntityName { get; private set; } + + public void SetCustomEntityName(string? name) + { + CustomEntityName = name; + } protected BasicRepositoryBase() { diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs index dc39255b25..c5942088f0 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs @@ -13,6 +13,8 @@ namespace Volo.Abp.Domain.Repositories; public interface IRepository { bool? IsChangeTrackingEnabled { get; } + + void SetCustomEntityName(string? name); } public interface IRepository : IReadOnlyRepository, IBasicRepository diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs index 0ed9a2e199..760660dc82 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs @@ -61,7 +61,7 @@ public class EfCoreRepository : RepositoryBase, IE } [Obsolete("Use GetDbSetAsync() method.")] - public virtual DbSet DbSet => DbContext.Set(); + public virtual DbSet DbSet => GetDbSetInternal(DbContext); Task> IEfCoreRepository.GetDbSetAsync() { @@ -70,7 +70,7 @@ public class EfCoreRepository : RepositoryBase, IE protected async Task> GetDbSetAsync() { - return (await GetDbContextAsync()).Set(); + return GetDbSetInternal(await GetDbContextAsync()); } protected async Task GetDbConnectionAsync() @@ -110,7 +110,7 @@ public class EfCoreRepository : RepositoryBase, IE var dbContext = await GetDbContextAsync(); - var savedEntity = (await dbContext.Set().AddAsync(entity, GetCancellationToken(cancellationToken))).Entity; + var savedEntity = (await GetDbSetInternal(dbContext).AddAsync(entity, GetCancellationToken(cancellationToken))).Entity; if (autoSave) { @@ -120,6 +120,13 @@ public class EfCoreRepository : RepositoryBase, IE return savedEntity; } + private DbSet GetDbSetInternal(TDbContext dbContext) + { + return CustomEntityName != null + ? dbContext.Set(CustomEntityName) + : dbContext.Set(); + } + public async override Task InsertManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) { var entityArray = entities.ToArray(); @@ -147,7 +154,7 @@ public class EfCoreRepository : RepositoryBase, IE return; } - await dbContext.Set().AddRangeAsync(entityArray, cancellationToken); + await GetDbSetInternal(dbContext).AddRangeAsync(entityArray, cancellationToken); if (autoSave) { @@ -159,9 +166,10 @@ public class EfCoreRepository : RepositoryBase, IE { var dbContext = await GetDbContextAsync(); - if (dbContext.Set().Local.All(e => e != entity)) + var dbSet = GetDbSetInternal(dbContext); + if (dbSet.Local.All(e => e != entity)) { - dbContext.Set().Attach(entity); + dbSet.Attach(entity); dbContext.Update(entity); } @@ -197,7 +205,7 @@ public class EfCoreRepository : RepositoryBase, IE var dbContext = await GetDbContextAsync(); - dbContext.Set().UpdateRange(entityArray); + GetDbSetInternal(dbContext).UpdateRange(entityArray); if (autoSave) { @@ -209,7 +217,7 @@ public class EfCoreRepository : RepositoryBase, IE { var dbContext = await GetDbContextAsync(); - dbContext.Set().Remove(entity); + GetDbSetInternal(dbContext).Remove(entity); if (autoSave) { @@ -318,7 +326,7 @@ public class EfCoreRepository : RepositoryBase, IE public async override Task DeleteAsync(Expression> predicate, bool autoSave = false, CancellationToken cancellationToken = default) { var dbContext = await GetDbContextAsync(); - var dbSet = dbContext.Set(); + var dbSet = GetDbSetInternal(dbContext); var entities = await dbSet .Where(predicate) @@ -335,8 +343,9 @@ public class EfCoreRepository : RepositoryBase, IE public async override Task DeleteDirectAsync(Expression> predicate, CancellationToken cancellationToken = default) { var dbContext = await GetDbContextAsync(); - var dbSet = dbContext.Set(); - await dbSet.Where(predicate).ExecuteDeleteAsync(GetCancellationToken(cancellationToken)); + await GetDbSetInternal(dbContext) + .Where(predicate) + .ExecuteDeleteAsync(GetCancellationToken(cancellationToken)); } public virtual async Task EnsureCollectionLoadedAsync( diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/IEfCoreDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/IEfCoreDbContext.cs index 833163d223..3097eba9cb 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/IEfCoreDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/IEfCoreDbContext.cs @@ -31,6 +31,9 @@ public interface IEfCoreDbContext : IDisposable, IInfrastructure Set() where T : class; + + DbSet Set(string name) + where T : class; DatabaseFacade Database { get; } diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs index 4a7a9fb28f..63aa0e3411 100644 --- a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs @@ -408,6 +408,11 @@ public class RepositoryRegistration_Tests public class MyTestAggregateRootWithDefaultPkEmptyRepository : IMyTestAggregateRootWithDefaultPkEmptyRepository { public bool? IsChangeTrackingEnabled { get; set; } + + public void SetCustomEntityName(string name) + { + + } } public class TestDbContextRegistrationOptions : AbpCommonDbContextRegistrationOptions From 8d9e4424181d85185c94257bc35b81fda98776fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 20 Aug 2025 16:24:27 +0300 Subject: [PATCH 2/5] Extract method: AutoConfigureEntityTypeProperties --- .../Abp/EntityFrameworkCore/AbpDbContext.cs | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index 797e4dbb67..882a51ebf1 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -126,17 +126,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, foreach (var entityType in modelBuilder.Model.GetEntityTypes().ToArray()) { - ConfigureBasePropertiesMethodInfo - .MakeGenericMethod(entityType.ClrType) - .Invoke(this, new object[] { modelBuilder, entityType }); - - ConfigureValueConverterMethodInfo - .MakeGenericMethod(entityType.ClrType) - .Invoke(this, new object[] { modelBuilder, entityType }); - - ConfigureValueGeneratedMethodInfo - .MakeGenericMethod(entityType.ClrType) - .Invoke(this, new object[] { modelBuilder, entityType }); + AutoConfigureEntityTypeProperties(modelBuilder, entityType); } if (LazyServiceProvider == null || Options == null) @@ -150,6 +140,24 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, onModelCreatingAction.As>().Invoke(this, modelBuilder); } } + + protected virtual void AutoConfigureEntityTypeProperties( + ModelBuilder modelBuilder, + IMutableEntityType entityType) + { + ConfigureBasePropertiesMethodInfo + .MakeGenericMethod(entityType.ClrType) + .Invoke(this, new object[] { modelBuilder, entityType }); + + ConfigureValueConverterMethodInfo + .MakeGenericMethod(entityType.ClrType) + .Invoke(this, new object[] { modelBuilder, entityType }); + + ConfigureValueGeneratedMethodInfo + .MakeGenericMethod(entityType.ClrType) + .Invoke(this, new object[] { modelBuilder, entityType }); + } + protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { From 485dd6f1a949856043d8c2420190cc2db9559cce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 20 Aug 2025 16:39:05 +0300 Subject: [PATCH 3/5] Handle shared types while getting EntityTypeBuilder --- .../Abp/EntityFrameworkCore/AbpDbContext.cs | 100 ++++++++++++------ 1 file changed, 67 insertions(+), 33 deletions(-) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index 882a51ebf1..fceb338051 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -37,6 +37,7 @@ using Volo.Abp.Reflection; using Volo.Abp.Timing; using Volo.Abp.Uow; using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace Volo.Abp.EntityFrameworkCore; @@ -126,7 +127,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, foreach (var entityType in modelBuilder.Model.GetEntityTypes().ToArray()) { - AutoConfigureEntityTypeProperties(modelBuilder, entityType); + ConfigureEntityTypeProperties(modelBuilder, entityType); } if (LazyServiceProvider == null || Options == null) @@ -140,24 +141,23 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, onModelCreatingAction.As>().Invoke(this, modelBuilder); } } - - protected virtual void AutoConfigureEntityTypeProperties( - ModelBuilder modelBuilder, - IMutableEntityType entityType) - { - ConfigureBasePropertiesMethodInfo - .MakeGenericMethod(entityType.ClrType) - .Invoke(this, new object[] { modelBuilder, entityType }); - - ConfigureValueConverterMethodInfo - .MakeGenericMethod(entityType.ClrType) - .Invoke(this, new object[] { modelBuilder, entityType }); - - ConfigureValueGeneratedMethodInfo - .MakeGenericMethod(entityType.ClrType) - .Invoke(this, new object[] { modelBuilder, entityType }); - } + protected virtual void ConfigureEntityTypeProperties( + ModelBuilder modelBuilder, + IMutableEntityType entityType) + { + ConfigureBasePropertiesMethodInfo + .MakeGenericMethod(entityType.ClrType) + .Invoke(this, new object[] { modelBuilder, entityType }); + + ConfigureValueConverterMethodInfo + .MakeGenericMethod(entityType.ClrType) + .Invoke(this, new object[] { modelBuilder, entityType }); + + ConfigureValueGeneratedMethodInfo + .MakeGenericMethod(entityType.ClrType) + .Invoke(this, new object[] { modelBuilder, entityType }); + } protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { @@ -770,7 +770,9 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, AuditPropertySetter?.IncrementEntityVersionProperty(entry.Entity); } - protected virtual void ConfigureBaseProperties(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType) + protected virtual void ConfigureBaseProperties( + ModelBuilder modelBuilder, + IMutableEntityType mutableEntityType) where TEntity : class { if (mutableEntityType.IsOwned()) @@ -783,25 +785,44 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, return; } - modelBuilder.Entity().ConfigureByConvention(); + var entityTypeBuilder = CreateEntityTypeBuilderFromMutableEntityType( + modelBuilder, + mutableEntityType + ); + + entityTypeBuilder.ConfigureByConvention(); + + ConfigureGlobalFilters(modelBuilder, mutableEntityType, entityTypeBuilder); + } - ConfigureGlobalFilters(modelBuilder, mutableEntityType); + protected virtual EntityTypeBuilder CreateEntityTypeBuilderFromMutableEntityType( + ModelBuilder modelBuilder, + IMutableEntityType mutableEntityType) where TEntity : class + { + return mutableEntityType.HasSharedClrType + ? modelBuilder.SharedTypeEntity(mutableEntityType.Name) + : modelBuilder.Entity(); } - protected virtual void ConfigureGlobalFilters(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType) + protected virtual void ConfigureGlobalFilters( + ModelBuilder modelBuilder, + IMutableEntityType mutableEntityType, + EntityTypeBuilder entityTypeBuilder) where TEntity : class { if (mutableEntityType.BaseType == null && ShouldFilterEntity(mutableEntityType)) { - var filterExpression = CreateFilterExpression(modelBuilder); + var filterExpression = CreateFilterExpression(modelBuilder, entityTypeBuilder); if (filterExpression != null) { - modelBuilder.Entity().HasAbpQueryFilter(filterExpression); + entityTypeBuilder.HasAbpQueryFilter(filterExpression); } } } - protected virtual void ConfigureValueConverter(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType) + protected virtual void ConfigureValueConverter( + ModelBuilder modelBuilder, + IMutableEntityType mutableEntityType) where TEntity : class { if (mutableEntityType.BaseType != null || @@ -816,6 +837,7 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, { return; } + foreach (var property in mutableEntityType.GetProperties(). Where(property => property.PropertyInfo != null && @@ -823,8 +845,12 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, property.PropertyInfo.CanWrite && ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(property.PropertyInfo) == null)) { - modelBuilder - .Entity() + var entityTypeBuilder = CreateEntityTypeBuilderFromMutableEntityType( + modelBuilder, + mutableEntityType + ); + + entityTypeBuilder .Property(property.Name) .HasConversion(property.ClrType == typeof(DateTime) ? new AbpDateTimeValueConverter(Clock) @@ -832,7 +858,9 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, } } - protected virtual void ConfigureValueGenerated(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType) + protected virtual void ConfigureValueGenerated( + ModelBuilder modelBuilder, + IMutableEntityType mutableEntityType) where TEntity : class { if (!typeof(IEntity).IsAssignableFrom(typeof(TEntity))) @@ -840,7 +868,8 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, return; } - var idPropertyBuilder = modelBuilder.Entity().Property(x => ((IEntity)x).Id); + var entityTypeBuilder = CreateEntityTypeBuilderFromMutableEntityType(modelBuilder, mutableEntityType); + var idPropertyBuilder = entityTypeBuilder.Property(x => ((IEntity)x).Id); if (idPropertyBuilder.Metadata.PropertyInfo!.IsDefined(typeof(DatabaseGeneratedAttribute), true)) { return; @@ -864,25 +893,30 @@ public abstract class AbpDbContext : DbContext, IAbpEfCoreDbContext, return false; } - protected virtual Expression>? CreateFilterExpression(ModelBuilder modelBuilder) + protected virtual Expression>? CreateFilterExpression( + ModelBuilder modelBuilder, + EntityTypeBuilder entityTypeBuilder) where TEntity : class { Expression>? expression = null; if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity))) { - var softDeleteColumnName = modelBuilder.Entity().Metadata.FindProperty(nameof(ISoftDelete.IsDeleted))?.GetColumnName() ?? "IsDeleted"; + var softDeleteColumnName = entityTypeBuilder.Metadata.FindProperty(nameof(ISoftDelete.IsDeleted))?.GetColumnName() ?? "IsDeleted"; expression = e => !IsSoftDeleteFilterEnabled || !EF.Property(e, softDeleteColumnName); if (UseDbFunction()) { expression = e => AbpEfCoreDataFilterDbFunctionMethods.SoftDeleteFilter(((ISoftDelete)e).IsDeleted, true); - modelBuilder.ConfigureSoftDeleteDbFunction(AbpEfCoreDataFilterDbFunctionMethods.SoftDeleteFilterMethodInfo, this.GetService()); + modelBuilder.ConfigureSoftDeleteDbFunction( + AbpEfCoreDataFilterDbFunctionMethods.SoftDeleteFilterMethodInfo, + this.GetService() + ); } } if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity))) { - var multiTenantColumnName = modelBuilder.Entity().Metadata.FindProperty(nameof(IMultiTenant.TenantId))?.GetColumnName() ?? "TenantId"; + var multiTenantColumnName = entityTypeBuilder.Metadata.FindProperty(nameof(IMultiTenant.TenantId))?.GetColumnName() ?? "TenantId"; Expression> multiTenantFilter = e => !IsMultiTenantFilterEnabled || EF.Property(e, multiTenantColumnName) == CurrentTenantId; if (UseDbFunction()) { From 1a8f0fc86ec8b58d0466b0f30ba9273c5a441d88 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 21 Aug 2025 13:13:32 +0800 Subject: [PATCH 4/5] Add `SharedEntity` base class and unit tests. --- .../Volo/Abp/Domain/Entities/Entity.cs | 52 +++++++ .../SharedEntity_Repository_Tests.cs | 147 ++++++++++++++++++ .../TestMigrationsDbContext.cs | 19 +++ .../EntityFrameworkCore/TestAppDbContext.cs | 19 +++ .../TestApp/Domain/TestSharedTypeEntity.cs | 28 ++++ 5 files changed, 265 insertions(+) create mode 100644 framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/SharedEntity_Repository_Tests.cs create mode 100644 framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/TestSharedTypeEntity.cs diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs index 9e52d1abfd..3bec827dac 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs @@ -54,3 +54,55 @@ public abstract class Entity : Entity, IEntity return $"[ENTITY: {GetType().Name}] Id = {Id}"; } } + +/// +[Serializable] +public abstract class SharedEntity : Dictionary, IEntity +{ + protected SharedEntity() + { + EntityHelper.TrySetTenantId(this); + } + + /// + public override string ToString() + { + return $"[ENTITY: {GetType().Name}] Keys = {GetKeys().JoinAsString(", ")}"; + } + + public abstract object?[] GetKeys(); + + public bool EntityEquals(IEntity other) + { + return EntityHelper.EntityEquals(this, other); + } +} + +/// +[Serializable] +public abstract class SharedEntity : SharedEntity, IEntity +{ + /// + public virtual TKey Id { get; protected set; } = default!; + + protected SharedEntity() + { + + } + + protected SharedEntity(TKey id) + { + Id = id; + } + + public override object?[] GetKeys() + { + return new object?[] { Id }; + } + + /// + public override string ToString() + { + return $"[ENTITY: {GetType().Name}] Id = {Id}"; + } +} diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/SharedEntity_Repository_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/SharedEntity_Repository_Tests.cs new file mode 100644 index 0000000000..699266a69d --- /dev/null +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/SharedEntity_Repository_Tests.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Data; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.MultiTenancy; +using Volo.Abp.TestApp.Domain; +using Xunit; + +namespace Volo.Abp.EntityFrameworkCore.Repositories; + +public class SharedEntity_Repository_Tests : EntityFrameworkCoreTestBase +{ + protected readonly IRepository TestSharedTypeEntityRepository; + protected readonly ICurrentTenant CurrentTenant; + protected readonly IDataFilter dataFilter; + + public SharedEntity_Repository_Tests() + { + TestSharedTypeEntityRepository = GetRequiredService>(); + CurrentTenant = GetRequiredService(); + dataFilter = GetRequiredService>(); + } + + [Fact] + public async Task SharedEntity_Test() + { + await WithUnitOfWorkAsync(async () => + { + TestSharedTypeEntityRepository.SetCustomEntityName("TestSharedEntity1"); + + var tenantId = Guid.NewGuid(); + await TestSharedTypeEntityRepository.InsertManyAsync(new List() + { + new TestSharedEntity(Guid.NewGuid()) + { + TenantId = null, + IsDeleted = false, + + ["Name"] = "Test Person1", + ["Age"] = 10, + ["Birthday"] = DateTime.Now + }.SetProperty("testProperty", "Test Value1"), + new TestSharedEntity(Guid.NewGuid()) + { + TenantId = tenantId, + IsDeleted = false, + + ["Name"] = "Test Person2", + ["Age"] = 20, + ["Birthday"] = DateTime.Now + }, + new TestSharedEntity(Guid.NewGuid()) + { + TenantId = tenantId, + IsDeleted = true, + + ["Name"] = "Test Person3", + ["Age"] = 30, + ["Birthday"] = DateTime.Now + }, + new TestSharedEntity(Guid.NewGuid()) + { + TenantId = null, + IsDeleted = true, + + ["Name"] = "Test Person4", + ["Age"] = 40, + ["Birthday"] = DateTime.Now + } + }, true); + + var entities = (await TestSharedTypeEntityRepository.GetListAsync()).OrderBy(x => x["Name"]).ToList(); + entities.Count.ShouldBe(1); + entities[0].TenantId.ShouldBeNull(); + entities[0].IsDeleted.ShouldBe(false); + entities[0]["Name"].ShouldBe("Test Person1"); + entities[0]["Age"].ShouldBe(10); + entities[0].GetProperty("testProperty").ShouldBe("Test Value1"); + + using (CurrentTenant.Change(tenantId)) + { + entities = (await TestSharedTypeEntityRepository.GetListAsync()).OrderBy(x => x["Name"]).ToList(); + entities.Count.ShouldBe(1); + entities[0].TenantId.ShouldBe(tenantId); + entities[0].IsDeleted.ShouldBe(false); + entities[0]["Name"].ShouldBe("Test Person2"); + entities[0]["Age"].ShouldBe(20); + } + + using (dataFilter.Disable()) + { + entities = (await TestSharedTypeEntityRepository.GetListAsync()).OrderBy(x => x["Name"]).ToList(); + entities.Count.ShouldBe(2); + + entities[0].TenantId.ShouldBeNull(); + entities[0].IsDeleted.ShouldBe(false); + entities[0]["Name"].ShouldBe("Test Person1"); + entities[0]["Age"].ShouldBe(10); + + entities[1].TenantId.ShouldBeNull(); + entities[1].IsDeleted.ShouldBe(true); + entities[1]["Name"].ShouldBe("Test Person4"); + entities[1]["Age"].ShouldBe(40); + } + + using (CurrentTenant.Change(tenantId)) + { + using (dataFilter.Disable()) + { + entities = (await TestSharedTypeEntityRepository.GetListAsync()).OrderBy(x => x["Name"]).ToList(); + entities.Count.ShouldBe(2); + + entities[0].TenantId.ShouldBe(tenantId); + entities[0].IsDeleted.ShouldBe(false); + entities[0]["Name"].ShouldBe("Test Person2"); + entities[0]["Age"].ShouldBe(20); + + entities[1].TenantId.ShouldBe(tenantId); + entities[1].IsDeleted.ShouldBe(true); + entities[1]["Name"].ShouldBe("Test Person3"); + entities[1]["Age"].ShouldBe(30); + } + } + + TestSharedTypeEntityRepository.SetCustomEntityName("TestSharedEntity2"); + await TestSharedTypeEntityRepository.InsertManyAsync(new List() + { + new TestSharedEntity(Guid.NewGuid()) + { + ["Name"] = "Test Person1 from Second Table", + ["Age"] = 110, + ["Birthday"] = DateTime.Now + } + }, true); + + var entitiesFromSecondTable = (await TestSharedTypeEntityRepository.GetListAsync()).OrderBy(x => x["Name"]).ToList(); + entitiesFromSecondTable.Count.ShouldBe(1); + entitiesFromSecondTable[0].TenantId.ShouldBeNull(); + entitiesFromSecondTable[0].IsDeleted.ShouldBe(false); + entitiesFromSecondTable[0]["Name"].ShouldBe("Test Person1 from Second Table"); + entitiesFromSecondTable[0]["Age"].ShouldBe(110); + }); + } +} diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs index d05e367ff7..20524cd662 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs @@ -1,5 +1,6 @@ using System; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; using Volo.Abp.EntityFrameworkCore.Modeling; using Volo.Abp.EntityFrameworkCore.TestApp.SecondContext; using Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext; @@ -33,6 +34,9 @@ public class TestMigrationsDbContext : AbpDbContext public DbSet Blogs { get; set; } public DbSet BlogPosts { get; set; } + public DbSet TestSharedEntity => Set("TestSharedEntity1"); + public DbSet TestSharedEntity2 => Set("TestSharedEntity2"); + public TestMigrationsDbContext(DbContextOptions options) : base(options) { @@ -41,8 +45,23 @@ public class TestMigrationsDbContext : AbpDbContext protected override void OnModelCreating(ModelBuilder modelBuilder) { + // Owned and SharedTypeEntity should be configured before the base OnModelCreating call + modelBuilder.Owned(); + Action> sharedEntityBuildAction = b => + { + b.ConfigureByConvention(); + b.Property("Id"); + b.Property("TenantId"); + b.Property("IsDeleted"); + b.Property("Name"); + b.Property("Age"); + b.Property("Birthday"); + }; + modelBuilder.SharedTypeEntity("TestSharedEntity1", sharedEntityBuildAction); + modelBuilder.SharedTypeEntity("TestSharedEntity2", sharedEntityBuildAction); + base.OnModelCreating(modelBuilder); modelBuilder.Entity(b => diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs index 242809bf7c..bc43a9ba02 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs @@ -1,6 +1,7 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.Extensions.Logging; using Volo.Abp.DependencyInjection; using Volo.Abp.EntityFrameworkCore; @@ -40,6 +41,9 @@ public class TestAppDbContext : AbpDbContext, IThirdDbContext, public DbSet Blogs { get; set; } public DbSet BlogPosts { get; set; } + public DbSet TestSharedEntity => Set("TestSharedEntity1"); + public DbSet TestSharedEntity2 => Set("TestSharedEntity2"); + public TestAppDbContext(DbContextOptions options) : base(options) { @@ -54,8 +58,23 @@ public class TestAppDbContext : AbpDbContext, IThirdDbContext, protected override void OnModelCreating(ModelBuilder modelBuilder) { + // Owned and SharedTypeEntity should be configured before the base OnModelCreating call + modelBuilder.Owned(); + Action> sharedEntityBuildAction = b => + { + b.ConfigureByConvention(); + b.Property("Id"); + b.Property("TenantId"); + b.Property("IsDeleted"); + b.Property("Name"); + b.Property("Age"); + b.Property("Birthday"); + }; + modelBuilder.SharedTypeEntity("TestSharedEntity1", sharedEntityBuildAction); + modelBuilder.SharedTypeEntity("TestSharedEntity2", sharedEntityBuildAction); + base.OnModelCreating(modelBuilder); modelBuilder.Entity(b => diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/TestSharedTypeEntity.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/TestSharedTypeEntity.cs new file mode 100644 index 0000000000..df8f5e17fa --- /dev/null +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/TestSharedTypeEntity.cs @@ -0,0 +1,28 @@ +using System; +using Volo.Abp.Data; +using Volo.Abp.Domain.Entities; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.TestApp.Domain; + +public class TestSharedEntity : SharedEntity, IMultiTenant, ISoftDelete, IHasExtraProperties +{ + public Guid? TenantId { get; set; } + + public bool IsDeleted { get; set; } + + public TestSharedEntity() + { + ExtraProperties = new ExtraPropertyDictionary(); + this.SetDefaultsForExtraProperties(); + } + + public TestSharedEntity(Guid id) + : base(id) + { + ExtraProperties = new ExtraPropertyDictionary(); + this.SetDefaultsForExtraProperties(); + } + + public ExtraPropertyDictionary ExtraProperties { get; set; } +} From 00d6120fdc8c1f06bbf7c9bf10a0711e2b3b38da Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 21 Aug 2025 15:43:00 +0800 Subject: [PATCH 5/5] Refactor shared entity implementation and tests --- .../Volo/Abp/Domain/Entities/Entity.cs | 52 -------- .../SharedEntity_Repository_Tests.cs | 114 +++++++++++------- .../TestMigrationsDbContext.cs | 14 ++- .../EntityFrameworkCore/TestAppDbContext.cs | 15 +-- .../TestApp/Domain/TestSharedTypeEntity.cs | 26 ++-- 5 files changed, 106 insertions(+), 115 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs index 3bec827dac..9e52d1abfd 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Entity.cs @@ -54,55 +54,3 @@ public abstract class Entity : Entity, IEntity return $"[ENTITY: {GetType().Name}] Id = {Id}"; } } - -/// -[Serializable] -public abstract class SharedEntity : Dictionary, IEntity -{ - protected SharedEntity() - { - EntityHelper.TrySetTenantId(this); - } - - /// - public override string ToString() - { - return $"[ENTITY: {GetType().Name}] Keys = {GetKeys().JoinAsString(", ")}"; - } - - public abstract object?[] GetKeys(); - - public bool EntityEquals(IEntity other) - { - return EntityHelper.EntityEquals(this, other); - } -} - -/// -[Serializable] -public abstract class SharedEntity : SharedEntity, IEntity -{ - /// - public virtual TKey Id { get; protected set; } = default!; - - protected SharedEntity() - { - - } - - protected SharedEntity(TKey id) - { - Id = id; - } - - public override object?[] GetKeys() - { - return new object?[] { Id }; - } - - /// - public override string ToString() - { - return $"[ENTITY: {GetType().Name}] Id = {Id}"; - } -} diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/SharedEntity_Repository_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/SharedEntity_Repository_Tests.cs index 699266a69d..abdc7a4e6d 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/SharedEntity_Repository_Tests.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/SharedEntity_Repository_Tests.cs @@ -15,13 +15,13 @@ public class SharedEntity_Repository_Tests : EntityFrameworkCoreTestBase { protected readonly IRepository TestSharedTypeEntityRepository; protected readonly ICurrentTenant CurrentTenant; - protected readonly IDataFilter dataFilter; + protected readonly IDataFilter DataFilter; public SharedEntity_Repository_Tests() { TestSharedTypeEntityRepository = GetRequiredService>(); CurrentTenant = GetRequiredService(); - dataFilter = GetRequiredService>(); + DataFilter = GetRequiredService>(); } [Fact] @@ -38,90 +38,86 @@ public class SharedEntity_Repository_Tests : EntityFrameworkCoreTestBase { TenantId = null, IsDeleted = false, - - ["Name"] = "Test Person1", - ["Age"] = 10, - ["Birthday"] = DateTime.Now + Name = "Test Person1", + Age = 10, + Birthday = DateTime.Now }.SetProperty("testProperty", "Test Value1"), new TestSharedEntity(Guid.NewGuid()) { TenantId = tenantId, IsDeleted = false, - - ["Name"] = "Test Person2", - ["Age"] = 20, - ["Birthday"] = DateTime.Now + Name = "Test Person2", + Age = 20, + Birthday = DateTime.Now }, new TestSharedEntity(Guid.NewGuid()) { TenantId = tenantId, IsDeleted = true, - - ["Name"] = "Test Person3", - ["Age"] = 30, - ["Birthday"] = DateTime.Now + Name = "Test Person3", + Age = 30, + Birthday = DateTime.Now }, new TestSharedEntity(Guid.NewGuid()) { TenantId = null, IsDeleted = true, - - ["Name"] = "Test Person4", - ["Age"] = 40, - ["Birthday"] = DateTime.Now + Name = "Test Person4", + Age = 40, + Birthday = DateTime.Now } }, true); - var entities = (await TestSharedTypeEntityRepository.GetListAsync()).OrderBy(x => x["Name"]).ToList(); + var entities = (await TestSharedTypeEntityRepository.GetListAsync()).OrderBy(x => x.Name).ToList(); entities.Count.ShouldBe(1); entities[0].TenantId.ShouldBeNull(); entities[0].IsDeleted.ShouldBe(false); - entities[0]["Name"].ShouldBe("Test Person1"); - entities[0]["Age"].ShouldBe(10); + entities[0].Name.ShouldBe("Test Person1"); + entities[0].Age.ShouldBe(10); entities[0].GetProperty("testProperty").ShouldBe("Test Value1"); using (CurrentTenant.Change(tenantId)) { - entities = (await TestSharedTypeEntityRepository.GetListAsync()).OrderBy(x => x["Name"]).ToList(); + entities = (await TestSharedTypeEntityRepository.GetListAsync()).OrderBy(x => x.Name).ToList(); entities.Count.ShouldBe(1); entities[0].TenantId.ShouldBe(tenantId); entities[0].IsDeleted.ShouldBe(false); - entities[0]["Name"].ShouldBe("Test Person2"); - entities[0]["Age"].ShouldBe(20); + entities[0].Name.ShouldBe("Test Person2"); + entities[0].Age.ShouldBe(20); } - using (dataFilter.Disable()) + using (DataFilter.Disable()) { - entities = (await TestSharedTypeEntityRepository.GetListAsync()).OrderBy(x => x["Name"]).ToList(); + entities = (await TestSharedTypeEntityRepository.GetListAsync()).OrderBy(x => x.Name).ToList(); entities.Count.ShouldBe(2); entities[0].TenantId.ShouldBeNull(); entities[0].IsDeleted.ShouldBe(false); - entities[0]["Name"].ShouldBe("Test Person1"); - entities[0]["Age"].ShouldBe(10); + entities[0].Name.ShouldBe("Test Person1"); + entities[0].Age.ShouldBe(10); entities[1].TenantId.ShouldBeNull(); entities[1].IsDeleted.ShouldBe(true); - entities[1]["Name"].ShouldBe("Test Person4"); - entities[1]["Age"].ShouldBe(40); + entities[1].Name.ShouldBe("Test Person4"); + entities[1].Age.ShouldBe(40); } using (CurrentTenant.Change(tenantId)) { - using (dataFilter.Disable()) + using (DataFilter.Disable()) { - entities = (await TestSharedTypeEntityRepository.GetListAsync()).OrderBy(x => x["Name"]).ToList(); + entities = (await TestSharedTypeEntityRepository.GetListAsync()).OrderBy(x => x.Name).ToList(); entities.Count.ShouldBe(2); entities[0].TenantId.ShouldBe(tenantId); entities[0].IsDeleted.ShouldBe(false); - entities[0]["Name"].ShouldBe("Test Person2"); - entities[0]["Age"].ShouldBe(20); + entities[0].Name.ShouldBe("Test Person2"); + entities[0].Age.ShouldBe(20); entities[1].TenantId.ShouldBe(tenantId); entities[1].IsDeleted.ShouldBe(true); - entities[1]["Name"].ShouldBe("Test Person3"); - entities[1]["Age"].ShouldBe(30); + entities[1].Name.ShouldBe("Test Person3"); + entities[1].Age.ShouldBe(30); } } @@ -130,18 +126,52 @@ public class SharedEntity_Repository_Tests : EntityFrameworkCoreTestBase { new TestSharedEntity(Guid.NewGuid()) { - ["Name"] = "Test Person1 from Second Table", - ["Age"] = 110, - ["Birthday"] = DateTime.Now + Name = "Test Person1 from Second Table", + Age = 110, + Birthday = DateTime.Now } }, true); - var entitiesFromSecondTable = (await TestSharedTypeEntityRepository.GetListAsync()).OrderBy(x => x["Name"]).ToList(); + var entitiesFromSecondTable = (await TestSharedTypeEntityRepository.GetListAsync()).OrderBy(x => x.Name).ToList(); entitiesFromSecondTable.Count.ShouldBe(1); entitiesFromSecondTable[0].TenantId.ShouldBeNull(); entitiesFromSecondTable[0].IsDeleted.ShouldBe(false); - entitiesFromSecondTable[0]["Name"].ShouldBe("Test Person1 from Second Table"); - entitiesFromSecondTable[0]["Age"].ShouldBe(110); + entitiesFromSecondTable[0].Name.ShouldBe("Test Person1 from Second Table"); + entitiesFromSecondTable[0].Age.ShouldBe(110); + }); + } + + [Fact] + public async Task SharedEntity_DynamicProperty_Test() + { + await WithUnitOfWorkAsync(async () => + { + TestSharedTypeEntityRepository.SetCustomEntityName("TestSharedEntity1"); + + var entity = new TestSharedEntity(Guid.NewGuid()) + { + TenantId = null, + IsDeleted = false, + Name = "Test Person1", + Age = 10, + Birthday = DateTime.Now + }; + + entity["DynamicProperty"] = "Test Value1"; + + await TestSharedTypeEntityRepository.InsertAsync(entity, true); + + entity = await TestSharedTypeEntityRepository.FindAsync(x => x.Id == entity.Id!); + entity.ShouldNotBeNull(); + + entity.Name.ShouldBe("Test Person1"); + entity.Age.ShouldBe(10); + entity.Birthday.ShouldNotBeNull(); + entity["DynamicProperty"].ShouldBe("Test Value1"); + + TestSharedTypeEntityRepository.SetCustomEntityName("TestSharedEntity2"); + entity = await TestSharedTypeEntityRepository.FindAsync(x => x.Id == entity.Id!); + entity.ShouldBeNull(); }); } } diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs index 20524cd662..0efef5ba1f 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/TestMigrationsDbContext.cs @@ -52,12 +52,14 @@ public class TestMigrationsDbContext : AbpDbContext Action> sharedEntityBuildAction = b => { b.ConfigureByConvention(); - b.Property("Id"); - b.Property("TenantId"); - b.Property("IsDeleted"); - b.Property("Name"); - b.Property("Age"); - b.Property("Birthday"); + b.Property(x => x.Id); + b.Property(x => x.TenantId); + b.Property(x => x.IsDeleted); + b.Property(x => x.Name); + b.Property(x => x.Age); + b.Property(x => x.Birthday); + + b.Property("DynamicProperty"); }; modelBuilder.SharedTypeEntity("TestSharedEntity1", sharedEntityBuildAction); modelBuilder.SharedTypeEntity("TestSharedEntity2", sharedEntityBuildAction); diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs index bc43a9ba02..b2680abdff 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/TestApp/EntityFrameworkCore/TestAppDbContext.cs @@ -2,7 +2,6 @@ using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.Extensions.Logging; using Volo.Abp.DependencyInjection; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.Modeling; @@ -65,12 +64,14 @@ public class TestAppDbContext : AbpDbContext, IThirdDbContext, Action> sharedEntityBuildAction = b => { b.ConfigureByConvention(); - b.Property("Id"); - b.Property("TenantId"); - b.Property("IsDeleted"); - b.Property("Name"); - b.Property("Age"); - b.Property("Birthday"); + b.Property(x => x.Id); + b.Property(x => x.TenantId); + b.Property(x => x.IsDeleted); + b.Property(x => x.Name); + b.Property(x => x.Age); + b.Property(x => x.Birthday); + + b.Property("DynamicProperty"); }; modelBuilder.SharedTypeEntity("TestSharedEntity1", sharedEntityBuildAction); modelBuilder.SharedTypeEntity("TestSharedEntity2", sharedEntityBuildAction); diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/TestSharedTypeEntity.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/TestSharedTypeEntity.cs index df8f5e17fa..5b1d94a316 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/TestSharedTypeEntity.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/TestSharedTypeEntity.cs @@ -1,28 +1,38 @@ using System; -using Volo.Abp.Data; +using System.Collections.Generic; using Volo.Abp.Domain.Entities; using Volo.Abp.MultiTenancy; namespace Volo.Abp.TestApp.Domain; -public class TestSharedEntity : SharedEntity, IMultiTenant, ISoftDelete, IHasExtraProperties +public class TestSharedEntity : AggregateRoot, IMultiTenant, ISoftDelete { + private readonly Dictionary _dynamicPropertites = new(); + + public object this[string key] + { + get => _dynamicPropertites.GetValueOrDefault(key); + set => _dynamicPropertites[key] = value; + } + public Guid? TenantId { get; set; } + public virtual string Name { get; set; } + + public virtual int Age { get; set; } + + public virtual DateTime? Birthday { get; set; } + public bool IsDeleted { get; set; } public TestSharedEntity() { - ExtraProperties = new ExtraPropertyDictionary(); - this.SetDefaultsForExtraProperties(); + } public TestSharedEntity(Guid id) : base(id) { - ExtraProperties = new ExtraPropertyDictionary(); - this.SetDefaultsForExtraProperties(); - } - public ExtraPropertyDictionary ExtraProperties { get; set; } + } }