Commit 5737786c authored by 阿星Plus's avatar 阿星Plus

chche entity

parent 904fdb8c
using Plus.Domain.Entities;
using Plus.Domain.Repositories;
using Plus.Event.Bus.Entities;
using Plus.Event.Bus.Handlers;
using Plus.ObjectMapping;
using Plus.Runtime.Caching;
using System.Threading.Tasks;
namespace Plus.Domain.Caching
{
public class EntityCache<TEntity, TCacheItem> :
EntityCache<TEntity, TCacheItem, int>,
IEntityCache<TCacheItem>
where TEntity : class, IEntity<int>
{
public EntityCache(
ICacheManager cacheManager,
IRepository<TEntity, int> repository,
string cacheName = null)
: base(
cacheManager,
repository,
cacheName)
{
}
}
public 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);
}
}
public IObjectMapper ObjectMapper { get; set; }
protected ICacheManager CacheManager { get; private set; }
protected IRepository<TEntity, TPrimaryKey> Repository { get; private set; }
public EntityCache(
ICacheManager cacheManager,
IRepository<TEntity, TPrimaryKey> repository,
string cacheName = null)
{
Repository = repository;
CacheManager = cacheManager;
CacheName = cacheName ?? GenerateDefaultCacheName();
ObjectMapper = NullObjectMapper.Instance;
}
public virtual TCacheItem Get(TPrimaryKey id)
{
return InternalCache.Get(id, () => GetCacheItemFromDataSource(id));
}
public virtual Task<TCacheItem> GetAsync(TPrimaryKey id)
{
return InternalCache.GetAsync(id, () => GetCacheItemFromDataSourceAsync(id));
}
public virtual 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 virtual TCacheItem MapToCacheItem(TEntity entity)
{
if (ObjectMapper is NullObjectMapper)
{
throw new PlusException(
string.Format(
"MapToCacheItem method should be overrided or IObjectMapper should be implemented in order to map {0} to {1}",
typeof(TEntity),
typeof(TCacheItem)
)
);
}
return ObjectMapper.Map<TCacheItem>(entity);
}
protected virtual string GenerateDefaultCacheName()
{
return GetType().FullName;
}
public override string ToString()
{
return string.Format("EntityCache {0}", CacheName);
}
}
}
\ No newline at end of file
using Plus.Runtime.Caching;
using System.Threading.Tasks;
namespace Plus.Domain.Caching
{
/// <summary>
/// IEntityCache
/// </summary>
/// <typeparam name="TCacheItem"></typeparam>
public interface IEntityCache<TCacheItem> : IEntityCache<TCacheItem, int>
{
}
/// <summary>
/// IEntityCache
/// </summary>
/// <typeparam name="TCacheItem"></typeparam>
/// <typeparam name="TPrimaryKey"></typeparam>
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);
}
}
\ No newline at end of file
using System;
namespace Plus.Event.Bus.Entities
{
/// <summary>
/// Used to pass data for an event when an entity (<see cref="IEntity"/>) is changed (created, updated or deleted).
/// See <see cref="EntityCreatedEventData{TEntity}"/>, <see cref="EntityDeletedEventData{TEntity}"/> and <see cref="EntityUpdatedEventData{TEntity}"/> classes.
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
[Serializable]
public class EntityChangedEventData<TEntity> : EntityEventData<TEntity>
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="entity">Changed entity in this event</param>
public EntityChangedEventData(TEntity entity)
: base(entity)
{
}
}
}
\ No newline at end of file
using System;
namespace Plus.Event.Bus.Entities
{
/// <summary>
/// Used to pass data for an event that is related to with an <see cref="IEntity"/> object.
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
[Serializable]
public class EntityEventData<TEntity> : EventData, IEventDataWithInheritableGenericArgument
{
/// <summary>
/// Related entity with this event.
/// </summary>
public TEntity Entity { get; private set; }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="entity">Related entity with this event</param>
public EntityEventData(TEntity entity)
{
Entity = entity;
}
public virtual object[] GetConstructorArgs()
{
return new object[] { Entity };
}
}
}
\ No newline at end of file
namespace Plus.ObjectMapping
{
/// <summary>
/// Defines a simple interface to map objects.
/// </summary>
public interface IObjectMapper
{
/// <summary>
/// Converts an object to another. Creates a new object of <see cref="TDestination"/>.
/// </summary>
/// <typeparam name="TDestination">Type of the destination object</typeparam>
/// <param name="source">Source object</param>
TDestination Map<TDestination>(object source);
/// <summary>
/// Execute a mapping from the source object to the existing destination object
/// </summary>
/// <typeparam name="TSource">Source type</typeparam>
/// <typeparam name="TDestination">Destination type</typeparam>
/// <param name="source">Source object</param>
/// <param name="destination">Destination object</param>
/// <returns>Returns the same <see cref="destination"/> object after mapping operation</returns>
TDestination Map<TSource, TDestination>(TSource source, TDestination destination);
}
}
\ No newline at end of file
using Plus.Dependency;
namespace Plus.ObjectMapping
{
public sealed class NullObjectMapper : IObjectMapper, ISingletonDependency
{
/// <summary>
/// Singleton instance.
/// </summary>
public static NullObjectMapper Instance { get; } = new NullObjectMapper();
public TDestination Map<TDestination>(object source)
{
throw new PlusException("Plus.ObjectMapping.IObjectMapper should be implemented in order to map objects.");
}
public TDestination Map<TSource, TDestination>(TSource source, TDestination destination)
{
throw new PlusException("Plus.ObjectMapping.IObjectMapper should be implemented in order to map objects.");
}
}
}
\ No newline at end of file
using System;
using System.Linq;
using System.Threading.Tasks;
namespace Plus.Runtime.Caching
{
/// <summary>
/// Extension methods for <see cref="ICache"/>.
/// </summary>
public static class CacheExtensions
{
public static object Get(this ICache cache, string key, Func<object> factory)
{
return cache.Get(key, k => factory());
}
public static object[] Get(this ICache cache, string[] keys, Func<object> factory)
{
return keys.Select(key => cache.Get(key, k => factory())).ToArray();
}
public static Task<object> GetAsync(this ICache cache, string key, Func<Task<object>> factory)
{
return cache.GetAsync(key, k => factory());
}
public static Task<object[]> GetAsync(this ICache cache, string[] keys, Func<Task<object>> factory)
{
var tasks = keys.Select(key => cache.GetAsync(key, k => factory()));
return Task.WhenAll(tasks);
}
public static ITypedCache<TKey, TValue> AsTyped<TKey, TValue>(this ICache cache)
{
return new TypedCacheWrapper<TKey, TValue>(cache);
}
public static TValue Get<TKey, TValue>(this ICache cache, TKey key, Func<TKey, TValue> factory)
{
return (TValue)cache.Get(key.ToString(), (k) => (object)factory(key));
}
public static TValue[] Get<TKey, TValue>(this ICache cache, TKey[] keys, Func<TKey, TValue> factory)
{
return keys.Select(key => (TValue)cache.Get(key.ToString(), (k) => (object)factory(key))).ToArray();
}
public static TValue Get<TKey, TValue>(this ICache cache, TKey key, Func<TValue> factory)
{
return cache.Get(key, (k) => factory());
}
public static TValue[] Get<TKey, TValue>(this ICache cache, TKey[] keys, Func<TValue> factory)
{
return keys.Select(key => cache.Get(key, (k) => factory())).ToArray();
}
public static async Task<TValue> GetAsync<TKey, TValue>(this ICache cache, TKey key, Func<TKey, Task<TValue>> factory)
{
var value = await cache.GetAsync(key.ToString(), async (keyAsString) =>
{
var v = await factory(key);
return (object)v;
});
return (TValue)value;
}
public static async Task<TValue[]> GetAsync<TKey, TValue>(this ICache cache, TKey[] keys, Func<TKey, Task<TValue>> factory)
{
var tasks = keys.Select(key =>
{
return cache.GetAsync(key.ToString(), async (keyAsString) =>
{
var v = await factory(key);
return (object)v;
});
});
var values = await Task.WhenAll(tasks);
return values.Select(value => (TValue)value).ToArray();
}
public static Task<TValue> GetAsync<TKey, TValue>(this ICache cache, TKey key, Func<Task<TValue>> factory)
{
return cache.GetAsync(key, (k) => factory());
}
public static Task<TValue[]> GetAsync<TKey, TValue>(this ICache cache, TKey[] keys, Func<Task<TValue>> factory)
{
var tasks = keys.Select(key => cache.GetAsync(key, (k) => factory()));
return Task.WhenAll(tasks);
}
public static TValue GetOrDefault<TKey, TValue>(this ICache cache, TKey key)
{
var value = cache.GetOrDefault(key.ToString());
if (value == null)
{
return default(TValue);
}
return (TValue)value;
}
public static TValue[] GetOrDefault<TKey, TValue>(this ICache cache, TKey[] keys)
{
var values = keys.Select(key =>
{
var value = cache.GetOrDefault(key.ToString());
if (value == null)
{
return default(TValue);
}
return (TValue)value;
});
return values.ToArray();
}
public static async Task<TValue> GetOrDefaultAsync<TKey, TValue>(this ICache cache, TKey key)
{
var value = await cache.GetOrDefaultAsync(key.ToString());
if (value == null)
{
return default(TValue);
}
return (TValue)value;
}
public static async Task<TValue[]> GetOrDefaultAsync<TKey, TValue>(this ICache cache, TKey[] keys)
{
var tasks = keys.Select(async (key) =>
{
var value = await cache.GetOrDefaultAsync(key.ToString());
if (value == null)
{
return default(TValue);
}
return (TValue)value;
});
return await Task.WhenAll(tasks);
}
}
}
\ No newline at end of file
namespace Plus.Runtime.Caching
{
/// <summary>
/// Extension methods for <see cref="ICacheManager"/>.
/// </summary>
public static class CacheManagerExtensions
{
public static ITypedCache<TKey, TValue> GetCache<TKey, TValue>(this ICacheManager cacheManager, string name)
{
return cacheManager.GetCache(name).AsTyped<TKey, TValue>();
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
namespace Plus.Runtime.Caching
{
/// <summary>
/// An upper level container for <see cref="ICache"/> objects.
/// A cache manager should work as Singleton and track and manage <see cref="ICache"/> objects.
/// </summary>
public interface ICacheManager : IDisposable
{
/// <summary>
/// Gets all caches.
/// </summary>
/// <returns>List of caches</returns>
IReadOnlyList<ICache> GetAllCaches();
/// <summary>
/// Gets a <see cref="ICache"/> instance.
/// It may create the cache if it does not already exists.
/// </summary>
/// <param name="name">
/// Unique and case sensitive name of the cache.
/// </param>
/// <returns>The cache reference</returns>
ICache GetCache(string name = "Plus");
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Plus.Runtime.Caching
{
/// <summary>
/// An interface to work with cache in a typed manner.
/// Use <see cref="CacheExtensions.AsTyped{TKey,TValue}"/> method
/// to convert a <see cref="ICache"/> to this interface.
/// </summary>
/// <typeparam name="TKey">Key type for cache items</typeparam>
/// <typeparam name="TValue">Value type for cache items</typeparam>
public interface ITypedCache<TKey, TValue> : IDisposable
{
/// <summary>
/// Unique name of the cache.
/// </summary>
string Name { get; }
/// <summary>
/// Default sliding expire time of cache items.
/// </summary>
TimeSpan DefaultSlidingExpireTime { get; set; }
/// <summary>
/// Gets the internal cache.
/// </summary>
ICache InternalCache { get; }
/// <summary>
/// Gets an item from the cache.
/// </summary>
/// <param name="key">Key</param>
/// <param name="factory">Factory method to create cache item if not exists</param>
/// <returns>Cached item</returns>
TValue Get(TKey key, Func<TKey, TValue> factory);
/// <summary>
/// Gets items from the cache.
/// </summary>
/// <param name="keys">Keys</param>
/// <param name="factory">Factory method to create cache item if not exists</param>
/// <returns>Cached items</returns>
TValue[] Get(TKey[] keys, Func<TKey, TValue> factory);
/// <summary>
/// Gets an item from the cache.
/// </summary>
/// <param name="key">Key</param>
/// <param name="factory">Factory method to create cache item if not exists</param>
/// <returns>Cached item</returns>
Task<TValue> GetAsync(TKey key, Func<TKey, Task<TValue>> factory);
/// <summary>
/// Gets items from the cache.
/// </summary>
/// <param name="keys">Keys</param>
/// <param name="factory">Factory method to create cache item if not exists</param>
/// <returns>Cached item</returns>
Task<TValue[]> GetAsync(TKey[] keys, Func<TKey, Task<TValue>> factory);
/// <summary>
/// Gets an item from the cache or null if not found.
/// </summary>
/// <param name="key">Key</param>
/// <returns>Cached item or null if not found</returns>
TValue GetOrDefault(TKey key);
/// <summary>
/// Gets items from the cache. For every key that is not found, a null value is returned.
/// </summary>
/// <param name="keys">Keys</param>
/// <returns>Cached items</returns>
TValue[] GetOrDefault(TKey[] keys);
/// <summary>
/// Gets an item from the cache or null if not found.
/// </summary>
/// <param name="key">Key</param>
/// <returns>Cached item or null if not found</returns>
Task<TValue> GetOrDefaultAsync(TKey key);
/// <summary>
/// Gets items from the cache. For every key that is not found, a null value is returned.
/// </summary>
/// <param name="keys">Keys</param>
/// <returns>Cached items</returns>
Task<TValue[]> GetOrDefaultAsync(TKey[] keys);
/// <summary>
/// Saves/Overrides an item in the cache by a key.
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
/// <param name="slidingExpireTime">Sliding expire time</param>
/// <param name="absoluteExpireTime">Absolute expire time</param>
void Set(TKey key, TValue value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null);
/// <summary>
/// Saves/Overrides items in the cache by the pairs.
/// </summary>
/// <param name="pairs">Pairs</param>
/// <param name="slidingExpireTime">Sliding expire time</param>
/// <param name="absoluteExpireTime">Absolute expire time</param>
void Set(KeyValuePair<TKey, TValue>[] pairs, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null);
/// <summary>
/// Saves/Overrides an item in the cache by a key.
/// </summary>
/// <param name="key">Key</param>
/// <param name="value">Value</param>
/// <param name="slidingExpireTime">Sliding expire time</param>
/// <param name="absoluteExpireTime">Absolute expire time</param>
Task SetAsync(TKey key, TValue value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null);
/// <summary>
/// Saves/Overrides items in the cache by the pairs.
/// </summary>
/// <param name="pairs">Pairs</param>
/// <param name="slidingExpireTime">Sliding expire time</param>
/// <param name="absoluteExpireTime">Absolute expire time</param>
Task SetAsync(KeyValuePair<TKey, TValue>[] pairs, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null);
/// <summary>
/// Removes a cache item by it's key (does nothing if given key does not exists in the cache).
/// </summary>
/// <param name="key">Key</param>
void Remove(TKey key);
/// <summary>
/// Removes cache items by their keys.
/// </summary>
/// <param name="keys">Keys</param>
void Remove(TKey[] keys);
/// <summary>
/// Removes a cache item by it's key (does nothing if given key does not exists in the cache).
/// </summary>
/// <param name="key">Key</param>
Task RemoveAsync(TKey key);
/// <summary>
/// Removes cache items by their keys.
/// </summary>
/// <param name="keys">Keys</param>
Task RemoveAsync(TKey[] keys);
/// <summary>
/// Clears all items in this cache.
/// </summary>
void Clear();
/// <summary>
/// Clears all items in this cache.
/// </summary>
Task ClearAsync();
}
}
\ No newline at end of file
using System;
using System.Threading.Tasks;
namespace Plus.Runtime.Caching
{
/// <summary>
/// Extension methods for <see cref="ITypedCache{TKey,TValue}"/>.
/// </summary>
public static class TypedCacheExtensions
{
public static TValue Get<TKey, TValue>(this ITypedCache<TKey, TValue> cache, TKey key, Func<TValue> factory)
{
return cache.Get(key, k => factory());
}
public static Task<TValue> GetAsync<TKey, TValue>(this ITypedCache<TKey, TValue> cache, TKey key, Func<Task<TValue>> factory)
{
return cache.GetAsync(key, k => factory());
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Plus.Runtime.Caching
{
/// <summary>
/// Implements <see cref="ITypedCache{TKey,TValue}"/> to wrap a <see cref="ICache"/>.
/// </summary>
/// <typeparam name="TKey"></typeparam>
/// <typeparam name="TValue"></typeparam>
public class TypedCacheWrapper<TKey, TValue> : ITypedCache<TKey, TValue>
{
public string Name
{
get { return InternalCache.Name; }
}
public TimeSpan DefaultSlidingExpireTime
{
get { return InternalCache.DefaultSlidingExpireTime; }
set { InternalCache.DefaultSlidingExpireTime = value; }
}
public TimeSpan? DefaultAbsoluteExpireTime
{
get { return InternalCache.DefaultAbsoluteExpireTime; }
set { InternalCache.DefaultAbsoluteExpireTime = value; }
}
public ICache InternalCache { get; private set; }
/// <summary>
/// Creates a new <see cref="TypedCacheWrapper{TKey,TValue}"/> object.
/// </summary>
/// <param name="internalCache">The actual internal cache</param>
public TypedCacheWrapper(ICache internalCache)
{
InternalCache = internalCache;
}
public void Dispose()
{
InternalCache.Dispose();
}
public void Clear()
{
InternalCache.Clear();
}
public Task ClearAsync()
{
return InternalCache.ClearAsync();
}
public TValue Get(TKey key, Func<TKey, TValue> factory)
{
return InternalCache.Get(key, factory);
}
public TValue[] Get(TKey[] keys, Func<TKey, TValue> factory)
{
return InternalCache.Get(keys, factory);
}
public Task<TValue> GetAsync(TKey key, Func<TKey, Task<TValue>> factory)
{
return InternalCache.GetAsync(key, factory);
}
public Task<TValue[]> GetAsync(TKey[] keys, Func<TKey, Task<TValue>> factory)
{
return InternalCache.GetAsync(keys, factory);
}
public TValue GetOrDefault(TKey key)
{
return InternalCache.GetOrDefault<TKey, TValue>(key);
}
public TValue[] GetOrDefault(TKey[] keys)
{
return InternalCache.GetOrDefault<TKey, TValue>(keys);
}
public Task<TValue> GetOrDefaultAsync(TKey key)
{
return InternalCache.GetOrDefaultAsync<TKey, TValue>(key);
}
public Task<TValue[]> GetOrDefaultAsync(TKey[] keys)
{
return InternalCache.GetOrDefaultAsync<TKey, TValue>(keys);
}
public void Set(TKey key, TValue value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null)
{
InternalCache.Set(key.ToString(), value, slidingExpireTime, absoluteExpireTime);
}
public void Set(KeyValuePair<TKey, TValue>[] pairs, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null)
{
var stringPairs = pairs.Select(p => new KeyValuePair<string, object>(p.Key.ToString(), p.Value));
InternalCache.Set(stringPairs.ToArray(), slidingExpireTime, absoluteExpireTime);
}
public Task SetAsync(TKey key, TValue value, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null)
{
return InternalCache.SetAsync(key.ToString(), value, slidingExpireTime, absoluteExpireTime);
}
public Task SetAsync(KeyValuePair<TKey, TValue>[] pairs, TimeSpan? slidingExpireTime = null, TimeSpan? absoluteExpireTime = null)
{
var stringPairs = pairs.Select(p => new KeyValuePair<string, object>(p.Key.ToString(), p.Value));
return InternalCache.SetAsync(stringPairs.ToArray(), slidingExpireTime, absoluteExpireTime);
}
public void Remove(TKey key)
{
InternalCache.Remove(key.ToString());
}
public void Remove(TKey[] keys)
{
InternalCache.Remove(keys.Select(key => key.ToString()).ToArray());
}
public Task RemoveAsync(TKey key)
{
return InternalCache.RemoveAsync(key.ToString());
}
public Task RemoveAsync(TKey[] keys)
{
return InternalCache.RemoveAsync(keys.Select(key => key.ToString()).ToArray());
}
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment