Skip to content

Commit 9aefef9

Browse files
committed
aspnetboilerplate#1044: Create an EntityCache base class to easily cache entities.
1 parent 97dddfd commit 9aefef9

File tree

5 files changed

+198
-0
lines changed

5 files changed

+198
-0
lines changed

src/Abp/Abp.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100
<Compile Include="Authorization\IPermissionDependency.cs" />
101101
<Compile Include="Authorization\IPermissionDependencyContext.cs" />
102102
<Compile Include="Configuration\Startup\AbpStartupConfigurationExtensions.cs" />
103+
<Compile Include="Domain\Entities\Caching\IEntityCache.cs" />
103104
<Compile Include="Domain\Uow\ConnectionStringResolveArgs.cs" />
104105
<Compile Include="Domain\Uow\DefaultConnectionStringResolver.cs" />
105106
<Compile Include="Domain\Uow\IConnectionStringResolver.cs" />
@@ -128,6 +129,7 @@
128129
<Compile Include="RegularGuidGenerator.cs" />
129130
<Compile Include="Notifications\LocalizableMessageNotificationData.cs" />
130131
<Compile Include="Notifications\MessageNotificationData.cs" />
132+
<Compile Include="Domain\Entities\Caching\EntityCache.cs" />
131133
<Compile Include="Runtime\Security\SimpleStringCipher.cs" />
132134
<Compile Include="SequentialGuidGenerator.cs" />
133135
<Compile Include="Domain\Entities\EntityHelper.cs" />
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using System.Threading.Tasks;
2+
using Abp.Domain.Repositories;
3+
using Abp.Events.Bus.Entities;
4+
using Abp.Events.Bus.Handlers;
5+
using Abp.Runtime.Caching;
6+
7+
namespace Abp.Domain.Entities.Caching
8+
{
9+
public abstract class EntityCache<TEntity, TCacheItem> :
10+
EntityCache<TEntity, TCacheItem, int>,
11+
IEventHandler<EntityChangedEventData<TEntity>>, IEntityCache<TCacheItem>
12+
where TEntity : class, IEntity<int>
13+
{
14+
protected EntityCache(
15+
ICacheManager cacheManager,
16+
IRepository<TEntity, int> repository,
17+
string cacheName = null)
18+
: base(
19+
cacheManager,
20+
repository,
21+
cacheName)
22+
{
23+
}
24+
}
25+
26+
public abstract class EntityCache<TEntity, TCacheItem, TPrimaryKey> :
27+
IEventHandler<EntityChangedEventData<TEntity>>, IEntityCache<TCacheItem, TPrimaryKey>
28+
where TEntity : class, IEntity<TPrimaryKey>
29+
{
30+
public TCacheItem this[TPrimaryKey id]
31+
{
32+
get { return Get(id); }
33+
}
34+
35+
public string CacheName { get; private set; }
36+
37+
public ITypedCache<TPrimaryKey, TCacheItem> InternalCache
38+
{
39+
get
40+
{
41+
return CacheManager.GetCache<TPrimaryKey, TCacheItem>(CacheName);
42+
}
43+
}
44+
45+
protected ICacheManager CacheManager { get; private set; }
46+
47+
protected IRepository<TEntity, TPrimaryKey> Repository { get; private set; }
48+
49+
protected EntityCache(ICacheManager cacheManager, IRepository<TEntity, TPrimaryKey> repository, string cacheName = null)
50+
{
51+
Repository = repository;
52+
CacheManager = cacheManager;
53+
CacheName = cacheName ?? GenerateDefaultCacheName();
54+
}
55+
56+
public TCacheItem Get(TPrimaryKey id)
57+
{
58+
return InternalCache.Get(id, () => GetCacheItemFromDataSource(id));
59+
}
60+
61+
public Task<TCacheItem> GetAsync(TPrimaryKey id)
62+
{
63+
return InternalCache.GetAsync(id, () => GetCacheItemFromDataSourceAsync(id));
64+
}
65+
66+
public void HandleEvent(EntityChangedEventData<TEntity> eventData)
67+
{
68+
InternalCache.Remove(eventData.Entity.Id);
69+
}
70+
71+
protected virtual TCacheItem GetCacheItemFromDataSource(TPrimaryKey id)
72+
{
73+
return MapToCacheItem(GetEntityFromDataSource(id));
74+
}
75+
76+
protected virtual async Task<TCacheItem> GetCacheItemFromDataSourceAsync(TPrimaryKey id)
77+
{
78+
return MapToCacheItem(await GetEntityFromDataSourceAsync(id));
79+
}
80+
81+
protected virtual TEntity GetEntityFromDataSource(TPrimaryKey id)
82+
{
83+
return Repository.FirstOrDefault(id);
84+
}
85+
86+
protected virtual Task<TEntity> GetEntityFromDataSourceAsync(TPrimaryKey id)
87+
{
88+
return Repository.FirstOrDefaultAsync(id);
89+
}
90+
91+
protected abstract TCacheItem MapToCacheItem(TEntity entity);
92+
93+
protected virtual string GenerateDefaultCacheName()
94+
{
95+
return GetType().FullName;
96+
}
97+
98+
public override string ToString()
99+
{
100+
return string.Format("EntityCache {0}", CacheName);
101+
}
102+
}
103+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Threading.Tasks;
2+
using Abp.Runtime.Caching;
3+
4+
namespace Abp.Domain.Entities.Caching
5+
{
6+
public interface IEntityCache<TCacheItem> : IEntityCache<TCacheItem, int>
7+
{
8+
9+
}
10+
11+
public interface IEntityCache<TCacheItem, TPrimaryKey>
12+
{
13+
TCacheItem this[TPrimaryKey id] { get; }
14+
15+
string CacheName { get; }
16+
17+
ITypedCache<TPrimaryKey, TCacheItem> InternalCache { get; }
18+
19+
TCacheItem Get(TPrimaryKey id);
20+
21+
Task<TCacheItem> GetAsync(TPrimaryKey id);
22+
}
23+
}

src/TestBase/Tests/Abp.TestBase.SampleApplication.Tests/Abp.TestBase.SampleApplication.Tests.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
<Compile Include="Auditing\SimpleAuditing_Test.cs" />
104104
<Compile Include="ContactLists\ContactList_MultiTenancy_Tests.cs" />
105105
<Compile Include="ContactLists\Messages_MultiTenancy_Tests.cs" />
106+
<Compile Include="Domain\Entities\Caching\EntityCache_Tests.cs" />
106107
<Compile Include="EntityFramework\ObjectMaterialize_Tests.cs" />
107108
<Compile Include="Features\FeatureSystem_Tests.cs" />
108109
<Compile Include="Net\Mail\SmtpEmailSender_Resolve_Test.cs" />
@@ -116,6 +117,10 @@
116117
<Compile Include="Properties\AssemblyInfo.cs" />
117118
</ItemGroup>
118119
<ItemGroup>
120+
<ProjectReference Include="..\..\..\Abp.AutoMapper\Abp.AutoMapper.csproj">
121+
<Project>{06eb5f7c-b974-4775-a305-f175d645f7c9}</Project>
122+
<Name>Abp.AutoMapper</Name>
123+
</ProjectReference>
119124
<ProjectReference Include="..\..\..\Abp.EntityFramework\Abp.EntityFramework.csproj">
120125
<Project>{948F0CAF-3382-4E03-9A0F-0DDB206FE40D}</Project>
121126
<Name>Abp.EntityFramework</Name>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using Abp.AutoMapper;
2+
using Abp.Dependency;
3+
using Abp.Domain.Entities.Caching;
4+
using Abp.Domain.Repositories;
5+
using Abp.Runtime.Caching;
6+
using Shouldly;
7+
using Xunit;
8+
9+
namespace Abp.TestBase.SampleApplication.Tests.Domain.Entities.Caching
10+
{
11+
public class EntityCache_Tests : SampleApplicationTestBase
12+
{
13+
private readonly IMessageCache _messageCache;
14+
private readonly IRepository<Message> _messageRepository;
15+
16+
public EntityCache_Tests()
17+
{
18+
_messageCache = Resolve<IMessageCache>();
19+
_messageRepository = Resolve<IRepository<Message>>();
20+
}
21+
22+
[Fact]
23+
public void Cached_Entities_Should_Be_Refreshed_On_Change()
24+
{
25+
//Arrange
26+
AbpSession.TenantId = 1;
27+
var message1 = _messageRepository.Single(m => m.Text == "tenant-1-message-1");
28+
29+
//Act & Assert
30+
_messageCache.Get(message1.Id).Text.ShouldBe(message1.Text);
31+
32+
//Arrange: Update the entity
33+
message1.Text = "host-message-1-updated";
34+
_messageRepository.Update(message1);
35+
36+
//Act & Assert: Cached object should be updated
37+
_messageCache.Get(message1.Id).Text.ShouldBe(message1.Text);
38+
}
39+
40+
public interface IMessageCache : IEntityCache<MessageCacheItem>
41+
{
42+
43+
}
44+
45+
public class MessageCache : EntityCache<Message, MessageCacheItem>, IMessageCache, ITransientDependency
46+
{
47+
public MessageCache(ICacheManager cacheManager, IRepository<Message, int> repository, string cacheName = null)
48+
: base(cacheManager, repository, cacheName)
49+
{
50+
51+
}
52+
53+
protected override MessageCacheItem MapToCacheItem(Message entity)
54+
{
55+
return entity.MapTo<MessageCacheItem>();
56+
}
57+
}
58+
59+
[AutoMapFrom(typeof(Message))]
60+
public class MessageCacheItem
61+
{
62+
public string Text { get; set; }
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)