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
{
protected IReadOnlyRepository<TEntity, TKey> Repository { get; }
protected IDistributedCache<TEntityCacheItem, TKey> Cache { get; }
protected IDistributedCache<EntityCacheItemWrapper<TEntityCacheItem>, TKey> Cache { get; }
protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected EntityCacheBase(
IReadOnlyRepository<TEntity, TKey> repository,
IDistributedCache<TEntityCacheItem, TKey> cache,
IDistributedCache<EntityCacheItemWrapper<TEntityCacheItem>, TKey> cache,
IUnitOfWorkManager unitOfWorkManager)
{
Repository = repository;
@ -31,7 +31,7 @@ public abstract class EntityCacheBase<TEntity, TEntityCacheItem, TKey> :
public virtual async Task<TEntityCacheItem?> FindAsync(TKey id)
{
return await Cache.GetOrAddAsync(
return (await Cache.GetOrAddAsync(
id,
async () =>
{
@ -41,7 +41,7 @@ public abstract class EntityCacheBase<TEntity, TEntityCacheItem, TKey> :
}
return MapToCacheItem(await Repository.FindAsync(id))!;
});
}))?.Value;
}
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))!;
}))!;
}))!.Value!;
}
protected virtual bool HasObjectExtensionInfo()
@ -65,15 +65,10 @@ public abstract class EntityCacheBase<TEntity, TEntityCacheItem, TKey> :
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)
{
if (eventData is EntityCreatedEventData<TEntity>)
{
return;
}
/* Why we are using double remove:
* 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

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(
IReadOnlyRepository<TEntity, TKey> repository,
IDistributedCache<TEntityCacheItem, TKey> cache,
IDistributedCache<EntityCacheItemWrapper<TEntityCacheItem>, TKey> cache,
IUnitOfWorkManager unitOfWorkManager,
IObjectMapper objectMapper)
: base(repository, cache, unitOfWorkManager)
@ -23,7 +23,7 @@ public class EntityCacheWithObjectMapper<TEntity, TEntityCacheItem, TKey> :
ObjectMapper = objectMapper;
}
protected override TEntityCacheItem? MapToCacheItem(TEntity? entity)
protected override EntityCacheItemWrapper<TEntityCacheItem>? MapToCacheItem(TEntity? entity)
{
if (entity == null)
{
@ -32,9 +32,9 @@ public class EntityCacheWithObjectMapper<TEntity, TEntityCacheItem, TKey> :
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(
IReadOnlyRepository<TEntity, TKey> repository,
IDistributedCache<TEntityCacheItem, TKey> cache,
IDistributedCache<EntityCacheItemWrapper<TEntityCacheItem>, TKey> cache,
IUnitOfWorkManager unitOfWorkManager,
IObjectMapper objectMapper)// Intentionally injected with TContext
: 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(
IReadOnlyRepository<TEntity, TKey> repository,
IDistributedCache<TEntity, TKey> cache,
IDistributedCache<EntityCacheItemWrapper<TEntity>, TKey> cache,
IUnitOfWorkManager 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();
}
[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]
public async Task Should_Return_New_EntityCache_IF_Updated()
{

Loading…
Cancel
Save