Skip to content

Commit e558b85

Browse files
Merge remote-tracking branch 'abpframework/dev' into Translate
2 parents 2f6a29c + 398146b commit e558b85

File tree

13 files changed

+133
-128
lines changed

13 files changed

+133
-128
lines changed

docs/en/Audit-Logging.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Here, a list of the options you can configure:
3939
* `IsEnabled` (default: `true`): A root switch to enable or disable the auditing system. Other options is not used if this value is `false`.
4040
* `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.
4141
* `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.
42+
* `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).
4243
* `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.
4344
* `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.
4445
* `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.
@@ -369,4 +370,4 @@ You can call other services, they may call others, they may change entities and
369370

370371
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.
371372

372-
See [the Audit Logging Module document](Modules/Audit-Logging.md) for more about it.
373+
See [the Audit Logging Module document](Modules/Audit-Logging.md) for more about it.

docs/en/Caching.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,32 @@
22

33
ABP framework extends ASP.NET Core's distributed caching system.
44

5+
## Volo.Abp.Caching Package
6+
7+
> This package is already installed by default with the startup template. So, most of the time, you don't need to install it manually.
8+
9+
Volo.Abp.Caching is the core package of the caching system. Install it to your project using the package manager console (PMC):
10+
11+
```
12+
Install-Package Volo.Abp.Caching
13+
```
14+
15+
Then you can add **AbpCachingModule** dependency to your module:
16+
17+
```c#
18+
using Volo.Abp.Modularity;
19+
using Volo.Abp.Caching;
20+
21+
namespace MyCompany.MyProject
22+
{
23+
[DependsOn(typeof(AbpCachingModule))]
24+
public class MyModule : AbpModule
25+
{
26+
//...
27+
}
28+
}
29+
```
30+
531
## `IDistributedCache` Interface
632

733
ASP.NET Core defines the `IDistributedCache` interface to get/set cache values. But it has some difficulties:

framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -112,26 +112,5 @@ public static void TrySetId<TKey>(
112112

113113
idProperty.SetValue(entity, idFactory());
114114
}
115-
116-
public static object GetEntityId(object entity)
117-
{
118-
if (!IsEntity(entity.GetType()))
119-
{
120-
throw new AbpException(entity.GetType() + " is not an Entity !");
121-
}
122-
123-
return ReflectionHelper.GetValueByPath(entity, entity.GetType(), "Id");
124-
}
125-
public static string GetHardDeleteKey(object entity, string tenantId)
126-
{
127-
//if (entity is IMultiTenant) // IsMultiTenantEntity
128-
if (typeof(IMultiTenant).IsAssignableFrom(entity.GetType()))
129-
{
130-
var tenantIdString = !string.IsNullOrEmpty(tenantId) ? tenantId : "null";
131-
return entity.GetType().FullName + ";TenantId=" + tenantIdString + ";Id=" + GetEntityId(entity);
132-
}
133-
134-
return entity.GetType().FullName + ";Id=" + GetEntityId(entity);
135-
}
136115
}
137116
}

framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public abstract class RepositoryBase<TEntity> : BasicRepositoryBase<TEntity>, IR
1818
public IDataFilter DataFilter { get; set; }
1919

2020
public ICurrentTenant CurrentTenant { get; set; }
21+
2122
public IUnitOfWorkManager UnitOfWorkManager { get; set; }
2223

2324
public virtual Type ElementType => GetQueryable().ElementType;

framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Linq;
43
using System.Linq.Expressions;
54
using System.Threading;
65
using System.Threading.Tasks;
@@ -44,31 +43,55 @@ public static async Task EnsurePropertyLoadedAsync<TEntity, TKey, TProperty>(
4443
}
4544
}
4645

