Browse Source

Merge pull request #19394 from abpframework/EntityCacheBase

`EnableTracking` if the `entity` has `ObjectExtensionInfo`.
pull/19454/head
maliming 3 years ago
committed by GitHub
parent
commit
7e715f8e10
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      docs/en/Entity-Framework-Core.md
  2. 42
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheBase.cs
  3. 24
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheServiceCollectionExtensions.cs
  4. 2
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithoutCacheItem.cs

2
docs/en/Entity-Framework-Core.md

@ -753,6 +753,8 @@ public static class QADbContextModelCreatingExtensions
> If you call `ConfigureByConvention()` extension method (like `b.ConfigureByConvention()` for this example), ABP Framework internally calls the `ConfigureObjectExtensions` and `ConfigureEfCoreEntity` methods. It is a **best practice** to use the `ConfigureByConvention()` method since it also configures database mapping for base properties by convention. > If you call `ConfigureByConvention()` extension method (like `b.ConfigureByConvention()` for this example), ABP Framework internally calls the `ConfigureObjectExtensions` and `ConfigureEfCoreEntity` methods. It is a **best practice** to use the `ConfigureByConvention()` method since it also configures database mapping for base properties by convention.
> The `Object Extension` feature need the `Change Tracking`, which means you can't use the read-only repositories for the entities that have `extension properties(MapEfCoreProperty)`, Please see the [Repositories documentation](Repositories.md) to learn the change tracking behavior.
See the "*ConfigureByConvention Method*" section above for more information. See the "*ConfigureByConvention Method*" section above for more information.
## Advanced Topics ## Advanced Topics

42
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheBase.cs

@ -1,8 +1,10 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Caching; using Volo.Abp.Caching;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities.Events; using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus; using Volo.Abp.EventBus;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Uow; using Volo.Abp.Uow;
namespace Volo.Abp.Domain.Entities.Caching; namespace Volo.Abp.Domain.Entities.Caching;
@ -26,32 +28,52 @@ public abstract class EntityCacheBase<TEntity, TEntityCacheItem, TKey> :
Cache = cache; Cache = cache;
UnitOfWorkManager = unitOfWorkManager; UnitOfWorkManager = unitOfWorkManager;
} }
public virtual async Task<TEntityCacheItem?> FindAsync(TKey id) public virtual async Task<TEntityCacheItem?> FindAsync(TKey id)
{ {
return await Cache.GetOrAddAsync( return await Cache.GetOrAddAsync(
id, id,
async () => MapToCacheItem(await Repository.FindAsync(id))! async () =>
); {
if (HasObjectExtensionInfo())
{
Repository.EnableTracking();
}
return MapToCacheItem(await Repository.FindAsync(id))!;
});
} }
public virtual async Task<TEntityCacheItem> GetAsync(TKey id) public virtual async Task<TEntityCacheItem> GetAsync(TKey id)
{ {
return (await Cache.GetOrAddAsync( return (await Cache.GetOrAddAsync(
id, id,
async () => MapToCacheItem(await Repository.GetAsync(id))! async () =>
))!; {
if (HasObjectExtensionInfo())
{
Repository.EnableTracking();
}
return MapToCacheItem(await Repository.GetAsync(id))!;
}))!;
}
protected virtual bool HasObjectExtensionInfo()
{
return typeof(IHasExtraProperties).IsAssignableFrom(typeof(TEntity)) &&
ObjectExtensionManager.Instance.GetOrNull(typeof(TEntity)) != null;
} }
protected abstract TEntityCacheItem? MapToCacheItem(TEntity? entity); protected abstract TEntityCacheItem? MapToCacheItem(TEntity? entity);
public async Task HandleEventAsync(EntityChangedEventData<TEntity> eventData) public async Task HandleEventAsync(EntityChangedEventData<TEntity> eventData)
{ {
if (eventData is EntityCreatedEventData<TEntity>) if (eventData is EntityCreatedEventData<TEntity>)
{ {
return; return;
} }
/* Why we are using double remove: /* Why we are using double remove:
* First Cache.RemoveAsync drops the cache item in a unit of work. * First Cache.RemoveAsync drops the cache item in a unit of work.
* Some other application / thread may read the value from database and put it to the cache again * Some other application / thread may read the value from database and put it to the cache again
@ -59,12 +81,12 @@ public abstract class EntityCacheBase<TEntity, TEntityCacheItem, TKey> :
* The second Cache.RemoveAsync drops the cache item after the database transaction is complete. * The second Cache.RemoveAsync drops the cache item after the database transaction is complete.
* Only the second Cache.RemoveAsync may not be enough if the application crashes just after the UOW completes. * Only the second Cache.RemoveAsync may not be enough if the application crashes just after the UOW completes.
*/ */
await Cache.RemoveAsync(eventData.Entity.Id); await Cache.RemoveAsync(eventData.Entity.Id);
if(UnitOfWorkManager.Current != null) if(UnitOfWorkManager.Current != null)
{ {
await Cache.RemoveAsync(eventData.Entity.Id, considerUow: true); await Cache.RemoveAsync(eventData.Entity.Id, considerUow: true);
} }
} }
} }

