Browse Source

Merge pull request #22720 from abpframework/EntityCacheItemWrapper

Use `EntityCacheItemWrapper` to prevent duplicate queries.
pull/22726/head
Halil İbrahim Kalkan 1 year ago
committed by GitHub
parent
commit
aaef828960
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 17
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheBase.cs
  2. 12
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheItemWrapper.cs
  3. 8
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithObjectMapper.cs
  4. 2
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithObjectMapperContext.cs
  5. 6
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithoutCacheItem.cs
  6. 23
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityCache_Tests.cs

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

@ -16,12 +16,12 @@ public abstract class EntityCacheBase<TEntity, TEntityCacheItem, TKey> :
where TEntityCacheItem : class where TEntityCacheItem : class
{ {
protected IReadOnlyRepository<TEntity, TKey> Repository { get; } protected IReadOnlyRepository<TEntity, TKey> Repository { get; }
protected IDistributedCache<TEntityCacheItem, TKey> Cache { get; } protected IDistributedCache<EntityCacheItemWrapper<TEntityCacheItem>, TKey> Cache { get; }
protected IUnitOfWorkManager UnitOfWorkManager { get; } protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected EntityCacheBase( protected EntityCacheBase(
IReadOnlyRepository<TEntity, TKey> repository, IReadOnlyRepository<TEntity, TKey> repository,
IDistributedCache<TEntityCacheItem, TKey> cache, IDistributedCache<EntityCacheItemWrapper<TEntityCacheItem>, TKey> cache,
IUnitOfWorkManager unitOfWorkManager) IUnitOfWorkManager unitOfWorkManager)
{ {
Repository = repository; Repository = repository;
@ -31,7 +31,7 @@ public abstract class EntityCacheBase<TEntity, TEntityCacheItem, TKey> :
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 () => async () =>
{ {
@ -41,7 +41,7 @@ public abstract class EntityCacheBase<TEntity, TEntityCacheItem, TKey> :
} }
return MapToCacheItem(await Repository.FindAsync(id))!; return MapToCacheItem(await Repository.FindAsync(id))!;
}); }))?.Value;
} }
public virtual async Task<TEntityCacheItem> GetAsync(TKey id) public virtual async Task<TEntityCacheItem> GetAsync(TKey id)
@ -56,7 +56,7 @@ public abstract class EntityCacheBase<TEntity, TEntityCacheItem, TKey> :
} }
return MapToCacheItem(await Repository.GetAsync(id))!; return MapToCacheItem(await Repository.GetAsync(id))!;
}))!; }))!.Value!;
} }
protected virtual bool HasObjectExtensionInfo() protected virtual bool HasObjectExtensionInfo()
@ -65,15 +65,10 @@ public abstract class EntityCacheBase<TEntity, TEntityCacheItem, TKey> :
ObjectExtensionManager.Instance.GetOrNull(typeof(TEntity)) != null; ObjectExtensionManager.Instance.GetOrNull(typeof(TEntity)) != null;
} }
protected abstract TEntityCacheItem? MapToCacheItem(TEntity? entity); protected abstract EntityCacheItemWrapper<TEntityCacheItem>? MapToCacheItem(TEntity? entity);
public async Task HandleEventAsync(EntityChangedEventData<TEntity> eventData) public async Task HandleEventAsync(EntityChangedEventData<TEntity> eventData)
{ {
if (eventData is EntityCreatedEventData<TEntity>)
{
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

12
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheItemWrapper.cs

@ -0,0 +1,12 @@
namespace Volo.Abp.Domain.Entities.Caching;
public class EntityCacheItemWrapper<TEntityCacheItem>
where TEntityCacheItem : class
{
public TEntityCacheItem? Value { get; set; }
public EntityCacheItemWrapper(TEntityCacheItem? value)
{
Value = value;
}
}

8
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Caching/EntityCacheWithObjectMapper.cs

@ -15,7 +15,7 @@ public class EntityCacheWithObjectMapper<TEntity, TEntityCacheItem, TKey> :
public EntityCacheWithObjectMapper( public EntityCacheWithObjectMapper(
IReadOnlyRepository<TEntity, TKey> repository, IReadOnlyRepository<TEntity, TKey> repository,
IDistributedCache<TEntityCacheItem, TKey> cache, IDistributedCache<EntityCacheItemWrapper<TEntityCacheItem>, TKey> cache,
IUnitOfWorkManager unitOfWorkManager, IUnitOfWorkManager unitOfWorkManager,
IObjectMapper objectMapper) IObjectMapper objectMapper)
: base(repository, cache, unitOfWorkManager) : base(repository, cache, unitOfWorkManager)
@ -23,7 +23,7 @@ public class EntityCacheWithObjectMapper<TEntity, TEntityCacheItem, TKey> :
ObjectMapper = objectMapper; ObjectMapper = objectMapper;
} }
protected override TEntityCacheItem? MapToCacheItem(TEntity? entity) protected override EntityCacheItemWrapper<TEntityCacheItem>? MapToCacheItem(TEntity? entity)
{ {
if (entity == null) if (entity == null)
{ {
@ -32,9 +32,9 @@ public class EntityCacheWithObjectMapper<TEntity, TEntityCacheItem, TKey> :
if (typeof(TEntity) == typeof(TEntityCacheItem)) if (typeof(TEntity) == typeof(TEntityCacheItem))
{ {
return entity.As<TEntityCacheItem>(); return new EntityCacheItemWrapper<TEntityCacheItem>(entity.As<TEntityCacheItem>());
} }
return ObjectMapper.Map<TEntity, TEntityCacheItem>(entity); return new EntityCacheItemWrapper<TEntityCacheItem>(ObjectMapper.Map<TEntity, TEntityCacheItem>(entity));
} }
} }

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

@ -12,7 +12,7 @@ public class EntityCacheWithObjectMapperContext<TObjectMapperContext, TEntity, T
{ {
public EntityCacheWithObjectMapperContext( public EntityCacheWithObjectMapperContext(
IReadOnlyRepository<TEntity, TKey> repository, IReadOnlyRepository<TEntity, TKey> repository,
IDistributedCache<TEntityCacheItem, TKey> cache, IDistributedCache<EntityCacheItemWrapper<TEntityCacheItem>, TKey> cache,
IUnitOfWorkManager unitOfWorkManager, IUnitOfWorkManager unitOfWorkManager,
IObjectMapper objectMapper)// Intentionally injected with TContext IObjectMapper objectMapper)// Intentionally injected with TContext
: base(repository, cache, unitOfWorkManager, objectMapper) : base(repository, cache, unitOfWorkManager, objectMapper)

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

@ -10,14 +10,14 @@ public class EntityCacheWithoutCacheItem<TEntity, TKey> :
{ {
public EntityCacheWithoutCacheItem( public EntityCacheWithoutCacheItem(
IReadOnlyRepository<TEntity, TKey> repository, IReadOnlyRepository<TEntity, TKey> repository,
IDistributedCache<TEntity, TKey> cache, IDistributedCache<EntityCacheItemWrapper<TEntity>, TKey> cache,
IUnitOfWorkManager unitOfWorkManager) IUnitOfWorkManager unitOfWorkManager)
: base(repository, cache, unitOfWorkManager) : base(repository, cache, unitOfWorkManager)
{ {
} }
protected override TEntity? MapToCacheItem(TEntity? entity) protected override EntityCacheItemWrapper<TEntity>? MapToCacheItem(TEntity? entity)
{ {
return entity; return new EntityCacheItemWrapper<TEntity>(entity);
} }
} }

23
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityCache_Tests.cs

@ -70,6 +70,29 @@ public abstract class EntityCache_Tests<TStartupModule> : TestAppTestBase<TStart
(await ProductCacheItem.FindAsync(TestDataBuilder.ProductId)).ShouldBeNull(); (await ProductCacheItem.FindAsync(TestDataBuilder.ProductId)).ShouldBeNull();
} }
[Fact]
public async Task Should_Return_New_EntityCache_IF_Added()
{
var productId = Guid.NewGuid();
(await ProductEntityCache.FindAsync(productId)).ShouldBeNull();
(await ProductCacheItem.FindAsync(productId)).ShouldBeNull();
var product = new Product(productId, "Product2", decimal.Zero);
await ProductRepository.InsertAsync(product);
product = await ProductEntityCache.FindAsync(product.Id);
product.ShouldNotBeNull();
product.Id.ShouldBe(productId);
product.Name.ShouldBe("Product2");
product.Price.ShouldBe(decimal.Zero);
var productCacheItem = await ProductCacheItem.FindAsync(product.Id);
productCacheItem.ShouldNotBeNull();
productCacheItem.Id.ShouldBe(productId);
productCacheItem.Name.ShouldBe("Product2");
productCacheItem.Price.ShouldBe(decimal.Zero);
}
[Fact] [Fact]
public async Task Should_Return_New_EntityCache_IF_Updated() public async Task Should_Return_New_EntityCache_IF_Updated()
{ {

Loading…
Cancel
Save