asp.net-mvc 使用存储库/服务模式和 MVC 时缓存数据对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/102913/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Caching Data Objects when using Repository/Service Pattern and MVC
提问by LaptopHeaven
I have an MVC-based site, which is using a Repository/Service pattern for data access. The Services are written to be using in a majority of applications (console, winform, and web). Currently, the controllers communicate directly to the services. This has limited the ability to apply proper caching.
我有一个基于 MVC 的站点,它使用存储库/服务模式进行数据访问。服务被编写为在大多数应用程序(控制台、winform 和 Web)中使用。目前,控制器直接与服务通信。这限制了应用适当缓存的能力。
I see my options as the following:
我认为我的选择如下:
- Write a wrapper for the web app, which implements the IWhatEverService which does caching.
- Apply caching in each controller by cache the ViewData for each Action.
- Don't worry about data caching and just implement OutputCaching for each Action.
- 为 Web 应用程序编写一个包装器,它实现了 IWhatEverService 进行缓存。
- 通过缓存每个 Action 的 ViewData 在每个控制器中应用缓存。
- 不用担心数据缓存,只需为每个 Action 实现 OutputCaching。
I can see the pros and cons of each. What is/should the best practice be for caching with Repository/Service
我可以看到每个人的优点和缺点。什么是/应该是使用存储库/服务缓存的最佳实践
采纳答案by Brendan Enrick
The easiest way would be to handle caching in your repository provider. That way you don't have to change out any code in the rest of your app; it will be oblivious to the fact that the data was served out of a cache rather than the repository.
最简单的方法是在存储库提供程序中处理缓存。这样您就不必更改应用程序其余部分中的任何代码;它不会注意到数据是从缓存而不是存储库中提供的。
So, I'd create an interface that the controllers use to communicate with the backend, and in the implementation of this I'd add the caching logic. Wrap it all up in a nice bow with some DI, and your app will be set for easy testing.
因此,我将创建一个控制器用于与后端通信的接口,并在此实现中添加缓存逻辑。用一些 DI 把它全部包起来,你的应用程序将被设置为易于测试。
回答by Brendan Enrick
Steve Smith did two great blog posts which demonstrate how to use his CachedRepository pattern to achieve the result you're looking for.
Steve Smith 写了两篇很棒的博客文章,展示了如何使用他的 CachedRepository 模式来实现您正在寻找的结果。
Introducing the CachedRepository Pattern
Building a CachedRepository via Strategy Pattern
In these two posts he shows you how to set up this pattern and also explains why it is useful. By using this pattern you get caching without your existing code seeing any of the caching logic. Essentially you use the cached repository as if it were any other repository.
在这两篇文章中,他向您展示了如何设置此模式,并解释了它为何有用。通过使用这种模式,您可以获得缓存,而您现有的代码不会看到任何缓存逻辑。本质上,您可以像使用任何其他存储库一样使用缓存存储库。
public class CachedAlbumRepository : IAlbumRepository
{
private readonly IAlbumRepository _albumRepository;
public CachedAlbumRepository(IAlbumRepository albumRepository)
{
_albumRepository = albumRepository;
}
private static readonly object CacheLockObject = new object();
public IEnumerable<Album> GetTopSellingAlbums(int count)
{
Debug.Print("CachedAlbumRepository:GetTopSellingAlbums");
string cacheKey = "TopSellingAlbums-" + count;
var result = HttpRuntime.Cache[cacheKey] as List<Album>;
if (result == null)
{
lock (CacheLockObject)
{
result = HttpRuntime.Cache[cacheKey] as List<Album>;
if (result == null)
{
result = _albumRepository.GetTopSellingAlbums(count).ToList();
HttpRuntime.Cache.Insert(cacheKey, result, null,
DateTime.Now.AddSeconds(60), TimeSpan.Zero);
}
}
}
return result;
}
}
回答by Alexei
Based on answer provided by Brendan, I defined a generic cached repository for the special case of relatively small lists that are rarely changed, but heavily read.
根据Brendan提供的答案,我为很少更改但大量读取的相对较小列表的特殊情况定义了一个通用缓存存储库。
1. The interface
1. 界面
public interface IRepository<T> : IRepository
where T : class
{
IQueryable<T> AllNoTracking { get; }
IQueryable<T> All { get; }
DbSet<T> GetSet { get; }
T Get(int id);
void Insert(T entity);
void BulkInsert(IEnumerable<T> entities);
void Delete(T entity);
void RemoveRange(IEnumerable<T> range);
void Update(T entity);
}
2. Normal/non-cached repository
2.普通/非缓存存储库
public class Repository<T> : IRepository<T> where T : class, new()
{
private readonly IEfDbContext _context;
public Repository(IEfDbContext context)
{
_context = context;
}
public IQueryable<T> All => _context.Set<T>().AsQueryable();
public IQueryable<T> AllNoTracking => _context.Set<T>().AsNoTracking();
public IQueryable AllNoTrackingGeneric(Type t)
{
return _context.GetSet(t).AsNoTracking();
}
public DbSet<T> GetSet => _context.Set<T>();
public DbSet GetSetNonGeneric(Type t)
{
return _context.GetSet(t);
}
public IQueryable AllNonGeneric(Type t)
{
return _context.GetSet(t);
}
public T Get(int id)
{
return _context.Set<T>().Find(id);
}
public void Delete(T entity)
{
if (_context.Entry(entity).State == EntityState.Detached)
_context.Set<T>().Attach(entity);
_context.Set<T>().Remove(entity);
}
public void RemoveRange(IEnumerable<T> range)
{
_context.Set<T>().RemoveRange(range);
}
public void Insert(T entity)
{
_context.Set<T>().Add(entity);
}
public void BulkInsert(IEnumerable<T> entities)
{
_context.BulkInsert(entities);
}
public void Update(T entity)
{
_context.Set<T>().Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
}
}
}
3. Generic cached repositoryis based on non-cached one
3.通用缓存存储库基于非缓存存储库
public interface ICachedRepository<T> where T : class, new()
{
string CacheKey { get; }
void InvalidateCache();
void InsertIntoCache(T item);
}
public class CachedRepository<T> : ICachedRepository<T>, IRepository<T> where T : class, new()
{
private readonly IRepository<T> _modelRepository;
private static readonly object CacheLockObject = new object();
private IList<T> ThreadSafeCacheAccessAction(Action<IList<T>> action = null)
{
// refresh cache if necessary
var list = HttpRuntime.Cache[CacheKey] as IList<T>;
if (list == null)
{
lock (CacheLockObject)
{
list = HttpRuntime.Cache[CacheKey] as IList<T>;
if (list == null)
{
list = _modelRepository.All.ToList();
//TODO: remove hardcoding
HttpRuntime.Cache.Insert(CacheKey, list, null, DateTime.UtcNow.AddMinutes(10), Cache.NoSlidingExpiration);
}
}
}
// execute custom action, if one is required
if (action != null)
{
lock (CacheLockObject)
{
action(list);
}
}
return list;
}
public IList<T> GetCachedItems()
{
IList<T> ret = ThreadSafeCacheAccessAction();
return ret;
}
/// <summary>
/// returns value without using cache, to allow Queryable usage
/// </summary>
public IQueryable<T> All => _modelRepository.All;
public IQueryable<T> AllNoTracking
{
get
{
var cachedItems = GetCachedItems();
return cachedItems.AsQueryable();
}
}
// other methods come here
public void BulkInsert(IEnumerable<T> entities)
{
var enumerable = entities as IList<T> ?? entities.ToList();
_modelRepository.BulkInsert(enumerable);
// also inserting items within the cache
ThreadSafeCacheAccessAction((list) =>
{
foreach (var item in enumerable)
list.Add(item);
});
}
public void Delete(T entity)
{
_modelRepository.Delete(entity);
ThreadSafeCacheAccessAction((list) =>
{
list.Remove(entity);
});
}
}
Using a DI framework (I am using Ninject), one can easily define if a repository should be cached or not:
使用 DI 框架(我使用的是 Ninject),可以轻松定义是否应缓存存储库:
// IRepository<T> should be solved using Repository<T>, by default
kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>));
// IRepository<T> must be solved to Repository<T>, if used in CachedRepository<T>
kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>)).WhenInjectedInto(typeof(CachedRepository<>));
// explicit repositories using caching
var cachedTypes = new List<Type>
{
typeof(ImportingSystem), typeof(ImportingSystemLoadInfo), typeof(Environment)
};
cachedTypes.ForEach(type =>
{
// allow access as normal repository
kernel
.Bind(typeof(IRepository<>).MakeGenericType(type))
.To(typeof(CachedRepository<>).MakeGenericType(type));
// allow access as a cached repository
kernel
.Bind(typeof(ICachedRepository<>).MakeGenericType(type))
.To(typeof(CachedRepository<>).MakeGenericType(type));
});
So, reading from cached repositories is done without knowing about the caching. However, changing them requires to inject from ICacheRepository<>and calling the appropriate methods.
因此,从缓存存储库读取是在不知道缓存的情况下完成的。但是,更改它们需要注入ICacheRepository<>和调用适当的方法。
回答by Hrvoje Hudo
Check my implementation of caching service:
How to cache data in a MVC application
(i don't want to repeat answer here...)
Fell free to comment!
检查我的缓存服务实现:
如何在 MVC 应用程序中缓存数据
(我不想在这里重复回答...)欢迎
发表评论!