47-
public static async Task HardDeleteAsync<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository, TEntity entity)
48-
where TEntity : class, IEntity<TPrimaryKey>, ISoftDelete
46+
public static async Task HardDeleteAsync<TEntity>(
47+
this IBasicRepository<TEntity> repository,
48+
TEntity entity,
49+
bool autoSave = false,
50+
CancellationToken cancellationToken = default
51+
)
52+
where TEntity : class, IEntity, ISoftDelete
4953
{
50-
var repo = ProxyHelper.UnProxy(repository) as IRepository<TEntity, TPrimaryKey>;
51-
if (repo != null)
54+
if (!(ProxyHelper.UnProxy(repository) is IUnitOfWorkManagerAccessor unitOfWorkManagerAccessor))
5255
{
53-
var uow = ((IUnitOfWorkManagerAccessor)repo).UnitOfWorkManager;
54-
var baseRepository = ((RepositoryBase<TEntity>)repo);
55-
56-
var items = ((IUnitOfWorkManagerAccessor)repo).UnitOfWorkManager.Current.Items;
57-
var hardDeleteEntities = items.GetOrAdd(UnitOfWorkExtensionDataTypes.HardDelete, () => new HashSet<string>()) as HashSet<string>;
56+
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!");
57+
}
5858

59-
var hardDeleteKey = EntityHelper.GetHardDeleteKey(entity, baseRepository.CurrentTenant?.Id?.ToString());
60-
hardDeleteEntities.Add(hardDeleteKey);
59+
var uowManager = unitOfWorkManagerAccessor.UnitOfWorkManager;
60+
if (uowManager == null)
61+
{
62+
throw new AbpException($"{nameof(unitOfWorkManagerAccessor.UnitOfWorkManager)} property of the given {nameof(repository)} object is null!");
63+
}
6164

62-
await repo.DeleteAsync(entity);
65+
if (uowManager.Current == null)
66+
{
67+
using (var uow = uowManager.Begin())
68+
{
69+
await HardDeleteWithUnitOfWorkAsync(repository, entity, autoSave, cancellationToken, uowManager.Current);
70+
await uow.CompleteAsync(cancellationToken);
71+
}
6372
}
64-
}
65-
public static async Task HardDeleteAsync<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository, Expression<Func<TEntity, bool>> predicate)
66-
where TEntity : class, IEntity<TPrimaryKey>, ISoftDelete
67-
{
68-
foreach (var entity in repository.Where(predicate).ToList())
73+
else
6974
{
70-
await repository.HardDeleteAsync(entity);
75+
await HardDeleteWithUnitOfWorkAsync(repository, entity, autoSave, cancellationToken, uowManager.Current);
7176
}
7277
}
78+
79+
private static async Task HardDeleteWithUnitOfWorkAsync<TEntity>(
80+
IBasicRepository<TEntity> repository,
81+
TEntity entity,
82+
bool autoSave,
83+
CancellationToken cancellationToken, IUnitOfWork currentUow
84+
)
85+
where TEntity : class, IEntity, ISoftDelete
86+
{
87+
var hardDeleteEntities = (HashSet<IEntity>) currentUow.Items.GetOrAdd(
88+
UnitOfWorkItemNames.HardDeletedEntities,
89+
() => new HashSet<IEntity>()
90+
);
91+
92+
hardDeleteEntities.Add(entity);
93+
94+
await repository.DeleteAsync(entity, autoSave, cancellationToken);
95+
}
7396
}
7497
}

framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkExtensionDataTypes.cs

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace Volo.Abp.Domain.Repositories
2+
{
3+
public static class UnitOfWorkItemNames
4+
{
5+
public const string HardDeletedEntities = "AbpHardDeletedEntities";
6+
}
7+
}

framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public override async Task<List<TEntity>> GetListAsync(bool includeDetails = fal
8080
{
8181
return includeDetails
8282
? await WithDetails().ToListAsync(GetCancellationToken(cancellationToken))
83-
: await DbSet.ToListAsync(GetCancellationToken(cancellationToken));
83+
: await DbSet.ToListAsync(GetCancellationToken(cancellationToken));
8484
}
8585

8686
public override async Task<long> GetCountAsync(CancellationToken cancellationToken = default)
@@ -208,7 +208,7 @@ public virtual async Task<TEntity> FindAsync(TKey id, bool includeDetails = true
208208
{
209209
return includeDetails
210210
? await WithDetails().FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken))
211-
: await DbSet.FindAsync(new object[] { id }, GetCancellationToken(cancellationToken));
211+
: await DbSet.FindAsync(new object[] {id}, GetCancellationToken(cancellationToken));
212212
}
213213

214214
public virtual async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default)

framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IEfCoreDbContext, IT
5050
public IEntityHistoryHelper EntityHistoryHelper { get; set; }
5151

5252
public IAuditingManager AuditingManager { get; set; }
53+
5354
public IUnitOfWorkManager UnitOfWorkManager { get; set; }
5455

5556
public IClock Clock { get; set; }
@@ -199,37 +200,24 @@ protected virtual void ApplyAbpConceptsForModifiedEntity(EntityEntry entry, Enti
199200

200201
protected virtual void ApplyAbpConceptsForDeletedEntity(EntityEntry entry, EntityChangeReport changeReport)
201202
{
202-
if (IsHardDeleteEntity(entry))
203+
if (TryCancelDeletionForSoftDelete(entry))
203204
{
204-
changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted));
205-
return;
205+
UpdateConcurrencyStamp(entry);
206+
SetDeletionAuditProperties(entry);
206207
}
207-
CancelDeletionForSoftDelete(entry);
208-
UpdateConcurrencyStamp(entry);
209-
SetDeletionAuditProperties(entry);
208+
210209
changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted));
211210
}
212211

