Skip to content

Commit

Permalink
#1044: Create an EntityCache base class to easily cache entities.
Browse files Browse the repository at this point in the history
  • Loading branch information
hikalkan committed May 23, 2016
1 parent 97dddfd commit 9aefef9
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/Abp/Abp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
<Compile Include="Authorization\IPermissionDependency.cs" />
<Compile Include="Authorization\IPermissionDependencyContext.cs" />
<Compile Include="Configuration\Startup\AbpStartupConfigurationExtensions.cs" />
<Compile Include="Domain\Entities\Caching\IEntityCache.cs" />
<Compile Include="Domain\Uow\ConnectionStringResolveArgs.cs" />
<Compile Include="Domain\Uow\DefaultConnectionStringResolver.cs" />
<Compile Include="Domain\Uow\IConnectionStringResolver.cs" />
Expand Down Expand Up @@ -128,6 +129,7 @@
<Compile Include="RegularGuidGenerator.cs" />
<Compile Include="Notifications\LocalizableMessageNotificationData.cs" />
<Compile Include="Notifications\MessageNotificationData.cs" />
<Compile Include="Domain\Entities\Caching\EntityCache.cs" />
<Compile Include="Runtime\Security\SimpleStringCipher.cs" />
<Compile Include="SequentialGuidGenerator.cs" />
<Compile Include="Domain\Entities\EntityHelper.cs" />
Expand Down
103 changes: 103 additions & 0 deletions src/Abp/Domain/Entities/Caching/EntityCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using System.Threading.Tasks;
using Abp.Domain.Repositories;
using Abp.Events.Bus.Entities;
using Abp.Events.Bus.Handlers;
using Abp.Runtime.Caching;

namespace Abp.Domain.Entities.Caching
{
public abstract class EntityCache<TEntity, TCacheItem> :
EntityCache<TEntity, TCacheItem, int>,
IEventHandler<EntityChangedEventData<TEntity>>, IEntityCache<TCacheItem>
where TEntity : class, IEntity<int>
{
protected EntityCache(
ICacheManager cacheManager,
IRepository<TEntity, int> repository,
string cacheName = null)
: base(
cacheManager,
repository,
cacheName)
{
}
}

public abstract class EntityCache<TEntity, TCacheItem, TPrimaryKey> :
IEventHandler<EntityChangedEventData<TEntity>>, IEntityCache<TCacheItem, TPrimaryKey>
where TEntity : class, IEntity<TPrimaryKey>
{
public TCacheItem this[TPrimaryKey id]
{
get { return Get(id); }
}

public string CacheName { get; private set; }

public ITypedCache<TPrimaryKey, TCacheItem> InternalCache
{
get
{
return CacheManager.GetCache<TPrimaryKey, TCacheItem>(CacheName);
}
}

protected ICacheManager CacheManager { get; private set; }

protected IRepository<TEntity, TPrimaryKey> Repository { get; private set; }

protected EntityCache(ICacheManager cacheManager, IRepository<TEntity, TPrimaryKey> repository, string cacheName = null)
{
Repository = repository;
CacheManager = cacheManager;
CacheName = cacheName ?? GenerateDefaultCacheName();
}

public TCacheItem Get(TPrimaryKey id)
{
return InternalCache.Get(id, () => GetCacheItemFromDataSource(id));
}

public Task<TCacheItem> GetAsync(TPrimaryKey id)
{
return InternalCache.GetAsync(id, () => GetCacheItemFromDataSourceAsync(id));
}

public void HandleEvent(EntityChangedEventData<TEntity> eventData)
{
InternalCache.Remove(eventData.Entity.Id);
}

protected virtual TCacheItem GetCacheItemFromDataSource(TPrimaryKey id)
{
return MapToCacheItem(GetEntityFromDataSource(id));
}

protected virtual async Task<TCacheItem> GetCacheItemFromDataSourceAsync(TPrimaryKey id)
{
return MapToCacheItem(await GetEntityFromDataSourceAsync(id));
}

protected virtual TEntity GetEntityFromDataSource(TPrimaryKey id)
{
return Repository.FirstOrDefault(id);
}

protected virtual Task<TEntity> GetEntityFromDataSourceAsync(TPrimaryKey id)
{
return Repository.FirstOrDefaultAsync(id);
}

protected abstract TCacheItem MapToCacheItem(TEntity entity);

protected virtual string GenerateDefaultCacheName()
{
return GetType().FullName;
}

public override string ToString()
{
return string.Format("EntityCache {0}", CacheName);
}
}
}
23 changes: 23 additions & 0 deletions src/Abp/Domain/Entities/Caching/IEntityCache.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Threading.Tasks;
using Abp.Runtime.Caching;