24
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheServiceCollectionExtensions.cs

@ -16,13 +16,8 @@ public static class EntityCacheServiceCollectionExtensions
DistributedCacheEntryOptions? cacheOptions = null) DistributedCacheEntryOptions? cacheOptions = null)
where TEntity : Entity<TKey> where TEntity : Entity<TKey>
{ {
services services.TryAddTransient<IEntityCache<TEntity, TKey>, EntityCacheWithoutCacheItem<TEntity, TKey>>();
.TryAddTransient< services.TryAddTransient<EntityCacheWithoutCacheItem<TEntity, TKey>>();
IEntityCache<TEntity, TKey>,
EntityCacheWithoutCacheItem<TEntity, TKey>
>();
services
.TryAddTransient<EntityCacheWithoutCacheItem<TEntity, TKey>>();
services.Configure<AbpDistributedCacheOptions>(options => services.Configure<AbpDistributedCacheOptions>(options =>
{ {
@ -43,13 +38,8 @@ public static class EntityCacheServiceCollectionExtensions
where TEntity : Entity<TKey> where TEntity : Entity<TKey>
where TEntityCacheItem : class where TEntityCacheItem : class
{ {
services services.TryAddTransient<IEntityCache<TEntityCacheItem, TKey>, EntityCacheWithObjectMapper<TEntity, TEntityCacheItem, TKey>>();
.TryAddTransient< services.TryAddTransient<EntityCacheWithObjectMapper<TEntity, TEntityCacheItem, TKey>>();
IEntityCache<TEntityCacheItem, TKey>,
EntityCacheWithObjectMapper<TEntity, TEntityCacheItem, TKey>
>();
services
.TryAddTransient<EntityCacheWithObjectMapper<TEntity, TEntityCacheItem, TKey>>();
services.Configure<AbpDistributedCacheOptions>(options => services.Configure<AbpDistributedCacheOptions>(options =>
{ {
@ -65,11 +55,7 @@ public static class EntityCacheServiceCollectionExtensions
where TEntity : Entity<TKey> where TEntity : Entity<TKey>
where TEntityCacheItem : class where TEntityCacheItem : class
{ {
services services.TryAddTransient<IEntityCache<TEntityCacheItem, TKey>, EntityCacheWithObjectMapperContext<TObjectMapperContext, TEntity, TEntityCacheItem, TKey>>();
.TryAddTransient<
IEntityCache<TEntityCacheItem, TKey>,
EntityCacheWithObjectMapperContext<TObjectMapperContext, TEntity, TEntityCacheItem, TKey>
>();
services.TryAddTransient<EntityCacheWithObjectMapperContext<TObjectMapperContext, TEntity, TEntityCacheItem, TKey>>(); services.TryAddTransient<EntityCacheWithObjectMapperContext<TObjectMapperContext, TEntity, TEntityCacheItem, TKey>>();
services.Configure<AbpDistributedCacheOptions>(options => services.Configure<AbpDistributedCacheOptions>(options =>

2
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithoutCacheItem.cs

@ -20,6 +20,4 @@ public class EntityCacheWithoutCacheItem<TEntity, TKey> :
{ {
return entity; return entity;
} }
} }

Loading…
Cancel
Save