From 01d2cf3a8bee56ecd3b4846a4cbae7ed18f7bc53 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 27 Aug 2023 14:02:24 +0800 Subject: [PATCH] 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); }); }