namespace Abp.Domain.Entities.Caching
{
public interface IEntityCache<TCacheItem> : IEntityCache<TCacheItem, int>
{

}

public interface IEntityCache<TCacheItem, TPrimaryKey>
{
TCacheItem this[TPrimaryKey id] { get; }

string CacheName { get; }

ITypedCache<TPrimaryKey, TCacheItem> InternalCache { get; }

TCacheItem Get(TPrimaryKey id);

Task<TCacheItem> GetAsync(TPrimaryKey id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
<Compile Include="Auditing\SimpleAuditing_Test.cs" />
<Compile Include="ContactLists\ContactList_MultiTenancy_Tests.cs" />
<Compile Include="ContactLists\Messages_MultiTenancy_Tests.cs" />
<Compile Include="Domain\Entities\Caching\EntityCache_Tests.cs" />
<Compile Include="EntityFramework\ObjectMaterialize_Tests.cs" />
<Compile Include="Features\FeatureSystem_Tests.cs" />
<Compile Include="Net\Mail\SmtpEmailSender_Resolve_Test.cs" />
Expand All @@ -116,6 +117,10 @@
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Abp.AutoMapper\Abp.AutoMapper.csproj">
<Project>{06eb5f7c-b974-4775-a305-f175d645f7c9}</Project>
<Name>Abp.AutoMapper</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Abp.EntityFramework\Abp.EntityFramework.csproj">
<Project>{948F0CAF-3382-4E03-9A0F-0DDB206FE40D}</Project>
<Name>Abp.EntityFramework</Name>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Abp.AutoMapper;
using Abp.Dependency;
using Abp.Domain.Entities.Caching;
using Abp.Domain.Repositories;
using Abp.Runtime.Caching;
using Shouldly;
using Xunit;

namespace Abp.TestBase.SampleApplication.Tests.Domain.Entities.Caching
{
public class EntityCache_Tests : SampleApplicationTestBase
{
private readonly IMessageCache _messageCache;
private readonly IRepository<Message> _messageRepository;

public EntityCache_Tests()
{
_messageCache = Resolve<IMessageCache>();
_messageRepository = Resolve<IRepository<Message>>();
}

[Fact]
public void Cached_Entities_Should_Be_Refreshed_On_Change()
{
//Arrange
AbpSession.TenantId = 1;
var message1 = _messageRepository.Single(m => m.Text == "tenant-1-message-1");

//Act & Assert
_messageCache.Get(message1.Id).Text.ShouldBe(message1.Text);

//Arrange: Update the entity
message1.Text = "host-message-1-updated";
_messageRepository.Update(message1);

//Act & Assert: Cached object should be updated
_messageCache.Get(message1.Id).Text.ShouldBe(message1.Text);
}

public interface IMessageCache : IEntityCache<MessageCacheItem>
{

}

public class MessageCache : EntityCache<Message, MessageCacheItem>, IMessageCache, ITransientDependency
{
public MessageCache(ICacheManager cacheManager, IRepository<Message, int> repository, string cacheName = null)
: base(cacheManager, repository, cacheName)
{

}

protected override MessageCacheItem MapToCacheItem(Message entity)
{
return entity.MapTo<MessageCacheItem>();
}
}

[AutoMapFrom(typeof(Message))]
public class MessageCacheItem
{
public string Text { get; set; }
}
}
}

0 comments on commit 9aefef9

Please sign in to comment.