213-
protected virtual bool IsHardDeleteEntity(EntityEntry entry)
212+
protected virtual bool IsHardDeleted(EntityEntry entry)
214213
{
215-
if (UnitOfWorkManager?.Current?.Items == null)
216-
{
217-
return false;
218-
}
219-
220-
if (!UnitOfWorkManager.Current.Items.ContainsKey(UnitOfWorkExtensionDataTypes.HardDelete))
214+
var hardDeletedEntities = UnitOfWorkManager?.Current?.Items.GetOrDefault(UnitOfWorkItemNames.HardDeletedEntities) as HashSet<IEntity>;
215+
if (hardDeletedEntities == null)
221216
{
222217
return false;
223218
}
224219

225-
var hardDeleteItems = UnitOfWorkManager.Current.Items[UnitOfWorkExtensionDataTypes.HardDelete];
226-
if (!(hardDeleteItems is HashSet<string> objects))
227-
{
228-
return false;
229-
}
230-
string hardDeleteKey = EntityHelper.GetHardDeleteKey(entry.Entity, CurrentTenantId?.ToString());
231-
232-
return objects.Contains(hardDeleteKey);
220+
return hardDeletedEntities.Contains(entry.Entity);
233221
}
234222

235223
protected virtual void AddDomainEvents(EntityChangeReport changeReport, object entityAsObj)
@@ -283,16 +271,22 @@ protected virtual void SetConcurrencyStampIfNull(EntityEntry entry)
283271
entity.ConcurrencyStamp = Guid.NewGuid().ToString("N");
284272
}
285273

286-
protected virtual void CancelDeletionForSoftDelete(EntityEntry entry)
274+
protected virtual bool TryCancelDeletionForSoftDelete(EntityEntry entry)
287275
{
288276
if (!(entry.Entity is ISoftDelete))
289277
{
290-
return;
278+
return false;
279+
}
280+
281+
if (IsHardDeleted(entry))
282+
{
283+
return false;
291284
}
292285

293286
entry.Reload();
294287
entry.State = EntityState.Modified;
295288
entry.Entity.As<ISoftDelete>().IsDeleted = true;
289+
return true;
296290
}
297291

298292
protected virtual void CheckAndSetId(EntityEntry entry)

framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public override async Task DeleteAsync(
111111
await ApplyAbpConceptsForDeletedEntityAsync(entity);
112112
var oldConcurrencyStamp = SetNewConcurrencyStamp(entity);
113113

114-
if (entity is ISoftDelete softDeleteEntity && !IsHardDeleteEntity(entity))
114+
if (entity is ISoftDelete softDeleteEntity && !IsHardDeleted(entity))
115115
{
116116
softDeleteEntity.IsDeleted = true;
117117
var result = await Collection.ReplaceOneAsync(
@@ -175,32 +175,21 @@ public virtual IMongoQueryable<TEntity> GetMongoQueryable()
175175
Collection.AsQueryable()
176176
);
177177
}
178-
protected virtual bool IsHardDeleteEntity(TEntity entry)
178+
protected virtual bool IsHardDeleted(TEntity entity)
179179
{
180-
if (UnitOfWorkManager?.Current?.Items == null)
180+
var hardDeletedEntities = UnitOfWorkManager?.Current?.Items.GetOrDefault(UnitOfWorkItemNames.HardDeletedEntities) as HashSet<IEntity>;
181+
if (hardDeletedEntities == null)
181182
{
182183
return false;
183184
}
184185

185-
if (!UnitOfWorkManager.Current.Items.ContainsKey(UnitOfWorkExtensionDataTypes.HardDelete))
186-
{
187-
return false;
188-
}
189-
190-
var hardDeleteItems = UnitOfWorkManager.Current.Items[UnitOfWorkExtensionDataTypes.HardDelete];
191-
if (!(hardDeleteItems is HashSet<string> objects))
192-
{
193-
return false;
194-
}
195-
string hardDeleteKey = EntityHelper.GetHardDeleteKey(entry, CurrentTenant?.Id.ToString());
196-
197-
return objects.Contains(hardDeleteKey);
186+
return hardDeletedEntities.Contains(entity);
198187
}
199188

200189
protected virtual FilterDefinition<TEntity> CreateEntityFilter(TEntity entity, bool withConcurrencyStamp = false, string concurrencyStamp = null)
201190
{
202191
throw new NotImplementedException(
203-
$"{nameof(CreateEntityFilter)} is not implemented for MongoDB by default. It should be overrided and implemented by the deriving class!"
192+
$"{nameof(CreateEntityFilter)} is not implemented for MongoDB by default. It should be overriden and implemented by the deriving class!"
204193
);
205194
}
206195

0 commit comments

Comments
 (0)