From 01d2cf3a8bee56ecd3b4846a4cbae7ed18f7bc53 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 27 Aug 2023 14:02:24 +0800 Subject: [PATCH 01/18] Use `IsChangeTrackingEnabled` to replace `IsReadOnly`. --- .../ServiceCollectionRepositoryExtensions.cs | 2 +- .../Repositories/BasicRepositoryBase.cs | 8 +++- .../Abp/Domain/Repositories/IRepository.cs | 2 +- .../Repositories/RepositoryExtensions.cs | 20 +++++++++ .../EntityFrameworkCore/EfCoreRepository.cs | 38 +++++++---------- .../RepositoryRegistration_Tests.cs | 2 +- .../Repositories/ReadOnlyRepository_Tests.cs | 41 +++++++++++++------ 7 files changed, 73 insertions(+), 40 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs b/framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs index 0a2fa66b77..a877c673f1 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs @@ -92,7 +92,7 @@ public static class ServiceCollectionRepositoryExtensions descriptor = ServiceDescriptor.Transient(serviceType, provider => { var repository = provider.GetRequiredService(implementationType); - ObjectHelper.TrySetProperty(repository.As(), x => x.IsReadOnly, _ => true); + ObjectHelper.TrySetProperty(repository.As(), x => x.IsChangeTrackingEnabled, _ => false); return repository; }); } 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 6de2aa3208..8257c4c8af 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 @@ -4,6 +4,8 @@ using System.Collections.Generic; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Entities; @@ -34,7 +36,11 @@ public abstract class BasicRepositoryBase : public ICancellationTokenProvider CancellationTokenProvider => LazyServiceProvider.LazyGetService(NullCancellationTokenProvider.Instance); - public bool IsReadOnly { get; protected set; } + public ILoggerFactory? LoggerFactory => LazyServiceProvider.LazyGetService(); + + public ILogger Logger => LazyServiceProvider.LazyGetService(provider => LoggerFactory?.CreateLogger(GetType().FullName!) ?? NullLogger.Instance); + + public bool IsChangeTrackingEnabled { get; protected set; } = true; 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 d50a881c28..fda0c7ed94 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 @@ -12,7 +12,7 @@ namespace Volo.Abp.Domain.Repositories; /// public interface IRepository { - bool IsReadOnly { get; } + bool IsChangeTrackingEnabled { get; } } public interface IRepository : IReadOnlyRepository, IBasicRepository diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs index cbebf62196..9ae120d2dc 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs @@ -145,6 +145,26 @@ public static class RepositoryExtensions } } + public static IDisposable DisableTracking(this IRepository repository) + { + return Tracking(repository, false); + } + + public static IDisposable EnableTracking(this IRepository repository) + { + return Tracking(repository, true); + } + + private static IDisposable Tracking(this IRepository repository, bool enabled) + { + var previous = repository.IsChangeTrackingEnabled; + ObjectHelper.TrySetProperty(ProxyHelper.UnProxy(repository).As(), x => x.IsChangeTrackingEnabled, _ => enabled); + return new DisposeAction(_ => + { + ObjectHelper.TrySetProperty(ProxyHelper.UnProxy(repository).As(), x => x.IsChangeTrackingEnabled, _ => previous); + }, repository); + } + private static IUnitOfWorkManager GetUnitOfWorkManager( this IBasicRepository repository, [CallerMemberName] string callingMethodName = nameof(GetUnitOfWorkManager) 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 b881c07245..aa0b10913d 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 @@ -9,6 +9,7 @@ using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.Extensions.Logging; using Volo.Abp.Data; using Volo.Abp.Domain.Entities; using Volo.Abp.EntityFrameworkCore; @@ -107,7 +108,7 @@ public class EfCoreRepository : RepositoryBase, IE public async override Task InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) { - CheckReadOnly(); + CheckChangeTracking(); CheckAndSetId(entity); var dbContext = await GetDbContextAsync(); @@ -124,7 +125,7 @@ public class EfCoreRepository : RepositoryBase, IE public async override Task InsertManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) { - CheckReadOnly(); + CheckChangeTracking(); var entityArray = entities.ToArray(); var dbContext = await GetDbContextAsync(); cancellationToken = GetCancellationToken(cancellationToken); @@ -155,7 +156,7 @@ public class EfCoreRepository : RepositoryBase, IE public async override Task UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) { - CheckReadOnly(); + CheckChangeTracking(); var dbContext = await GetDbContextAsync(); dbContext.Attach(entity); @@ -172,7 +173,7 @@ public class EfCoreRepository : RepositoryBase, IE public async override Task UpdateManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) { - CheckReadOnly(); + CheckChangeTracking(); cancellationToken = GetCancellationToken(cancellationToken); if (BulkOperationProvider != null) @@ -199,7 +200,7 @@ public class EfCoreRepository : RepositoryBase, IE public async override Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) { - CheckReadOnly(); + CheckChangeTracking(); var dbContext = await GetDbContextAsync(); dbContext.Set().Remove(entity); @@ -212,7 +213,7 @@ public class EfCoreRepository : RepositoryBase, IE public async override Task DeleteManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) { - CheckReadOnly(); + CheckChangeTracking(); cancellationToken = GetCancellationToken(cancellationToken); if (BulkOperationProvider != null) @@ -276,12 +277,12 @@ public class EfCoreRepository : RepositoryBase, IE [Obsolete("Use GetQueryableAsync method.")] protected override IQueryable GetQueryable() { - return DbSet.AsQueryable().AsNoTrackingIf(IsReadOnly); + return DbSet.AsQueryable().AsNoTrackingIf(!IsChangeTrackingEnabled); } public async override Task> GetQueryableAsync() { - return (await GetDbSetAsync()).AsQueryable().AsNoTrackingIf(IsReadOnly); + return (await GetDbSetAsync()).AsQueryable().AsNoTrackingIf(!IsChangeTrackingEnabled); } protected async override Task SaveChangesAsync(CancellationToken cancellationToken) @@ -305,7 +306,7 @@ public class EfCoreRepository : RepositoryBase, IE public async override Task DeleteAsync(Expression> predicate, bool autoSave = false, CancellationToken cancellationToken = default) { - CheckReadOnly(); + CheckChangeTracking(); var dbContext = await GetDbContextAsync(); var dbSet = dbContext.Set(); @@ -323,7 +324,6 @@ public class EfCoreRepository : RepositoryBase, IE public async override Task DeleteDirectAsync(Expression> predicate, CancellationToken cancellationToken = default) { - CheckReadOnly(); var dbContext = await GetDbContextAsync(); var dbSet = dbContext.Set(); await dbSet.Where(predicate).ExecuteDeleteAsync(GetCancellationToken(cancellationToken)); @@ -428,18 +428,12 @@ public class EfCoreRepository : RepositoryBase, IE ); } - protected virtual void CheckReadOnly() + + protected virtual void CheckChangeTracking() { - if (IsReadOnly) + if (!IsChangeTrackingEnabled) { - throw new AbpRepositoryIsReadOnlyException($"Can not call " + - $"{nameof(InsertAsync)}, " + - $"{nameof(InsertManyAsync)}, " + - $"{nameof(UpdateAsync)}, " + - $"{nameof(UpdateManyAsync)}, " + - $"{nameof(DeleteAsync)}, " + - $"{nameof(DeleteManyAsync)}, " + - $"{nameof(DeleteDirectAsync)} methods on a read-only repository!"); + Logger.LogWarning("This repository has disabled change tracking. Your changes may not be saved!"); } } } @@ -473,14 +467,13 @@ public class EfCoreRepository : EfCoreRepository e.Id).FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken)) - : IsReadOnly + : !IsChangeTrackingEnabled ? await (await GetQueryableAsync()).OrderBy(e => e.Id).FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken)) : await (await GetDbSetAsync()).FindAsync(new object[] {id}, GetCancellationToken(cancellationToken)); } public virtual async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default) { - CheckReadOnly(); var entity = await FindAsync(id, cancellationToken: cancellationToken); if (entity == null) { @@ -492,7 +485,6 @@ public class EfCoreRepository : EfCoreRepository ids, bool autoSave = false, CancellationToken cancellationToken = default) { - CheckReadOnly(); cancellationToken = GetCancellationToken(cancellationToken); var entities = await (await GetDbSetAsync()).Where(x => ids.Contains(x.Id)).ToListAsync(cancellationToken); 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 f9b59e2c94..00be2f9522 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 @@ -407,7 +407,7 @@ public class RepositoryRegistration_Tests public class MyTestAggregateRootWithDefaultPkEmptyRepository : IMyTestAggregateRootWithDefaultPkEmptyRepository { - public bool IsReadOnly { get; set; } + public bool IsChangeTrackingEnabled { get; set; } } public class TestDbContextRegistrationOptions : AbpCommonDbContextRegistrationOptions diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/ReadOnlyRepository_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/ReadOnlyRepository_Tests.cs index 393b54dbe3..6213a6e114 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/ReadOnlyRepository_Tests.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/ReadOnlyRepository_Tests.cs @@ -2,13 +2,9 @@ using System; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; using Shouldly; -using Volo.Abp.Data; using Volo.Abp.Domain.Repositories; -using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.TestApp.Domain; -using Volo.Abp.TestApp.EntityFrameworkCore; using Volo.Abp.TestApp.Testing; using Volo.Abp.Uow; using Xunit; @@ -55,23 +51,42 @@ public class ReadOnlyRepository_Tests : TestAppTestBase>(); + await WithUnitOfWorkAsync(async () => { - var repository = GetRequiredService>(); - await repository.ToEfCoreRepository().InsertAsync(new Person(Guid.NewGuid(), "test", 18)); - var person = await repository.ToEfCoreRepository().FirstOrDefaultAsync(); - person.ShouldNotBeNull(); + await repository.InsertAsync(new Person(Guid.NewGuid(), "people1", 18)); + await repository.InsertAsync(new Person(Guid.NewGuid(), "people2", 19)); + await repository.InsertAsync(new Person(Guid.NewGuid(), "people3", 20)); + await repository.InsertAsync(new Person(Guid.NewGuid(), "people4", 21)); }); await WithUnitOfWorkAsync(async () => { - await Assert.ThrowsAsync(async () => + var db = await repository.GetDbContextAsync(); + db.ChangeTracker.Entries().Count().ShouldBe(0); + using (repository.DisableTracking()) { - var readonlyRepository = GetRequiredService>(); - await readonlyRepository.ToEfCoreRepository().As>().InsertAsync(new Person(Guid.NewGuid(), "test readonly", 18)); - }); + var p1 = await repository.FindAsync(x => x.Name == "people1"); + p1.ShouldNotBeNull(); + db.ChangeTracker.Entries().Count().ShouldBe(0); + } + + var p2 = await repository.FindAsync(x => x.Name == "people2"); + p2.ShouldNotBeNull(); + db.ChangeTracker.Entries().Count().ShouldBe(1); + + repository.DisableTracking(); + var p3 = await repository.FindAsync(x => x.Name == "people3"); + p3.ShouldNotBeNull(); + db.ChangeTracker.Entries().Count().ShouldBe(1); + + repository.EnableTracking(); + var p4 = await repository.FindAsync(x => x.Name == "people4"); + p4.ShouldNotBeNull(); + db.ChangeTracker.Entries().Count().ShouldBe(2); }); } From eb9b95471792d8350001215d5a67c229729f488c Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 27 Aug 2023 17:18:17 +0800 Subject: [PATCH 02/18] Add `RepositoryInterceptor`. --- .../Volo/Abp/Domain/AbpDddDomainModule.cs | 1 + .../Repositories/BasicRepositoryBase.cs | 22 +- .../DisableEntityChangeTrackingAttribute.cs | 12 + .../EnableEntityChangeTrackingAttribute.cs | 12 + .../EntityChangeTrackingAttribute.cs | 14 ++ .../EntityChangeTrackingProvider.cs | 19 ++ .../IEntityChangeTrackingProvider.cs | 10 + .../Abp/Domain/Repositories/IRepository.cs | 2 +- .../Domain/Repositories/RepositoryHelper.cs | 52 +++++ .../Repositories/RepositoryInterceptor.cs | 29 +++ .../RepositoryInterceptorRegistrar.cs | 22 ++ .../EntityFrameworkCore/EfCoreRepository.cs | 8 +- .../RepositoryRegistration_Tests.cs | 2 +- .../RepositoryInterceptor_Tests.cs | 211 ++++++++++++++++++ 14 files changed, 409 insertions(+), 7 deletions(-) create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/DisableEntityChangeTrackingAttribute.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EnableEntityChangeTrackingAttribute.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingProvider.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IEntityChangeTrackingProvider.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryHelper.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptor.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptorRegistrar.cs create mode 100644 framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/RepositoryInterceptor_Tests.cs diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs index 536e31e51f..2e67c42820 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs @@ -30,5 +30,6 @@ public class AbpDddDomainModule : AbpModule public override void PreConfigureServices(ServiceConfigurationContext context) { context.Services.AddConventionalRegistrar(new AbpRepositoryConventionalRegistrar()); + context.Services.OnRegistered(RepositoryInterceptorRegistrar.RegisterIfNeeded); } } 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 8257c4c8af..cd85acce50 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 @@ -40,7 +40,9 @@ public abstract class BasicRepositoryBase : public ILogger Logger => LazyServiceProvider.LazyGetService(provider => LoggerFactory?.CreateLogger(GetType().FullName!) ?? NullLogger.Instance); - public bool IsChangeTrackingEnabled { get; protected set; } = true; + public IEntityChangeTrackingProvider EntityChangeTrackingProvider => LazyServiceProvider.LazyGetRequiredService(); + + public bool? IsChangeTrackingEnabled { get; protected set; } protected BasicRepositoryBase() { @@ -114,6 +116,24 @@ public abstract class BasicRepositoryBase : { return CancellationTokenProvider.FallbackToProvider(preferredValue); } + + protected virtual bool ShouldTrackingEntityChange() + { + // If IsChangeTrackingEnabled is set, it has the highest priority. This generally means the repository is read-only. + if (IsChangeTrackingEnabled.HasValue) + { + return IsChangeTrackingEnabled.Value; + } + + // If Interface/Class/Method has Enable/DisableEntityChangeTrackingAttribute, it has the second highest priority. + if (EntityChangeTrackingProvider.Enabled.HasValue) + { + return EntityChangeTrackingProvider.Enabled.Value; + } + + // Default behavior is tracking entity change. + return true; + } } public abstract class BasicRepositoryBase : BasicRepositoryBase, IBasicRepository diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/DisableEntityChangeTrackingAttribute.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/DisableEntityChangeTrackingAttribute.cs new file mode 100644 index 0000000000..d6a73cba32 --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/DisableEntityChangeTrackingAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Volo.Abp.Domain.Repositories; + +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface)] +public class DisableEntityChangeTrackingAttribute : EntityChangeTrackingAttribute +{ + public DisableEntityChangeTrackingAttribute() + : base(false) + { + } +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EnableEntityChangeTrackingAttribute.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EnableEntityChangeTrackingAttribute.cs new file mode 100644 index 0000000000..a94df978fb --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EnableEntityChangeTrackingAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Volo.Abp.Domain.Repositories; + +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface)] +public class EnableEntityChangeTrackingAttribute : EntityChangeTrackingAttribute +{ + public EnableEntityChangeTrackingAttribute() + : base(true) + { + } +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs new file mode 100644 index 0000000000..0bb6ca82e4 --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace Volo.Abp.Domain.Repositories; + +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface)] +public class EntityChangeTrackingAttribute : Attribute +{ + public bool Enabled { get; set; } + + public EntityChangeTrackingAttribute(bool enabled) + { + Enabled = enabled; + } +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingProvider.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingProvider.cs new file mode 100644 index 0000000000..19c10e9a3f --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingProvider.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Domain.Repositories; + +public class EntityChangeTrackingProvider : IEntityChangeTrackingProvider, ISingletonDependency +{ + public bool? Enabled => _current.Value; + + private readonly AsyncLocal _current = new AsyncLocal(); + + public IDisposable Change(bool? enabled) + { + var previousValue = Enabled; + _current.Value = enabled; + return new DisposeAction(() => _current.Value = previousValue); + } +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IEntityChangeTrackingProvider.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IEntityChangeTrackingProvider.cs new file mode 100644 index 0000000000..f1db1584fa --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IEntityChangeTrackingProvider.cs @@ -0,0 +1,10 @@ +using System; + +namespace Volo.Abp.Domain.Repositories; + +public interface IEntityChangeTrackingProvider +{ + bool? Enabled { get; } + + IDisposable Change(bool? enabled); +} 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 fda0c7ed94..dc39255b25 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 @@ -12,7 +12,7 @@ namespace Volo.Abp.Domain.Repositories; /// public interface IRepository { - bool IsChangeTrackingEnabled { get; } + bool? IsChangeTrackingEnabled { get; } } public interface IRepository : IReadOnlyRepository, IBasicRepository diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryHelper.cs new file mode 100644 index 0000000000..014970c527 --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryHelper.cs @@ -0,0 +1,52 @@ +using System.Linq; +using System.Reflection; +using JetBrains.Annotations; + +namespace Volo.Abp.Domain.Repositories; + +public static class RepositoryHelper +{ + public static bool IsEntityChangeTrackingType(TypeInfo implementationType) + { + return HasEntityChangeTrackingAttribute(implementationType) || AnyMethodHasEntityChangeTrackingAttribute(implementationType); + } + + public static bool IsEntityChangeTrackingMethod([NotNull] MethodInfo methodInfo, out EntityChangeTrackingAttribute? entityChangeTrackingAttribute) + { + Check.NotNull(methodInfo, nameof(methodInfo)); + + //Method declaration + var attrs = methodInfo.GetCustomAttributes(true).OfType().ToArray(); + if (attrs.Any()) + { + entityChangeTrackingAttribute = attrs.First(); + return true; + } + + if (methodInfo.DeclaringType != null) + { + //Class declaration + attrs = methodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes(true).OfType().ToArray(); + if (attrs.Any()) + { + entityChangeTrackingAttribute = attrs.First(); + return true; + } + } + + entityChangeTrackingAttribute = null; + return false; + } + + private static bool AnyMethodHasEntityChangeTrackingAttribute(TypeInfo implementationType) + { + return implementationType + .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Any(HasEntityChangeTrackingAttribute); + } + + private static bool HasEntityChangeTrackingAttribute(MemberInfo methodInfo) + { + return methodInfo.IsDefined(typeof(EntityChangeTrackingAttribute), true); + } +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptor.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptor.cs new file mode 100644 index 0000000000..f322d1e87d --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptor.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; + +namespace Volo.Abp.Domain.Repositories; + +public class RepositoryInterceptor : AbpInterceptor, ITransientDependency +{ + private readonly IEntityChangeTrackingProvider _entityChangeTrackingProvider; + + public RepositoryInterceptor(IEntityChangeTrackingProvider entityChangeTrackingProvider) + { + _entityChangeTrackingProvider = entityChangeTrackingProvider; + } + + public async override Task InterceptAsync(IAbpMethodInvocation invocation) + { + if (!RepositoryHelper.IsEntityChangeTrackingMethod(invocation.Method, out var changeTrackingAttribute)) + { + await invocation.ProceedAsync(); + return; + } + + using (_entityChangeTrackingProvider.Change(changeTrackingAttribute?.Enabled)) + { + await invocation.ProceedAsync(); + } + } +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptorRegistrar.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptorRegistrar.cs new file mode 100644 index 0000000000..538fa72774 --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptorRegistrar.cs @@ -0,0 +1,22 @@ +using System; +using System.Reflection; +using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; + +namespace Volo.Abp.Domain.Repositories; + +public class RepositoryInterceptorRegistrar +{ + public static void RegisterIfNeeded(IOnServiceRegistredContext context) + { + if (ShouldIntercept(context.ImplementationType)) + { + context.Interceptors.TryAdd(); + } + } + + private static bool ShouldIntercept(Type type) + { + return !DynamicProxyIgnoreTypes.Contains(type) && RepositoryHelper.IsEntityChangeTrackingType(type.GetTypeInfo()); + } +} 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 aa0b10913d..ff40c8a60b 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 @@ -277,12 +277,12 @@ public class EfCoreRepository : RepositoryBase, IE [Obsolete("Use GetQueryableAsync method.")] protected override IQueryable GetQueryable() { - return DbSet.AsQueryable().AsNoTrackingIf(!IsChangeTrackingEnabled); + return DbSet.AsQueryable().AsNoTrackingIf(!ShouldTrackingEntityChange()); } public async override Task> GetQueryableAsync() { - return (await GetDbSetAsync()).AsQueryable().AsNoTrackingIf(!IsChangeTrackingEnabled); + return (await GetDbSetAsync()).AsQueryable().AsNoTrackingIf(!ShouldTrackingEntityChange()); } protected async override Task SaveChangesAsync(CancellationToken cancellationToken) @@ -431,7 +431,7 @@ public class EfCoreRepository : RepositoryBase, IE protected virtual void CheckChangeTracking() { - if (!IsChangeTrackingEnabled) + if (!ShouldTrackingEntityChange()) { Logger.LogWarning("This repository has disabled change tracking. Your changes may not be saved!"); } @@ -467,7 +467,7 @@ public class EfCoreRepository : EfCoreRepository e.Id).FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken)) - : !IsChangeTrackingEnabled + : !ShouldTrackingEntityChange() ? await (await GetQueryableAsync()).OrderBy(e => e.Id).FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken)) : await (await GetDbSetAsync()).FindAsync(new object[] {id}, GetCancellationToken(cancellationToken)); } 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 00be2f9522..ca98017e0a 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 @@ -407,7 +407,7 @@ public class RepositoryRegistration_Tests public class MyTestAggregateRootWithDefaultPkEmptyRepository : IMyTestAggregateRootWithDefaultPkEmptyRepository { - public bool IsChangeTrackingEnabled { get; set; } + public bool? IsChangeTrackingEnabled { get; set; } } public class TestDbContextRegistrationOptions : AbpCommonDbContextRegistrationOptions diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/RepositoryInterceptor_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/RepositoryInterceptor_Tests.cs new file mode 100644 index 0000000000..8230a6ec7e --- /dev/null +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/RepositoryInterceptor_Tests.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.TestApp.Domain; +using Volo.Abp.TestApp.Testing; +using Xunit; + +namespace Volo.Abp.EntityFrameworkCore.Repositories; + +public class RepositoryInterceptor_Tests : TestAppTestBase +{ + [Fact] + public async Task ReadOnly_Repository_Should_Not_Track_Entities() + { + await AddSomePeopleAsync(); + + var readOnlyRepository = GetRequiredService>(); + + await WithUnitOfWorkAsync(async () => + { + var db = await readOnlyRepository.GetDbContextAsync(); + db.ChangeTracker.Entries().Count().ShouldBe(0); + + var service = GetRequiredService(); + var list = await service.GetPeoplesAsync(); + list.Count.ShouldBeGreaterThan(0); + + // RepositoryInterceptor always not track entities + db.ChangeTracker.Entries().Count().ShouldBe(0); + }); + } + + [Fact] + public async Task RepositoryInterceptor_Test() + { + await AddSomePeopleAsync(); + + var repository = GetRequiredService>(); + + await WithUnitOfWorkAsync(async () => + { + var service = GetRequiredService(); + var db = await repository.GetDbContextAsync(); + db.ChangeTracker.Entries().Count().ShouldBe(0); + + var list = await service.GetPeoplesAsync(); + list.Count.ShouldBeGreaterThan(0); + + db.ChangeTracker.Entries().Count().ShouldBe(1); // Track one entity from GetPeopleAsync + }); + + await WithUnitOfWorkAsync(async () => + { + var service = GetRequiredService(); + var db = await repository.GetDbContextAsync(); + db.ChangeTracker.Entries().Count().ShouldBe(0); + + var list = await service.GetPeoplesAsync(); + list.Count.ShouldBeGreaterThan(0); + + db.ChangeTracker.Entries().Count().ShouldBe(1); // Track one entity from GetPeoplesAsync + }); + + await WithUnitOfWorkAsync(async () => + { + var service = GetRequiredService(); + var db = await repository.GetDbContextAsync(); + db.ChangeTracker.Entries().Count().ShouldBe(0); + + var list = await service.GetPeoplesAsync(); + list.Count.ShouldBeGreaterThan(0); + + db.ChangeTracker.Entries().Count().ShouldBe(1); // Track one entity from GetPeoplesAsync + }); + + await WithUnitOfWorkAsync(async () => + { + var service = GetRequiredService(); + var db = await repository.GetDbContextAsync(); + db.ChangeTracker.Entries().Count().ShouldBe(0); + + var entityChangeTrackingProvider = GetRequiredService(); + // Disable entity change tracking + using (entityChangeTrackingProvider.Change(false)) + { + var list = await service.GetPeoplesAsync(); + list.Count.ShouldBeGreaterThan(0); + db.ChangeTracker.Entries().Count().ShouldBe(0); + } + }); + } + + private async Task AddSomePeopleAsync() + { + var repository = GetRequiredService>(); + await repository.InsertAsync(new Person(Guid.NewGuid(), "people1", 18)); + await repository.InsertAsync(new Person(Guid.NewGuid(), "people2", 19)); + await repository.InsertAsync(new Person(Guid.NewGuid(), "people3", 20)); + await repository.InsertAsync(new Person(Guid.NewGuid(), "people4", 21)); + } +} + +public class MyService : ITransientDependency +{ + private readonly IRepository _repository; + + public MyService(IRepository repository) + { + _repository = repository; + } + + [DisableEntityChangeTracking] + public virtual async Task> GetPeoplesAsync() + { + await GetPeopleAsync(); + return await _repository.GetListAsync(); + } + + [EnableEntityChangeTracking] + public virtual async Task GetPeopleAsync() + { + var p1 = await _repository.FindAsync(x => x.Name == "people1"); + return p1; + } +} + +public class MyReadOnlyService : MyService +{ + public MyReadOnlyService(IReadOnlyRepository repository) + : base(repository.As>()) + { + } +} + + +[EnableEntityChangeTracking] +public class MyServiceEnableEntityChangeTracking : ITransientDependency +{ + private readonly IRepository _repository; + + public MyServiceEnableEntityChangeTracking(IRepository repository) + { + _repository = repository; + } + + public virtual async Task> GetPeoplesAsync() + { + var p1 = await GetPeopleAsync(); + var p2 = await _repository.FindAsync(x => x.Name == "people2"); + + return new List {p1, p2}; + } + + [DisableEntityChangeTracking] + public virtual async Task GetPeopleAsync() + { + var p1 = await _repository.FindAsync(x => x.Name == "people1"); + return p1; + } +} + + +[EnableEntityChangeTracking] +public interface IMyServiceEnableEntityChangeTrackingOnInterface +{ + +} + +public class MyServiceEnableEntityChangeTrackingOnInterface : IMyServiceEnableEntityChangeTrackingOnInterface, ITransientDependency +{ + private readonly IRepository _repository; + + public MyServiceEnableEntityChangeTrackingOnInterface(IRepository repository) + { + _repository = repository; + } + + public virtual async Task> GetPeoplesAsync() + { + var p1 = await GetPeopleAsync(); + var p2 = await _repository.FindAsync(x => x.Name == "people2"); + + return new List {p1, p2}; + } + + [DisableEntityChangeTracking] + public virtual async Task GetPeopleAsync() + { + var p1 = await _repository.FindAsync(x => x.Name == "people1"); + return p1; + } +} + +public class MyServiceChangeTrackingByEntityChangeTrackingProvider : ITransientDependency +{ + private readonly IRepository _repository; + + public MyServiceChangeTrackingByEntityChangeTrackingProvider(IRepository repository) + { + _repository = repository; + } + + public virtual async Task> GetPeoplesAsync() + { + return await _repository.GetListAsync(); + } +} From b6c41c9f28963f1b7d271f7066db3ad3ff1475ab Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 28 Aug 2023 10:53:28 +0800 Subject: [PATCH 03/18] Delete AbpRepositoryIsReadOnlyException.cs --- .../Data/AbpRepositoryIsReadOnlyException.cs | 22 ------------------- 1 file changed, 22 deletions(-) delete mode 100644 framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpRepositoryIsReadOnlyException.cs diff --git a/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpRepositoryIsReadOnlyException.cs b/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpRepositoryIsReadOnlyException.cs deleted file mode 100644 index 6c648fd022..0000000000 --- a/framework/src/Volo.Abp.Data/Volo/Abp/Data/AbpRepositoryIsReadOnlyException.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Volo.Abp.Data; - -public class AbpRepositoryIsReadOnlyException : AbpException -{ - /// - /// Creates a new object. - /// - public AbpRepositoryIsReadOnlyException() - { - - } - - /// - /// Creates a new object. - /// - /// Exception message - public AbpRepositoryIsReadOnlyException(string message) - : base(message) - { - - } -} From 4dfdf48e75faa4f758ddb837eaf162a3fd9d378f Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 29 Aug 2023 15:54:59 +0800 Subject: [PATCH 04/18] Make `EntityChangeTrackingAttribute` abstract. --- .../Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs | 2 +- .../Volo/Abp/Domain/Repositories/RepositoryHelper.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs index 0bb6ca82e4..23bc6ef851 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs @@ -3,7 +3,7 @@ using System; namespace Volo.Abp.Domain.Repositories; [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface)] -public class EntityChangeTrackingAttribute : Attribute +public abstract class EntityChangeTrackingAttribute : Attribute { public bool Enabled { get; set; } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryHelper.cs index 014970c527..2c57dd9640 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryHelper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryHelper.cs @@ -45,8 +45,8 @@ public static class RepositoryHelper .Any(HasEntityChangeTrackingAttribute); } - private static bool HasEntityChangeTrackingAttribute(MemberInfo methodInfo) + private static bool HasEntityChangeTrackingAttribute(MemberInfo memberInfo) { - return methodInfo.IsDefined(typeof(EntityChangeTrackingAttribute), true); + return memberInfo.IsDefined(typeof(EntityChangeTrackingAttribute), true); } } From 1b35f9c9f6c9255c4e4e4e81758505e24644934d Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 29 Aug 2023 16:05:23 +0800 Subject: [PATCH 05/18] Rename `Enabled` of `EntityChangeTrackingAttribute` to `IsEnabled`. --- .../Domain/Repositories/EntityChangeTrackingAttribute.cs | 6 +++--- .../Volo/Abp/Domain/Repositories/RepositoryInterceptor.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs index 23bc6ef851..3719cd3c0c 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs @@ -5,10 +5,10 @@ namespace Volo.Abp.Domain.Repositories; [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface)] public abstract class EntityChangeTrackingAttribute : Attribute { - public bool Enabled { get; set; } + public virtual bool IsEnabled { get; set; } - public EntityChangeTrackingAttribute(bool enabled) + public EntityChangeTrackingAttribute(bool isEnabled) { - Enabled = enabled; + IsEnabled = isEnabled; } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptor.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptor.cs index f322d1e87d..e9727b932f 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptor.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptor.cs @@ -21,7 +21,7 @@ public class RepositoryInterceptor : AbpInterceptor, ITransientDependency return; } - using (_entityChangeTrackingProvider.Change(changeTrackingAttribute?.Enabled)) + using (_entityChangeTrackingProvider.Change(changeTrackingAttribute?.IsEnabled)) { await invocation.ProceedAsync(); } From a3b443f1470de4f66eb56f0627616ad70fa366b5 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 1 Sep 2023 10:30:39 +0800 Subject: [PATCH 06/18] Prevent to disable account authenticated user self. --- .../Volo/Abp/Identity/IdentityUserAppService.cs | 5 ++++- .../Pages/Identity/UserManagement.razor | 9 ++++++--- .../Pages/Identity/UserManagement.razor.cs | 3 ++- .../Pages/Identity/Users/EditModal.cshtml | 6 ++++-- .../Pages/Identity/Users/EditModal.cshtml.cs | 4 +++- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs index 498184b9cc..fa1beb9124 100644 --- a/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs +++ b/modules/identity/src/Volo.Abp.Identity.Application/Volo/Abp/Identity/IdentityUserAppService.cs @@ -173,7 +173,10 @@ public class IdentityUserAppService : IdentityAppServiceBase, IIdentityUserAppSe (await UserManager.SetPhoneNumberAsync(user, input.PhoneNumber)).CheckErrors(); } - (await UserManager.SetLockoutEnabledAsync(user, input.LockoutEnabled)).CheckErrors(); + if (user.Id != CurrentUser.Id) + { + (await UserManager.SetLockoutEnabledAsync(user, input.LockoutEnabled)).CheckErrors(); + } user.Name = input.Name; user.Surname = input.Surname; diff --git a/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor b/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor index afa3fd3897..0f66c7131e 100644 --- a/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor +++ b/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor @@ -129,9 +129,12 @@ @L["DisplayName:IsActive"] - - @L["DisplayName:LockoutEnabled"] - + @if (!IsEditCurrentUser) + { + + @L["DisplayName:LockoutEnabled"] + + } diff --git a/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor.cs b/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor.cs index 403e46601d..b219c820a6 100644 --- a/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor.cs +++ b/modules/identity/src/Volo.Abp.Identity.Blazor/Pages/Identity/UserManagement.razor.cs @@ -41,6 +41,7 @@ public partial class UserManagement private List UserManagementTableColumns => TableColumns.Get(); private TextRole _passwordTextRole = TextRole.Password; + public bool IsEditCurrentUser { get; set; } public UserManagement() { @@ -119,7 +120,7 @@ public partial class UserManagement try { EditModalSelectedTab = DefaultSelectedTab; - + IsEditCurrentUser = entity.Id == CurrentUser.Id; var userRoleNames = (await AppService.GetRolesAsync(entity.Id)).Items.Select(r => r.Name).ToList(); EditUserRoles = Roles.Select(x => new AssignedRoleViewModel diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml index 28d0fd6da6..3e5a59fe57 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml @@ -39,8 +39,10 @@ - - + @if (!Model.IsEditCurrentUser) + { + + } @foreach (var propertyInfo in ObjectExtensionManager.Instance.GetProperties()) { if (!propertyInfo.Name.EndsWith("_Text")) diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs index 2b15d794ac..55365498a7 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml.cs @@ -22,6 +22,8 @@ public class EditModalModel : IdentityPageModel protected IIdentityUserAppService IdentityUserAppService { get; } + public bool IsEditCurrentUser { get; set; } + public EditModalModel(IIdentityUserAppService identityUserAppService) { IdentityUserAppService = identityUserAppService; @@ -30,7 +32,7 @@ public class EditModalModel : IdentityPageModel public virtual async Task OnGetAsync(Guid id) { UserInfo = ObjectMapper.Map(await IdentityUserAppService.GetAsync(id)); - + IsEditCurrentUser = CurrentUser.Id == id; Roles = ObjectMapper.Map, AssignedRoleViewModel[]>((await IdentityUserAppService.GetAssignableRolesAsync()).Items); var userRoleNames = (await IdentityUserAppService.GetRolesAsync(UserInfo.Id)).Items.Select(r => r.Name).ToList(); From 4da3cf412a6c5fc462ab0b92b6d5a2a3289fed7c Mon Sep 17 00:00:00 2001 From: Masum ULU Date: Fri, 1 Sep 2023 10:06:48 +0300 Subject: [PATCH 07/18] Refactor error handler for 401 status code --- .../src/lib/handlers/error.handler.ts | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts index cc7f733798..3077e22b00 100644 --- a/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts +++ b/npm/ng-packs/packages/theme-shared/src/lib/handlers/error.handler.ts @@ -1,4 +1,4 @@ - import { +import { AuthService, HttpErrorReporterService, LocalizationParam, @@ -86,7 +86,6 @@ export class ErrorHandler { protected httpErrorConfig: HttpErrorConfig; protected sessionStateService: SessionStateService; private authService: AuthService; - constructor(protected injector: Injector) { this.httpErrorReporter = injector.get(HttpErrorReporterService); @@ -146,7 +145,7 @@ export class ErrorHandler { }; if (err instanceof HttpErrorResponse && err.headers.get('Abp-Tenant-Resolve-Error')) { - this.sessionStateService.setTenant(null) + this.sessionStateService.setTenant(null); this.authService.logout().subscribe(); return; } @@ -162,18 +161,11 @@ export class ErrorHandler { } else { switch (err.status) { case 401: - this.canCreateCustomError(401) - ? this.show401Page() - : this.showError( - { - key: DEFAULT_ERROR_LOCALIZATIONS.defaultError401.title, - defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.title, - }, - { - key: DEFAULT_ERROR_LOCALIZATIONS.defaultError401.details, - defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.details, - }, - ).subscribe(() => this.navigateToLogin()); + if (this.canCreateCustomError(401)) { + this.show401Page(); + } + + this.navigateToLogin(); break; case 403: this.createErrorComponent({ From c0cec5bdb01201da90f6ec70eba4147bef748f18 Mon Sep 17 00:00:00 2001 From: Enis Necipoglu Date: Fri, 1 Sep 2023 11:46:31 +0300 Subject: [PATCH 08/18] Add default protected constructors to CmsKit Entities --- .../cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Blog.cs | 4 ++++ .../src/Volo.CmsKit.Domain/Volo/CmsKit/Menus/MenuItem.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Blog.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Blog.cs index 32ce890ff0..fb976fd957 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Blog.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/Blog.cs @@ -16,6 +16,10 @@ public class Blog : FullAuditedAggregateRoot, IMultiTenant public virtual Guid? TenantId { get; protected set; } + protected Blog() + { + } + protected internal Blog( Guid id, [NotNull] string name, diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Menus/MenuItem.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Menus/MenuItem.cs index ab7e23df8b..fa2505eecc 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Menus/MenuItem.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Menus/MenuItem.cs @@ -41,6 +41,10 @@ public class MenuItem : AuditedAggregateRoot, IMultiTenant public Guid? TenantId { get; protected set; } + protected MenuItem() + { + } + public MenuItem(Guid id, [NotNull] string displayName, [NotNull] string url, From f00c880da6674c8368d52a8e7b88316a7a9dca60 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 1 Sep 2023 17:40:23 +0800 Subject: [PATCH 09/18] Add empty procted constructor to all the entities. --- .../Volo/Abp/BlobStoring/Database/DatabaseBlob.cs | 4 ++++ .../Volo/Abp/BlobStoring/Database/DatabaseBlobContainer.cs | 4 ++++ .../src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogFeature.cs | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlob.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlob.cs index 8b51868e27..214ebff508 100644 --- a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlob.cs +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlob.cs @@ -17,6 +17,10 @@ public class DatabaseBlob : AggregateRoot, IMultiTenant [DisableAuditing] public virtual byte[] Content { get; protected set; } + protected DatabaseBlob() + { + } + public DatabaseBlob(Guid id, Guid containerId, [NotNull] string name, [NotNull] byte[] content, Guid? tenantId = null) : base(id) { diff --git a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlobContainer.cs b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlobContainer.cs index 0ae8a3ad4b..5186248a40 100644 --- a/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlobContainer.cs +++ b/modules/blob-storing-database/src/Volo.Abp.BlobStoring.Database.Domain/Volo/Abp/BlobStoring/Database/DatabaseBlobContainer.cs @@ -11,6 +11,10 @@ public class DatabaseBlobContainer : AggregateRoot, IMultiTenant public virtual string Name { get; protected set; } + protected DatabaseBlobContainer() + { + } + public DatabaseBlobContainer(Guid id, [NotNull] string name, Guid? tenantId = null) : base(id) { diff --git a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogFeature.cs b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogFeature.cs index 39e2e2c65c..5571e776b1 100644 --- a/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogFeature.cs +++ b/modules/cms-kit/src/Volo.CmsKit.Domain/Volo/CmsKit/Blogs/BlogFeature.cs @@ -13,6 +13,10 @@ public class BlogFeature : FullAuditedAggregateRoot public bool IsEnabled { get; protected internal set; } + protected BlogFeature() + { + } + public BlogFeature(Guid blogId, [NotNull] string featureName, bool isEnabled = true) { BlogId = blogId; From 6d029f5fd1ee311bf08a61cf714771a365632106 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 3 Sep 2023 19:05:33 +0800 Subject: [PATCH 10/18] Remove `CheckChangeTracking` method. --- .../EntityFrameworkCore/EfCoreRepository.cs | 18 ------------------ 1 file changed, 18 deletions(-) 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 0829265044..3f22385ea6 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 @@ -108,7 +108,6 @@ public class EfCoreRepository : RepositoryBase, IE public async override Task InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) { - CheckChangeTracking(); CheckAndSetId(entity); var dbContext = await GetDbContextAsync(); @@ -125,7 +124,6 @@ public class EfCoreRepository : RepositoryBase, IE public async override Task InsertManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) { - CheckChangeTracking(); var entityArray = entities.ToArray(); if (entityArray.IsNullOrEmpty()) { @@ -161,7 +159,6 @@ public class EfCoreRepository : RepositoryBase, IE public async override Task UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) { - CheckChangeTracking(); var dbContext = await GetDbContextAsync(); dbContext.Attach(entity); @@ -184,8 +181,6 @@ public class EfCoreRepository : RepositoryBase, IE return; } - CheckChangeTracking(); - cancellationToken = GetCancellationToken(cancellationToken); if (BulkOperationProvider != null) @@ -212,7 +207,6 @@ public class EfCoreRepository : RepositoryBase, IE public async override Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) { - CheckChangeTracking(); var dbContext = await GetDbContextAsync(); dbContext.Set().Remove(entity); @@ -231,8 +225,6 @@ public class EfCoreRepository : RepositoryBase, IE return; } - CheckChangeTracking(); - cancellationToken = GetCancellationToken(cancellationToken); if (BulkOperationProvider != null) @@ -325,7 +317,6 @@ public class EfCoreRepository : RepositoryBase, IE public async override Task DeleteAsync(Expression> predicate, bool autoSave = false, CancellationToken cancellationToken = default) { - CheckChangeTracking(); var dbContext = await GetDbContextAsync(); var dbSet = dbContext.Set(); @@ -446,15 +437,6 @@ public class EfCoreRepository : RepositoryBase, IE true ); } - - - protected virtual void CheckChangeTracking() - { - if (!ShouldTrackingEntityChange()) - { - Logger.LogWarning("This repository has disabled change tracking. Your changes may not be saved!"); - } - } } public class EfCoreRepository : EfCoreRepository, From 47cd7268d0a9e7b1b3b35290d4f12ef7895f0453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 3 Sep 2023 14:11:25 +0300 Subject: [PATCH 11/18] Remove unnecessary TKey restriction --- .../Repositories/ISupportsExplicitLoading.cs | 4 ++-- .../Domain/Repositories/RepositoryExtensions.cs | 16 ++++++++-------- .../EntityFrameworkCore/EfCoreRepository.cs | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/ISupportsExplicitLoading.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/ISupportsExplicitLoading.cs index 1c51954f57..5131486c1e 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/ISupportsExplicitLoading.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/ISupportsExplicitLoading.cs @@ -7,8 +7,8 @@ using Volo.Abp.Domain.Entities; namespace Volo.Abp.Domain.Repositories; -public interface ISupportsExplicitLoading - where TEntity : class, IEntity +public interface ISupportsExplicitLoading + where TEntity : class, IEntity { Task EnsureCollectionLoadedAsync( TEntity entity, diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs index cbebf62196..e5c2eb9a3f 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs @@ -15,16 +15,16 @@ namespace Volo.Abp.Domain.Repositories; public static class RepositoryExtensions { - public async static Task EnsureCollectionLoadedAsync( - this IBasicRepository repository, + public async static Task EnsureCollectionLoadedAsync( + this IBasicRepository repository, TEntity entity, Expression>> propertyExpression, CancellationToken cancellationToken = default ) - where TEntity : class, IEntity + where TEntity : class, IEntity where TProperty : class { - var repo = ProxyHelper.UnProxy(repository) as ISupportsExplicitLoading; + var repo = ProxyHelper.UnProxy(repository) as ISupportsExplicitLoading; if (repo != null) { await repo.EnsureCollectionLoadedAsync(entity, propertyExpression, cancellationToken); @@ -40,7 +40,7 @@ public static class RepositoryExtensions where TEntity : class, IEntity where TProperty : class { - var repo = ProxyHelper.UnProxy(repository) as ISupportsExplicitLoading; + var repo = ProxyHelper.UnProxy(repository) as ISupportsExplicitLoading; if (repo != null) { await repo.EnsurePropertyLoadedAsync(entity, propertyExpression, cancellationToken); @@ -60,12 +60,12 @@ public static class RepositoryExtensions } } - public async static Task EnsureExistsAsync( - this IRepository repository, + public async static Task EnsureExistsAsync( + this IRepository repository, Expression> expression, CancellationToken cancellationToken = default ) - where TEntity : class, IEntity + where TEntity : class, IEntity { if (!await repository.AnyAsync(expression, cancellationToken)) { 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 325ba8345d..581396a390 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 @@ -464,7 +464,7 @@ public class EfCoreRepository : RepositoryBase, IE public class EfCoreRepository : EfCoreRepository, IEfCoreRepository, - ISupportsExplicitLoading + ISupportsExplicitLoading where TDbContext : IEfCoreDbContext where TEntity : class, IEntity From 19d4ccc502e332d1a2dfa6a75e133407698f4cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 3 Sep 2023 14:49:12 +0300 Subject: [PATCH 12/18] Comment on classes. --- .../DisableEntityChangeTrackingAttribute.cs | 3 +++ .../EnableEntityChangeTrackingAttribute.cs | 3 +++ .../Domain/Repositories/RepositoryExtensions.cs | 14 ++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/DisableEntityChangeTrackingAttribute.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/DisableEntityChangeTrackingAttribute.cs index d6a73cba32..76a2bdeaa1 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/DisableEntityChangeTrackingAttribute.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/DisableEntityChangeTrackingAttribute.cs @@ -2,6 +2,9 @@ using System; namespace Volo.Abp.Domain.Repositories; +/// +/// Ensures that the change tracking in enabled for the given method or class. +/// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface)] public class DisableEntityChangeTrackingAttribute : EntityChangeTrackingAttribute { diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EnableEntityChangeTrackingAttribute.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EnableEntityChangeTrackingAttribute.cs index a94df978fb..9765d3253f 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EnableEntityChangeTrackingAttribute.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EnableEntityChangeTrackingAttribute.cs @@ -2,6 +2,9 @@ using System; namespace Volo.Abp.Domain.Repositories; +/// +/// Ensures that the change tracking in enabled for the given method or class. +/// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface)] public class EnableEntityChangeTrackingAttribute : EntityChangeTrackingAttribute { diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs index 710140d2ec..d40449a2e4 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs @@ -145,11 +145,25 @@ public static class RepositoryExtensions } } + /// + /// Disables change tracking mechanism for the given repository. + /// + /// A repository object + /// + /// A disposable object. Dispose it to restore change tracking mechanism back to its previous state. + /// public static IDisposable DisableTracking(this IRepository repository) { return Tracking(repository, false); } + /// + /// Enables change tracking mechanism for the given repository. + /// + /// A repository object + /// + /// A disposable object. Dispose it to restore change tracking mechanism back to its previous state. + /// public static IDisposable EnableTracking(this IRepository repository) { return Tracking(repository, true); From b33ff894fab8b5003cbdc1c34dbf6840c23f7abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 3 Sep 2023 14:51:57 +0300 Subject: [PATCH 13/18] Remove unused namespace --- .../Volo/Abp/Application/Services/ApplicationService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs index ba82e5bdc2..f0d23fa865 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/ApplicationService.cs @@ -1,4 +1,3 @@ -using JetBrains.Annotations; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; From 8207c915ed3fb837764ab843a31d2670d97b7ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 3 Sep 2023 15:08:06 +0300 Subject: [PATCH 14/18] Document how to use the change tracking control. --- docs/en/Entity-Framework-Core.md | 6 ++- docs/en/Repositories.md | 71 +++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/docs/en/Entity-Framework-Core.md b/docs/en/Entity-Framework-Core.md index 1f8b9e22f0..cb9393b1aa 100644 --- a/docs/en/Entity-Framework-Core.md +++ b/docs/en/Entity-Framework-Core.md @@ -598,10 +598,14 @@ See also [lazy loading document](https://docs.microsoft.com/en-us/ef/core/queryi ABP Framework provides read-only [repository](Repositories.md) interfaces (`IReadOnlyRepository<...>` or `IReadOnlyBasicRepository<...>`) to explicitly indicate that your purpose is to query data, but not change it. If so, you can inject these interfaces into your services. -Entity Framework Core read-only repository implementation uses [EF Core's No-Tracking feature](https://learn.microsoft.com/en-us/ef/core/querying/tracking#no-tracking-queries). That means the entities returned from the repository will not be tracked by the EF Core [change tracker](https://learn.microsoft.com/en-us/ef/core/change-tracking/), because it is expected that you won't update entities queried from a read-only repository. +Entity Framework Core read-only repository implementation uses [EF Core's No-Tracking feature](https://learn.microsoft.com/en-us/ef/core/querying/tracking#no-tracking-queries). That means the entities returned from the repository will not be tracked by the EF Core [change tracker](https://learn.microsoft.com/en-us/ef/core/change-tracking/), because it is expected that you won't update entities queried from a read-only repository. If you need to track the entities, you can still use the [AsTracking()](https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.entityframeworkqueryableextensions.astracking) extension method on the LINQ expression, or `EnableTracking()` extension method on the repository object (See *Enabling / Disabling the Change Tracking* section in this document). > This behavior works only if the repository object is injected with one of the read-only repository interfaces (`IReadOnlyRepository<...>` or `IReadOnlyBasicRepository<...>`). It won't work if you have injected a standard repository (e.g. `IRepository<...>`) then casted it to a read-only repository interface. +## Enabling / Disabling the Change Tracking + +In addition to the read-only repositories, ABP allows to manually control the change tracking behavior for querying objects. Please see the *Enabling / Disabling the Change Tracking* section of the [Repositories documentation](Repositories.md) to learn how to use it. + ## Access to the EF Core API In most cases, you want to hide EF Core APIs behind a repository (this is the main purpose of the repository pattern). However, if you want to access the `DbContext` instance over the repository, you can use `GetDbContext()` or `GetDbSet()` extension methods. Example: diff --git a/docs/en/Repositories.md b/docs/en/Repositories.md index a93460bbdd..ec55db7f3d 100644 --- a/docs/en/Repositories.md +++ b/docs/en/Repositories.md @@ -176,6 +176,75 @@ Some features (like soft-delete, multi-tenancy and audit logging) won't work, so The `EnsureExistsAsync` extension method accepts entity id or entities query expression to ensure entities exist, otherwise, it will throw `EntityNotFoundException`. +### Enabling / Disabling the Change Tracking + +ABP provides repository extension methods and attributes those can be used to control the change tracking behavior for queried entities in the underlying database provider. + +Disabling change tracking can gain performance if you query many entities from the database for read-only purposes. Querying single or a few entities won't make much performance difference, but you are free to use it whenever you like. + +> If the underlying database provider doesn't support change tracking, then this system won't have any effect. [Entity Framework Core](Entity-Framework-Core.md) supports change tracking, for example, while the [MongoDB](MongoDB.md) provider doesn't support it. + +#### Repository Extension Methods for Change Tracking + +Change tracking is enabled unless you explicitly disable it. + +**Example: Using the `DisableTracking` extension method** + +````csharp +public class MyDemoService : ApplicationService +{ + private readonly IRepository _personRepository; + + public MyDemoService(IRepository personRepository) + { + _personRepository = personRepository; + } + + public async Task DoItAsync() + { + // Change tracking is enabled in that point (by default) + + using (_personRepository.DisableTracking()) + { + // Change tracking is disabled in that point + var list = await _personRepository.GetPagedListAsync(0, 100, "Name ASC"); + } + + // Change tracking is enabled in that point (by default) + } +} +```` + +> `DisableTracking` extension method returns a `IDisposable` object, so you can safely **restore** the change tracking behavior to the **previous state** one the `using` block ends. Basically, `DisableTracking` method ensures that the change tracking is disabled inside the `using` block, but doesn't affect outside of the `using` block. That means, if change tracking was already disabled, `DisableTracking` and the disposable return value do nothing. + +`EnableTracking()` method works exactly opposite to the `DisableTracking()` method. You typically won't use it (because the change tracking is already enabled by default), but it is there in case of you need that. + +#### Attributes for Change Tracking + +You typically use the `DisableTracking()` method for the application service methods those only returns data, but doesn't make any change on entities. For such cases, you can use the `DisableEntityChangeTracking` attribute on your method/class as a shortcut to disable the change tracking for whole method body. + +**Example: Using the `DisableEntityChangeTracking` attribute on a method** + +````csharp +[DisableEntityChangeTracking] +public virtual async Task> GetListAsync() +{ + /* We disabled the change tracking in this method + because we won't change the people objects */ + var people = await _personRepository.GetListAsync(); + return ObjectMapper.Map, List(people); +} +```` + +`EnableEntityChangeTracking` can be used for the opposite purpose, and it ensures that the change tracking is enabled for a given method. Since the change tracking is enabled by default, `EnableEntityChangeTracking` may be needed only if you know that your method is called from a context that disables the change tracking. + +`DisableEntityChangeTracking` and `EnableEntityChangeTracking` attributes can be used on a **method** or on a **class** (which affects all of the class methods). + +ABP uses dynamic proxying to make these attributes working. There are some rules here: + +* If you are **not injecting** the service over an interface (like `IPersonAppService`), then the methods of the service must be `virtual`. Otherwise, [dynamic proxy / interception](Dynamic-Proxying-Interceptors.md) system can not work. +* Only `async` methods (methods returning a `Task` or `Task`) are intercepted. + ## Other Generic Repository Types Standard `IRepository` interface exposes the standard `IQueryable` and you can freely query using the standard LINQ methods. This is fine for most of the applications. However, some ORM providers or database systems may not support standard `IQueryable` interface. If you want to use such providers, you can't rely on the `IQueryable`. @@ -217,7 +286,7 @@ They can all be seen as below: #### Read Only Repositories behavior in Entity Framework Core -Entity Framework Core read-only repository implementation uses [EF Core's No-Tracking feature](https://learn.microsoft.com/en-us/ef/core/querying/tracking#no-tracking-queries). That means the entities returned from the repository will not be tracked by the EF Core [change tracker](https://learn.microsoft.com/en-us/ef/core/change-tracking/), because it is expected that you won't update entities queried from a read-only repository. If you need to track the entities, you can still uses [AsTracking()](https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.entityframeworkqueryableextensions.astracking) extension method. +Entity Framework Core read-only repository implementation uses [EF Core's No-Tracking feature](https://learn.microsoft.com/en-us/ef/core/querying/tracking#no-tracking-queries). That means the entities returned from the repository will not be tracked by the EF Core [change tracker](https://learn.microsoft.com/en-us/ef/core/change-tracking/), because it is expected that you won't update entities queried from a read-only repository. If you need to track the entities, you can still use the [AsTracking()](https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.entityframeworkqueryableextensions.astracking) extension method on the LINQ expression, or `EnableTracking()` extension method on the repository object (See *Enabling / Disabling the Change Tracking* section in this document). > This behavior works only if the repository object is injected with one of the read-only repository interfaces (`IReadOnlyRepository<...>` or `IReadOnlyBasicRepository<...>`). It won't work if you have injected a standard repository (e.g. `IRepository<...>`) then casted it to a read-only repository interface. From d6256540f075147d7ddee5d6f63107d8ba372523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 3 Sep 2023 15:12:19 +0300 Subject: [PATCH 15/18] Added note for inserted/updated entities --- docs/en/Repositories.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/en/Repositories.md b/docs/en/Repositories.md index ec55db7f3d..03fb27ab50 100644 --- a/docs/en/Repositories.md +++ b/docs/en/Repositories.md @@ -245,6 +245,8 @@ ABP uses dynamic proxying to make these attributes working. There are some rules * If you are **not injecting** the service over an interface (like `IPersonAppService`), then the methods of the service must be `virtual`. Otherwise, [dynamic proxy / interception](Dynamic-Proxying-Interceptors.md) system can not work. * Only `async` methods (methods returning a `Task` or `Task`) are intercepted. +> Change tracking behavior doesn't affect tracking entity objects returned from `InsertAsync` and `UpdateAsync` methods. The objects returned from these methods are always tracked (if the underlying provider has the change tracking feature) and any change you made to these objects are saved into the database. + ## Other Generic Repository Types Standard `IRepository` interface exposes the standard `IQueryable` and you can freely query using the standard LINQ methods. This is fine for most of the applications. However, some ORM providers or database systems may not support standard `IQueryable` interface. If you want to use such providers, you can't rely on the `IQueryable`. From 9356884516bc86b75dd72139c3a98aafd25fb1f4 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 3 Sep 2023 20:14:28 +0800 Subject: [PATCH 16/18] Rename `RepositoryInterceptor` and so on to `ChangeTrackingInterceptor`. --- .../Volo/Abp/Domain/AbpDddDomainModule.cs | 3 +- .../ChangeTrackingHelper.cs} | 5 +- .../ChangeTrackingInterceptor.cs} | 9 ++-- .../ChangeTrackingInterceptorRegistrar.cs} | 8 +-- .../DisableEntityChangeTrackingAttribute.cs | 4 +- .../EnableEntityChangeTrackingAttribute.cs | 4 +- .../EntityChangeTrackingAttribute.cs | 4 +- .../ChangeTrackingInterceptor_Tests.cs} | 49 ++----------------- 8 files changed, 23 insertions(+), 63 deletions(-) rename framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/{Repositories/RepositoryHelper.cs => ChangeTracking/ChangeTrackingHelper.cs} (93%) rename framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/{Repositories/RepositoryInterceptor.cs => ChangeTracking/ChangeTrackingInterceptor.cs} (61%) rename framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/{Repositories/RepositoryInterceptorRegistrar.cs => ChangeTracking/ChangeTrackingInterceptorRegistrar.cs} (54%) rename framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/{Repositories => ChangeTracking}/DisableEntityChangeTrackingAttribute.cs (83%) rename framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/{Repositories => ChangeTracking}/EnableEntityChangeTrackingAttribute.cs (83%) rename framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/{Repositories => ChangeTracking}/EntityChangeTrackingAttribute.cs (80%) rename framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/{Repositories/RepositoryInterceptor_Tests.cs => ChangeTracking/ChangeTrackingInterceptor_Tests.cs} (77%) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs index 2e67c42820..de6632704d 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs @@ -2,6 +2,7 @@ using Volo.Abp.Auditing; using Volo.Abp.Caching; using Volo.Abp.Data; +using Volo.Abp.Domain.ChangeTracking; using Volo.Abp.Domain.Repositories; using Volo.Abp.EventBus; using Volo.Abp.ExceptionHandling; @@ -30,6 +31,6 @@ public class AbpDddDomainModule : AbpModule public override void PreConfigureServices(ServiceConfigurationContext context) { context.Services.AddConventionalRegistrar(new AbpRepositoryConventionalRegistrar()); - context.Services.OnRegistered(RepositoryInterceptorRegistrar.RegisterIfNeeded); + context.Services.OnRegistered(ChangeTrackingInterceptorRegistrar.RegisterIfNeeded); } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/ChangeTrackingHelper.cs similarity index 93% rename from framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryHelper.cs rename to framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/ChangeTrackingHelper.cs index 2c57dd9640..d2235afd22 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryHelper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/ChangeTrackingHelper.cs @@ -1,10 +1,11 @@ using System.Linq; using System.Reflection; using JetBrains.Annotations; +using Volo.Abp.Domain.Repositories; -namespace Volo.Abp.Domain.Repositories; +namespace Volo.Abp.Domain.ChangeTracking; -public static class RepositoryHelper +public static class ChangeTrackingHelper { public static bool IsEntityChangeTrackingType(TypeInfo implementationType) { diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptor.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/ChangeTrackingInterceptor.cs similarity index 61% rename from framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptor.cs rename to framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/ChangeTrackingInterceptor.cs index e9727b932f..307e2dee6a 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptor.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/ChangeTrackingInterceptor.cs @@ -1,21 +1,22 @@ using System.Threading.Tasks; using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; using Volo.Abp.DynamicProxy; -namespace Volo.Abp.Domain.Repositories; +namespace Volo.Abp.Domain.ChangeTracking; -public class RepositoryInterceptor : AbpInterceptor, ITransientDependency +public class ChangeTrackingInterceptor : AbpInterceptor, ITransientDependency { private readonly IEntityChangeTrackingProvider _entityChangeTrackingProvider; - public RepositoryInterceptor(IEntityChangeTrackingProvider entityChangeTrackingProvider) + public ChangeTrackingInterceptor(IEntityChangeTrackingProvider entityChangeTrackingProvider) { _entityChangeTrackingProvider = entityChangeTrackingProvider; } public async override Task InterceptAsync(IAbpMethodInvocation invocation) { - if (!RepositoryHelper.IsEntityChangeTrackingMethod(invocation.Method, out var changeTrackingAttribute)) + if (!ChangeTrackingHelper.IsEntityChangeTrackingMethod(invocation.Method, out var changeTrackingAttribute)) { await invocation.ProceedAsync(); return; diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptorRegistrar.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/ChangeTrackingInterceptorRegistrar.cs similarity index 54% rename from framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptorRegistrar.cs rename to framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/ChangeTrackingInterceptorRegistrar.cs index 538fa72774..0249c570dd 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryInterceptorRegistrar.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/ChangeTrackingInterceptorRegistrar.cs @@ -3,20 +3,20 @@ using System.Reflection; using Volo.Abp.DependencyInjection; using Volo.Abp.DynamicProxy; -namespace Volo.Abp.Domain.Repositories; +namespace Volo.Abp.Domain.ChangeTracking; -public class RepositoryInterceptorRegistrar +public class ChangeTrackingInterceptorRegistrar { public static void RegisterIfNeeded(IOnServiceRegistredContext context) { if (ShouldIntercept(context.ImplementationType)) { - context.Interceptors.TryAdd(); + context.Interceptors.TryAdd(); } } private static bool ShouldIntercept(Type type) { - return !DynamicProxyIgnoreTypes.Contains(type) && RepositoryHelper.IsEntityChangeTrackingType(type.GetTypeInfo()); + return !DynamicProxyIgnoreTypes.Contains(type) && ChangeTrackingHelper.IsEntityChangeTrackingType(type.GetTypeInfo()); } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/DisableEntityChangeTrackingAttribute.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/DisableEntityChangeTrackingAttribute.cs similarity index 83% rename from framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/DisableEntityChangeTrackingAttribute.cs rename to framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/DisableEntityChangeTrackingAttribute.cs index 76a2bdeaa1..98011bda5f 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/DisableEntityChangeTrackingAttribute.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/DisableEntityChangeTrackingAttribute.cs @@ -1,11 +1,11 @@ using System; -namespace Volo.Abp.Domain.Repositories; +namespace Volo.Abp.Domain.ChangeTracking; /// /// Ensures that the change tracking in enabled for the given method or class. /// -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface)] +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] public class DisableEntityChangeTrackingAttribute : EntityChangeTrackingAttribute { public DisableEntityChangeTrackingAttribute() diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EnableEntityChangeTrackingAttribute.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/EnableEntityChangeTrackingAttribute.cs similarity index 83% rename from framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EnableEntityChangeTrackingAttribute.cs rename to framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/EnableEntityChangeTrackingAttribute.cs index 9765d3253f..542b60cd74 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EnableEntityChangeTrackingAttribute.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/EnableEntityChangeTrackingAttribute.cs @@ -1,11 +1,11 @@ using System; -namespace Volo.Abp.Domain.Repositories; +namespace Volo.Abp.Domain.ChangeTracking; /// /// Ensures that the change tracking in enabled for the given method or class. /// -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface)] +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] public class EnableEntityChangeTrackingAttribute : EntityChangeTrackingAttribute { public EnableEntityChangeTrackingAttribute() diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/EntityChangeTrackingAttribute.cs similarity index 80% rename from framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs rename to framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/EntityChangeTrackingAttribute.cs index 3719cd3c0c..3446a49354 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/EntityChangeTrackingAttribute.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/ChangeTracking/EntityChangeTrackingAttribute.cs @@ -1,8 +1,8 @@ using System; -namespace Volo.Abp.Domain.Repositories; +namespace Volo.Abp.Domain.ChangeTracking; -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Interface)] +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)] public abstract class EntityChangeTrackingAttribute : Attribute { public virtual bool IsEnabled { get; set; } diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/RepositoryInterceptor_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/ChangeTracking/ChangeTrackingInterceptor_Tests.cs similarity index 77% rename from framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/RepositoryInterceptor_Tests.cs rename to framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/ChangeTracking/ChangeTrackingInterceptor_Tests.cs index 8230a6ec7e..87b487b68a 100644 --- a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/RepositoryInterceptor_Tests.cs +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/ChangeTracking/ChangeTrackingInterceptor_Tests.cs @@ -4,14 +4,15 @@ using System.Linq; using System.Threading.Tasks; using Shouldly; using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.ChangeTracking; using Volo.Abp.Domain.Repositories; using Volo.Abp.TestApp.Domain; using Volo.Abp.TestApp.Testing; using Xunit; -namespace Volo.Abp.EntityFrameworkCore.Repositories; +namespace Volo.Abp.EntityFrameworkCore.ChangeTracking; -public class RepositoryInterceptor_Tests : TestAppTestBase +public class ChangeTrackingInterceptor_Tests : TestAppTestBase { [Fact] public async Task ReadOnly_Repository_Should_Not_Track_Entities() @@ -65,18 +66,6 @@ public class RepositoryInterceptor_Tests : TestAppTestBase - { - var service = GetRequiredService(); - var db = await repository.GetDbContextAsync(); - db.ChangeTracker.Entries().Count().ShouldBe(0); - - var list = await service.GetPeoplesAsync(); - list.Count.ShouldBeGreaterThan(0); - - db.ChangeTracker.Entries().Count().ShouldBe(1); // Track one entity from GetPeoplesAsync - }); - await WithUnitOfWorkAsync(async () => { var service = GetRequiredService(); @@ -163,38 +152,6 @@ public class MyServiceEnableEntityChangeTracking : ITransientDependency } } - -[EnableEntityChangeTracking] -public interface IMyServiceEnableEntityChangeTrackingOnInterface -{ - -} - -public class MyServiceEnableEntityChangeTrackingOnInterface : IMyServiceEnableEntityChangeTrackingOnInterface, ITransientDependency -{ - private readonly IRepository _repository; - - public MyServiceEnableEntityChangeTrackingOnInterface(IRepository repository) - { - _repository = repository; - } - - public virtual async Task> GetPeoplesAsync() - { - var p1 = await GetPeopleAsync(); - var p2 = await _repository.FindAsync(x => x.Name == "people2"); - - return new List {p1, p2}; - } - - [DisableEntityChangeTracking] - public virtual async Task GetPeopleAsync() - { - var p1 = await _repository.FindAsync(x => x.Name == "people1"); - return p1; - } -} - public class MyServiceChangeTrackingByEntityChangeTrackingProvider : ITransientDependency { private readonly IRepository _repository; From 129a0bf17a7551b2dec7e0abe2ab5a8a89fae469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 3 Sep 2023 20:09:20 +0300 Subject: [PATCH 17/18] Rename AbpAutoMapperExtensibleDtoExtensions to AbpAutoMapperExtensibleObjectExtensions --- ...xtensions.cs => AbpAutoMapperExtensibleObjectExtensions.cs} | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename framework/src/Volo.Abp.AutoMapper/AutoMapper/{AbpAutoMapperExtensibleDtoExtensions.cs => AbpAutoMapperExtensibleObjectExtensions.cs} (96%) diff --git a/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs b/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleObjectExtensions.cs similarity index 96% rename from framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs rename to framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleObjectExtensions.cs index 58047f575d..ea746500d6 100644 --- a/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleDtoExtensions.cs +++ b/framework/src/Volo.Abp.AutoMapper/AutoMapper/AbpAutoMapperExtensibleObjectExtensions.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; -using Volo.Abp; using Volo.Abp.AutoMapper; using Volo.Abp.Data; using Volo.Abp.ObjectExtending; namespace AutoMapper; -public static class AbpAutoMapperExtensibleDtoExtensions +public static class AbpAutoMapperExtensibleObjectExtensions { public static IMappingExpression MapExtraProperties( this IMappingExpression mappingExpression, From 3634de2b88284c3d0e15c1e5a34104bd39933429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 3 Sep 2023 21:31:28 +0300 Subject: [PATCH 18/18] Remove unnecesarry NotNull attribute --- .../Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditLogScope.cs | 5 +---- .../Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingManager.cs | 4 +--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditLogScope.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditLogScope.cs index 8e9f14e976..e3e8f22d0a 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditLogScope.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditLogScope.cs @@ -1,9 +1,6 @@ -using JetBrains.Annotations; - -namespace Volo.Abp.Auditing; +namespace Volo.Abp.Auditing; public interface IAuditLogScope { - [NotNull] AuditLogInfo Log { get; } } diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingManager.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingManager.cs index a1091b8600..c496a22340 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingManager.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/IAuditingManager.cs @@ -1,6 +1,4 @@ -using JetBrains.Annotations; - -namespace Volo.Abp.Auditing; +namespace Volo.Abp.Auditing; public interface IAuditingManager {