存储库模式和本地缓存
本文关键字:缓存 模式 存储 | 更新日期: 2023-09-27 18:24:43
我有以下接口/类:
public interface IUnitOfWork : IDisposable
{
event EventHandler<EventArgs> Saved;
DbSet<T> Set<T>() where T : class;
DbEntityEntry<T> Entry<T>(T entity) where T : class;
void Commit();
}
以及一个存储库的实现:
public class CachedSqlRepository<T, TKey, TContext> : ICacheRepository<T, TKey, TContext>
where T : class
where TContext : DbContext, IDisposable, new()
{
//A list of the Navigation Properties to include
private readonly Expression<Func<T, object>>[] _NavigationProperties;
public CachedSqlRepository(params Expression<Func<T, object>>[] navigationProperties)
{
_NavigationProperties = navigationProperties;
using (TContext dbContext = new TContext()) //Fetch the List of Entities
{
RefreshCache(dbContext);
}
}
/// <summary>
/// The Collection of Items in the database
/// Note this is a Cache, but should replicate whats in the DB
/// </summary>
public IList<T> Items { get; private set; }
public bool Any(Func<T, bool> predicate)
{
return Items.Any(predicate);
}
public void RefreshCache(DbContext context)
{
switch (_NavigationProperties.Length)
{
case 0:
Items = context.Set<T>().ToList();
break;
case 1:
Items = context.Set<T>().Include(_NavigationProperties[0]).ToList();
break;
//more here
}
}
/// <summary>
/// Refresh the internal cache
/// </summary>
public void RefreshCache()
{
using (TContext dbContext = new TContext())
{
RefreshCache(dbContext);
}
}
public IEnumerable<T> FilterBy(Func<T, bool> predicate)
{
return Items.Where(predicate);
}
public T Add(T entity)
{
T newEntity;
using (TContext dbContext = new TContext())
{
newEntity = dbContext.Set<T>().Add(entity);
if (dbContext.SaveChanges() == 1) //1 change was made
Items.Add(newEntity);
}
return newEntity;
}
public void Delete(TKey id)
{
using (TContext dbContext = new TContext())
{
var attachedEntry = dbContext.Set<T>().Find(id);
if (attachedEntry == null) return; //it doesnt exist anyway!
dbContext.Set<T>().Remove(attachedEntry);
dbContext.SaveChanges();
RefreshCache(dbContext);
}
}
public void Update(T entity, TKey id)
{
if (entity == null) throw new ArgumentException("Cannot update a null entity.");
using (TContext dbContext = new TContext())
{
var entry = dbContext.Entry(entity);
if (entry.State != EntityState.Detached) return;
T attachedEntity = dbContext.Set<T>().Find(id);
if (attachedEntity != null)
{
var attachedEntry = dbContext.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
}
else
{
entry.State = EntityState.Modified; // This should attach entity
}
dbContext.SaveChanges();
RefreshCache(dbContext);
}
}
#region Transaction Methods
public IUnitOfWork StartTransaction()
{
return new EFUnitOfWork(new TContext());
}
public T TransactionAdd(T entity, IUnitOfWork context)
{
context.Saved += OnSave;
return context.Set<T>().Add(entity);
}
public void TransactionDelete(TKey id, IUnitOfWork context)
{
var attachedEntry = context.Set<T>().Find(id);
if (attachedEntry == null) return; //it doesnt exist anyway
context.Saved += OnSave;
context.Set<T>().Remove(attachedEntry);
}
public void TransactionUpdate(T entity, TKey id, IUnitOfWork context)
{
if (entity == null) throw new ArgumentException("Cannot update a null entity.");
var entry = context.Entry(entity);
if (entry.State != EntityState.Detached) return;
T attachedEntity = context.Set<T>().Find(id);
if (attachedEntity != null)
{
var attachedEntry = context.Entry(attachedEntity);
attachedEntry.CurrentValues.SetValues(entity);
}
else
{
entry.State = EntityState.Modified; // This should attach entity
}
context.Saved += OnSave;
}
private void OnSave(object sender, EventArgs e)
{
RefreshCache();
}
#endregion
}
它是根据网络上的各种模式改编的。我不认为这对具有数十万行的表有用,但对查找表等有用——我并不总是碰到DB。
它是有效的,但有些事情不是非常干净,例如我刷新缓存的地方——有时我不得不再次提取所有数据(目前正在进行中)。
这是声音设计吗?还是我在这里重新发明轮子?
一个有趣的问题+1。在我看来,上下文内容缓存是一个最好做得好或不做得好的缓存。并使用数据库缓存。
原因:
- 并行WP都有一个缓存
- 每个WP可能都有线程,上下文不是线程安全的
- 每个线程都应该有一个缓存吗
- 缓存会话是否持久?
- 否:您重新加载每个请求
- 是:您在ASP.NET、EnterpriseLibary缓存或类似平台上使用全局缓存吗?
- 您是否正确管理缓存
- 如何处理并发和更改
- 您是否考虑过上下文生命周期的最佳实践?一些专家建议寿命短
- 数据库是否位于Web服务器附近的局域网上
- 您是否比较了使用DB缓冲区访问时的响应时间
在不同的环境中研究了这个主题,而不仅仅是EF/.NET/SQL Server,我得出的结论是,除非数据库服务器已经成为或正在成为CPU瓶颈,并且无法轻松扩展,否则向数据库提供内存并让它缓存100sMB是一种非常合理的方法在构建或尝试缓存条目之前。我宁愿在SQLServer上抛出GB或RAM,然后在WebServer上的应用程序节点中对自己进行编码。
当每微秒都很重要,或者您的数据库在网络跨度上因延迟/吞吐量问题而分离,并且您的数据是非易失性的,不需要缓存过期/并发管理时。然后继续实现缓存。
仔细考虑内存使用、缓存构建时间和内存持久性模型。
看看一些用于缓存想法和潜在解决方案的工具。例如Enterprise Caching Block。
祝你好运。