我开始研究ASP.NET MVC站点的缓存基础架构。问题是,我似乎找不到合理的数据缓存位置(除了“无处不在”)
现在我的架构看起来像这样:
控制器 - >服务层 - >库。存储库使用Linq to SQL进行数据访问。
存储库公开了一些通用方法,如Insert,GetById和GetQueryable,它们返回服务层可以进一步细化的IQueryable。
我喜欢将缓存放在存储库层中的想法,因为服务层不应该真正关心数据来自何处。但问题是缓存失效。服务层有关于数据何时变得陈旧而不是存储库的更多信息。例如:
假设我们有一个Users表和一个Orders表(规范示例)。服务层提供了GetOrder(int id)等方法,它们可以调用存储库层:
public Order GetOrder(int id)
{
using(var repo = _repoFactory.Create<Order>())
{
return repo.GetById(id)
}
}
或
repo.GetQueryable(order => order.Id == id && order.HasShipped == false).Single();
如果我们在存储库层中进行缓存,那么在知道订单数据何时发生变化时,它似乎非常有限。假设用户已被删除,导致所有订单都被CASCADE删除。服务层可能使Orders缓存无效,因为它知道用户刚被删除。虽然存储库(因为它是工作单元),但是不知道。 (忽略我们不应该查询已删除用户的订单这一事实,因为它只是一个例子)。
在其他情况下,我认为这表明了这一点。假设我们想要获取所有用户订单:
repo.GetQueryable(order => order.UserId == userId).ToList()
存储库可以缓存此查询的结果,但是,如果添加了另一个订单,则此查询不再有效。但是只有服务层才知道这一点。
我对存储库层的理解也是错误的。我把它视为围绕数据源的外观(即从L2SQL更改为EF到任何东西,服务层不知道底层源)。
答案 0 :(得分:8)
实际上,你需要另一层;数据缓存层。请求数据时,服务层将使用它。根据这样的请求,它将决定它是否具有缓存中的数据,或者是否需要查询相应的存储库。同样,您的服务层可以告知此新数据缓存层失效(删除特定用户等)。
这对您的架构意味着什么,是您的数据缓存层将实现您的存储库所执行的相同接口。一个相当简单的实现将按实体类型和密钥缓存数据。但是,如果你在幕后使用更复杂的ORM(NHibernate,EF 4等),它应该有缓存作为你的选择。
答案 1 :(得分:2)
您可以在存储库返回的对象上放置一个事件,并让存储库将缓存失效订阅到处理程序。
例如,
public class SomethingRepository{
public Something GetById(int id){
var something = _table.Single(x=>x.id==id);
something.DataChanged += this.InvalidateCache;
return something;
}
public void InvalidateCache(object sender, EventArgs e){
// invalidate your cache
}
}
您的Something
对象需要有DataChanged
个事件和一些公共方法供服务层调用才能触发它。像,
public class Something{
private int _id;
public int Id{
get { return _id; }
set {
if( _id != value )
{
_id = value;
OnDataChanged();
}
}
}
public event EventHandler DataChanged;
public void OnDataChanged(){
if(DataChanged!=null)
DataChanged(this, EventArgs.Empty);
}
}
因此,您的所有服务层需要知道的是数据正在被更改,并且存储库处理缓存失效。
我还建议您接受Ventaur的建议,并将缓存失效逻辑放在单独的服务中。您不需要创建单独的“数据缓存层”,但如果保存在不同的类中,逻辑将更清晰。