From 69e6d1b752498fd6f6f6f47f9c8356bbe0ad4df8 Mon Sep 17 00:00:00 2001 From: Galip Tolga Erdem Date: Thu, 13 Feb 2020 16:52:32 +0300 Subject: [PATCH 1/5] added AlwaysLogOnException documentation --- docs/en/Audit-Logging.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/en/Audit-Logging.md b/docs/en/Audit-Logging.md index 610efc0451..ebd110a450 100644 --- a/docs/en/Audit-Logging.md +++ b/docs/en/Audit-Logging.md @@ -1,4 +1,5 @@ -# Audit Logging + +# Audit Logging [Wikipedia](https://en.wikipedia.org/wiki/Audit_trail): "*An audit trail (also called **audit log**) is a security-relevant chronological record, set of records, and/or destination and source of records that provide documentary evidence of the sequence of activities that have affected at any time a specific operation, procedure, or event*". @@ -39,6 +40,7 @@ Here, a list of the options you can configure: * `IsEnabled` (default: `true`): A root switch to enable or disable the auditing system. Other options is not used if this value is `false`. * `HideErrors` (default: `true`): Audit log system hides and write regular [logs](Logging.md) if any error occurs while saving the audit log objects. If saving the audit logs is critical for your system, set this to `false` to throw exception in case of hiding the errors. * `IsEnabledForAnonymousUsers` (default: `true`): If you want to write audit logs only for the authenticated users, set this to `false`. If you save audit logs for anonymous users, you will see `null` for `UserId` values for these users. +* `AlwaysLogOnException` (default: `true`): Audit log option to save all the exceptions occur in the application. * `IsEnabledForGetRequests` (default: `false`): HTTP GET requests should not make any change in the database normally and audit log system doesn't save audit log objects for GET request. Set this to `true` to enable it also for the GET requests. * `ApplicationName`: If multiple applications saving audit logs into a single database, set this property to your application name, so you can distinguish the logs of different applications. * `IgnoredTypes`: A list of `Type`s to be ignored for audit logging. If this is an entity type, changes for this type of entities will not be saved. This list is also used while serializing the action parameters. From 905826f825dfb8cfc1c3b42edea28c265dee721f Mon Sep 17 00:00:00 2001 From: Galip Tolga Erdem Date: Thu, 13 Feb 2020 17:07:05 +0300 Subject: [PATCH 2/5] removed empty space on top. --- docs/en/Audit-Logging.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/en/Audit-Logging.md b/docs/en/Audit-Logging.md index ebd110a450..8fdb8cff8b 100644 --- a/docs/en/Audit-Logging.md +++ b/docs/en/Audit-Logging.md @@ -1,5 +1,4 @@ - -# Audit Logging +# Audit Logging [Wikipedia](https://en.wikipedia.org/wiki/Audit_trail): "*An audit trail (also called **audit log**) is a security-relevant chronological record, set of records, and/or destination and source of records that provide documentary evidence of the sequence of activities that have affected at any time a specific operation, procedure, or event*". From fc8d34eae94bb628ea170321fe8236fa4eb73b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 14 Feb 2020 19:33:29 +0300 Subject: [PATCH 3/5] Refactor and improvements for #2807 --- .../Volo/Abp/Domain/Entities/EntityHelper.cs | 21 ------- .../Abp/Domain/Repositories/RepositoryBase.cs | 1 + .../Repositories/RepositoryExtensions.cs | 61 +++++++++++++------ .../UnitOfWorkExtensionDataTypes.cs | 11 ---- .../Repositories/UnitOfWorkItemNames.cs | 7 +++ .../EntityFrameworkCore/EfCoreRepository.cs | 4 +- .../Abp/EntityFrameworkCore/AbpDbContext.cs | 40 ++++++------ .../Repositories/MongoDB/MongoDbRepository.cs | 23 ++----- .../Volo.Abp.Uow/Volo/Abp/Uow/IUnitOfWork.cs | 1 + .../Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs | 8 +-- .../Abp/TestApp/Testing/HardDelete_Tests.cs | 55 ++++++++--------- 11 files changed, 105 insertions(+), 127 deletions(-) delete mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkExtensionDataTypes.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkItemNames.cs diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs index cce1e9c18f..a33c5f56ec 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs @@ -112,26 +112,5 @@ namespace Volo.Abp.Domain.Entities idProperty.SetValue(entity, idFactory()); } - - public static object GetEntityId(object entity) - { - if (!IsEntity(entity.GetType())) - { - throw new AbpException(entity.GetType() + " is not an Entity !"); - } - - return ReflectionHelper.GetValueByPath(entity, entity.GetType(), "Id"); - } - public static string GetHardDeleteKey(object entity, string tenantId) - { - //if (entity is IMultiTenant) // IsMultiTenantEntity - if (typeof(IMultiTenant).IsAssignableFrom(entity.GetType())) - { - var tenantIdString = !string.IsNullOrEmpty(tenantId) ? tenantId : "null"; - return entity.GetType().FullName + ";TenantId=" + tenantIdString + ";Id=" + GetEntityId(entity); - } - - return entity.GetType().FullName + ";Id=" + GetEntityId(entity); - } } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs index b908827a98..3ade403017 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs @@ -18,6 +18,7 @@ namespace Volo.Abp.Domain.Repositories public IDataFilter DataFilter { get; set; } public ICurrentTenant CurrentTenant { get; set; } + public IUnitOfWorkManager UnitOfWorkManager { get; set; } public virtual Type ElementType => GetQueryable().ElementType; 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 9a2bc147af..ef44d6830d 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 @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; @@ -44,31 +43,55 @@ namespace Volo.Abp.Domain.Repositories } } - public static async Task HardDeleteAsync(this IRepository repository, TEntity entity) - where TEntity : class, IEntity, ISoftDelete + public static async Task HardDeleteAsync( + this IBasicRepository repository, + TEntity entity, + bool autoSave = false, + CancellationToken cancellationToken = default + ) + where TEntity : class, IEntity, ISoftDelete { - var repo = ProxyHelper.UnProxy(repository) as IRepository; - if (repo != null) + if (!(ProxyHelper.UnProxy(repository) is IUnitOfWorkManagerAccessor unitOfWorkManagerAccessor)) { - var uow = ((IUnitOfWorkManagerAccessor)repo).UnitOfWorkManager; - var baseRepository = ((RepositoryBase)repo); - - var items = ((IUnitOfWorkManagerAccessor)repo).UnitOfWorkManager.Current.Items; - var hardDeleteEntities = items.GetOrAdd(UnitOfWorkExtensionDataTypes.HardDelete, () => new HashSet()) as HashSet; + throw new AbpException($"The given repository (of type {repository.GetType().AssemblyQualifiedName}) should implement the {typeof(IUnitOfWorkManagerAccessor).AssemblyQualifiedName} interface in order to invoke the {nameof(HardDeleteAsync)} method!"); + } - var hardDeleteKey = EntityHelper.GetHardDeleteKey(entity, baseRepository.CurrentTenant?.Id?.ToString()); - hardDeleteEntities.Add(hardDeleteKey); + var uowManager = unitOfWorkManagerAccessor.UnitOfWorkManager; + if (uowManager == null) + { + throw new AbpException($"{nameof(unitOfWorkManagerAccessor.UnitOfWorkManager)} property of the given {nameof(repository)} object is null!"); + } - await repo.DeleteAsync(entity); + if (uowManager.Current == null) + { + using (var uow = uowManager.Begin()) + { + await HardDeleteWithUnitOfWorkAsync(repository, entity, autoSave, cancellationToken, uowManager.Current); + await uow.CompleteAsync(cancellationToken); + } } - } - public static async Task HardDeleteAsync(this IRepository repository, Expression> predicate) - where TEntity : class, IEntity, ISoftDelete - { - foreach (var entity in repository.Where(predicate).ToList()) + else { - await repository.HardDeleteAsync(entity); + await HardDeleteWithUnitOfWorkAsync(repository, entity, autoSave, cancellationToken, uowManager.Current); } } + + private static async Task HardDeleteWithUnitOfWorkAsync( + IBasicRepository repository, + TEntity entity, + bool autoSave, + CancellationToken cancellationToken, IUnitOfWork currentUow + ) + where TEntity : class, IEntity, ISoftDelete + { + var hardDeleteEntities = (HashSet) currentUow.Items.GetOrAdd( + UnitOfWorkItemNames.HardDeletedEntities, + () => new HashSet() + ); + + hardDeleteEntities.Add(entity); + + await repository.DeleteAsync(entity, autoSave, cancellationToken); + } } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkExtensionDataTypes.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkExtensionDataTypes.cs deleted file mode 100644 index a95aca42c0..0000000000 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkExtensionDataTypes.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Volo.Abp.Domain.Repositories -{ - public class UnitOfWorkExtensionDataTypes - { - public static string HardDelete { get; } = "HardDelete"; - } -} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkItemNames.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkItemNames.cs new file mode 100644 index 0000000000..4ec067ceec --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkItemNames.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Domain.Repositories +{ + public static class UnitOfWorkItemNames + { + public const string HardDeletedEntities = "AbpHardDeletedEntities"; + } +} 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 6c3eb689d8..106e00f77a 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 @@ -80,7 +80,7 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore { return includeDetails ? await WithDetails().ToListAsync(GetCancellationToken(cancellationToken)) - : await DbSet.ToListAsync(GetCancellationToken(cancellationToken)); + : await DbSet.ToListAsync(GetCancellationToken(cancellationToken)); } public override async Task GetCountAsync(CancellationToken cancellationToken = default) @@ -208,7 +208,7 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore { return includeDetails ? await WithDetails().FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken)) - : await DbSet.FindAsync(new object[] { id }, GetCancellationToken(cancellationToken)); + : await DbSet.FindAsync(new object[] {id}, GetCancellationToken(cancellationToken)); } public virtual async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index 4e2f4c0d51..ba9cee70ee 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -50,6 +50,7 @@ namespace Volo.Abp.EntityFrameworkCore public IEntityHistoryHelper EntityHistoryHelper { get; set; } public IAuditingManager AuditingManager { get; set; } + public IUnitOfWorkManager UnitOfWorkManager { get; set; } public IClock Clock { get; set; } @@ -199,37 +200,24 @@ namespace Volo.Abp.EntityFrameworkCore protected virtual void ApplyAbpConceptsForDeletedEntity(EntityEntry entry, EntityChangeReport changeReport) { - if (IsHardDeleteEntity(entry)) + if (TryCancelDeletionForSoftDelete(entry)) { - changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted)); - return; + UpdateConcurrencyStamp(entry); + SetDeletionAuditProperties(entry); } - CancelDeletionForSoftDelete(entry); - UpdateConcurrencyStamp(entry); - SetDeletionAuditProperties(entry); + changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted)); } - protected virtual bool IsHardDeleteEntity(EntityEntry entry) + protected virtual bool IsHardDeleted(EntityEntry entry) { - if (UnitOfWorkManager?.Current?.Items == null) - { - return false; - } - - if (!UnitOfWorkManager.Current.Items.ContainsKey(UnitOfWorkExtensionDataTypes.HardDelete)) + var hardDeletedEntities = UnitOfWorkManager?.Current?.Items.GetOrDefault(UnitOfWorkItemNames.HardDeletedEntities) as HashSet; + if (hardDeletedEntities == null) { return false; } - var hardDeleteItems = UnitOfWorkManager.Current.Items[UnitOfWorkExtensionDataTypes.HardDelete]; - if (!(hardDeleteItems is HashSet objects)) - { - return false; - } - string hardDeleteKey = EntityHelper.GetHardDeleteKey(entry.Entity, CurrentTenantId?.ToString()); - - return objects.Contains(hardDeleteKey); + return hardDeletedEntities.Contains(entry.Entity); } protected virtual void AddDomainEvents(EntityChangeReport changeReport, object entityAsObj) @@ -283,16 +271,22 @@ namespace Volo.Abp.EntityFrameworkCore entity.ConcurrencyStamp = Guid.NewGuid().ToString("N"); } - protected virtual void CancelDeletionForSoftDelete(EntityEntry entry) + protected virtual bool TryCancelDeletionForSoftDelete(EntityEntry entry) { if (!(entry.Entity is ISoftDelete)) { - return; + return false; + } + + if (IsHardDeleted(entry)) + { + return false; } entry.Reload(); entry.State = EntityState.Modified; entry.Entity.As().IsDeleted = true; + return true; } protected virtual void CheckAndSetId(EntityEntry entry) diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs index b827292702..3333f1d505 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs @@ -111,7 +111,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB await ApplyAbpConceptsForDeletedEntityAsync(entity); var oldConcurrencyStamp = SetNewConcurrencyStamp(entity); - if (entity is ISoftDelete softDeleteEntity && !IsHardDeleteEntity(entity)) + if (entity is ISoftDelete softDeleteEntity && !IsHardDeleted(entity)) { softDeleteEntity.IsDeleted = true; var result = await Collection.ReplaceOneAsync( @@ -175,32 +175,21 @@ namespace Volo.Abp.Domain.Repositories.MongoDB Collection.AsQueryable() ); } - protected virtual bool IsHardDeleteEntity(TEntity entry) + protected virtual bool IsHardDeleted(TEntity entity) { - if (UnitOfWorkManager?.Current?.Items == null) + var hardDeletedEntities = UnitOfWorkManager?.Current?.Items.GetOrDefault(UnitOfWorkItemNames.HardDeletedEntities) as HashSet; + if (hardDeletedEntities == null) { return false; } - if (!UnitOfWorkManager.Current.Items.ContainsKey(UnitOfWorkExtensionDataTypes.HardDelete)) - { - return false; - } - - var hardDeleteItems = UnitOfWorkManager.Current.Items[UnitOfWorkExtensionDataTypes.HardDelete]; - if (!(hardDeleteItems is HashSet objects)) - { - return false; - } - string hardDeleteKey = EntityHelper.GetHardDeleteKey(entry, CurrentTenant?.Id.ToString()); - - return objects.Contains(hardDeleteKey); + return hardDeletedEntities.Contains(entity); } protected virtual FilterDefinition CreateEntityFilter(TEntity entity, bool withConcurrencyStamp = false, string concurrencyStamp = null) { throw new NotImplementedException( - $"{nameof(CreateEntityFilter)} is not implemented for MongoDB by default. It should be overrided and implemented by the deriving class!" + $"{nameof(CreateEntityFilter)} is not implemented for MongoDB by default. It should be overriden and implemented by the deriving class!" ); } diff --git a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/IUnitOfWork.cs b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/IUnitOfWork.cs index 97fd81372b..ee5baa58bc 100644 --- a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/IUnitOfWork.cs +++ b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/IUnitOfWork.cs @@ -9,6 +9,7 @@ namespace Volo.Abp.Uow public interface IUnitOfWork : IDatabaseApiContainer, ITransactionApiContainer, IDisposable { Guid Id { get; } + Dictionary Items { get; } //TODO: Switch to OnFailed (sync) and OnDisposed (sync) methods to be compatible with OnCompleted diff --git a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs index c494bfdfe6..05f49df180 100644 --- a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs +++ b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using JetBrains.Annotations; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; @@ -31,6 +32,7 @@ namespace Volo.Abp.Uow public IServiceProvider ServiceProvider { get; } + [NotNull] public Dictionary Items { get; } private readonly Dictionary _databaseApis; @@ -48,6 +50,7 @@ namespace Volo.Abp.Uow _databaseApis = new Dictionary(); _transactionApis = new Dictionary(); + Items = new Dictionary(); } @@ -320,10 +323,5 @@ namespace Volo.Abp.Uow { return $"[UnitOfWork {Id}]"; } - - public Dictionary GetHardDeleteItems() - { - return Items; - } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HardDelete_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HardDelete_Tests.cs index 9d0430e968..1eac79fd7c 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HardDelete_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HardDelete_Tests.cs @@ -1,7 +1,5 @@ using Shouldly; using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using Volo.Abp.Data; using Volo.Abp.Domain.Repositories; @@ -15,58 +13,57 @@ namespace Volo.Abp.TestApp.Testing public abstract class HardDelete_Tests : TestAppTestBase where TStartupModule : IAbpModule { - protected readonly IRepository _personRepository; + protected readonly IRepository PersonRepository; protected readonly IDataFilter DataFilter; - protected readonly IUnitOfWorkManager _unitOfWorkManager; - public HardDelete_Tests() + protected readonly IUnitOfWorkManager UnitOfWorkManager; + + protected HardDelete_Tests() { - _personRepository = GetRequiredService>(); + PersonRepository = GetRequiredService>(); DataFilter = GetRequiredService(); - _unitOfWorkManager = GetRequiredService(); + UnitOfWorkManager = GetRequiredService(); } + [Fact] - public async Task Should_HardDelete_Entity_With_Collection() + public async Task Should_HardDelete_Entities() { - using (var uow = _unitOfWorkManager.Begin()) - { - using (DataFilter.Disable()) - { - var douglas = await _personRepository.FindAsync(TestDataBuilder.UserDouglasId); - await _personRepository.HardDeleteAsync(x => x.Id == TestDataBuilder.UserDouglasId); - await uow.CompleteAsync(); - } + var douglas = await PersonRepository.GetAsync(TestDataBuilder.UserDouglasId); + await PersonRepository.HardDeleteAsync(douglas); - var deletedDougles = await _personRepository.FindAsync(TestDataBuilder.UserDouglasId); - deletedDougles.ShouldBeNull(); - } + douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId); + douglas.ShouldBeNull(); } + [Fact] public async Task Should_HardDelete_Soft_Deleted_Entities() { - var douglas = await _personRepository.GetAsync(TestDataBuilder.UserDouglasId); - await _personRepository.DeleteAsync(douglas); + var douglas = await PersonRepository.GetAsync(TestDataBuilder.UserDouglasId); + await PersonRepository.DeleteAsync(douglas); - douglas = await _personRepository.FindAsync(TestDataBuilder.UserDouglasId); + douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId); douglas.ShouldBeNull(); using (DataFilter.Disable()) { - douglas = await _personRepository.FindAsync(TestDataBuilder.UserDouglasId); + douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId); douglas.ShouldNotBeNull(); douglas.IsDeleted.ShouldBeTrue(); douglas.DeletionTime.ShouldNotBeNull(); } - using (var uow = _unitOfWorkManager.Begin()) + + using (var uow = UnitOfWorkManager.Begin()) { using (DataFilter.Disable()) { - douglas = await _personRepository.GetAsync(TestDataBuilder.UserDouglasId); - await _personRepository.HardDeleteAsync(douglas); - await uow.CompleteAsync(); - var deletedDougles = await _personRepository.FindAsync(TestDataBuilder.UserDouglasId); - deletedDougles.ShouldBeNull(); + douglas = await PersonRepository.GetAsync(TestDataBuilder.UserDouglasId); } + + await PersonRepository.HardDeleteAsync(douglas); + await uow.CompleteAsync(); } + + douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId); + douglas.ShouldBeNull(); } } } From 1033a3db55a6b479fde805624fbeb9adaf6afb4e Mon Sep 17 00:00:00 2001 From: Galip Tolga Erdem <25867+gterdem@users.noreply.github.com> Date: Mon, 17 Feb 2020 08:36:45 +0300 Subject: [PATCH 4/5] Update docs/en/Audit-Logging.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated with reviewer's suggestion Co-Authored-By: Halil İbrahim Kalkan --- docs/en/Audit-Logging.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/Audit-Logging.md b/docs/en/Audit-Logging.md index 8fdb8cff8b..799b3b76c3 100644 --- a/docs/en/Audit-Logging.md +++ b/docs/en/Audit-Logging.md @@ -39,7 +39,7 @@ Here, a list of the options you can configure: * `IsEnabled` (default: `true`): A root switch to enable or disable the auditing system. Other options is not used if this value is `false`. * `HideErrors` (default: `true`): Audit log system hides and write regular [logs](Logging.md) if any error occurs while saving the audit log objects. If saving the audit logs is critical for your system, set this to `false` to throw exception in case of hiding the errors. * `IsEnabledForAnonymousUsers` (default: `true`): If you want to write audit logs only for the authenticated users, set this to `false`. If you save audit logs for anonymous users, you will see `null` for `UserId` values for these users. -* `AlwaysLogOnException` (default: `true`): Audit log option to save all the exceptions occur in the application. +* `AlwaysLogOnException` (default: `true`): If you set to true, it always saves the audit log on an exception/error case without checking other options (except `IsEnabled`, which completely disables the audit logging). * `IsEnabledForGetRequests` (default: `false`): HTTP GET requests should not make any change in the database normally and audit log system doesn't save audit log objects for GET request. Set this to `true` to enable it also for the GET requests. * `ApplicationName`: If multiple applications saving audit logs into a single database, set this property to your application name, so you can distinguish the logs of different applications. * `IgnoredTypes`: A list of `Type`s to be ignored for audit logging. If this is an entity type, changes for this type of entities will not be saved. This list is also used while serializing the action parameters. @@ -370,4 +370,4 @@ You can call other services, they may call others, they may change entities and The Audit Logging Module basically implements the `IAuditingStore` to save the audit log objects to a database. It supports multiple database providers. This module is added to the startup templates by default. -See [the Audit Logging Module document](Modules/Audit-Logging.md) for more about it. \ No newline at end of file +See [the Audit Logging Module document](Modules/Audit-Logging.md) for more about it. From 398146bb47535786d23f20040738b82f9edcec87 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Mon, 17 Feb 2020 11:40:35 +0300 Subject: [PATCH 5/5] Update Caching.md resolves https://github.com/abpframework/abp/issues/2842 --- docs/en/Caching.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/en/Caching.md b/docs/en/Caching.md index 8aa9f00657..bb11b2b45a 100644 --- a/docs/en/Caching.md +++ b/docs/en/Caching.md @@ -2,6 +2,32 @@ ABP framework extends ASP.NET Core's distributed caching system. +## Volo.Abp.Caching Package + +> This package is already installed by default with the startup template. So, most of the time, you don't need to install it manually. + +Volo.Abp.Caching is the core package of the caching system. Install it to your project using the package manager console (PMC): + +``` +Install-Package Volo.Abp.Caching +``` + +Then you can add **AbpCachingModule** dependency to your module: + +```c# +using Volo.Abp.Modularity; +using Volo.Abp.Caching; + +namespace MyCompany.MyProject +{ + [DependsOn(typeof(AbpCachingModule))] + public class MyModule : AbpModule + { + //... + } +} +``` + ## `IDistributedCache` Interface ASP.NET Core defines the `IDistributedCache` interface to get/set cache values. But it has some difficulties: