在"MVC缓存01,使用控制器缓存或数据层缓存"中,在数据层中可以设置缓存的有效时间。但这个还不够"智能",常常希望在编辑或创建的时候使缓存失效,加载新的数据。
□ 思路
1、缓存是以键值<string, object>存放的,在创建缓存的时候,先把IDictionary<int,T>作为缓存内容存储,int为T的主键。
2、EF上下文保存的时候时候把变化保存到数据库,并更新缓存中的内容。
● 先找出上下文中状态为added或modified的实体:var changeobjects
● 把变化保存数据到数据库:context.SaveChanges()
● 根据缓存key获取类型为IDictionary<int,T>的缓存内容:var cacheData = Cache.Get("vehicles") as Dictionary<int, Vehicle>;
● 最后遍历这些变化的实体,更新缓存项:cacheData[vehicle.Id] = vehicle;
□ 缓存接口
class="alt"> public interface ICacheProvider
{object Get(string key);
void Set(string key, object data, int cacheTime);
bool IsSet(string key);
void Invalidate(string key);
}
□ 缓存接口实现
围绕using System.Runtime.Caching的MemoryCache.Default返回类型为ObjectCache的缓存属性实现缓存接口:获取缓存项、设置缓存、判断是否设置缓存、清空缓存。
using System;using System.Runtime.Caching;namespace MvcApplication1.Cache{public class DefaultCacheProvider : ICacheProvider
{ private ObjectCache Cache { get { return MemoryCache.Default; }}
public object Get(string key)
{ return Cache[key];}
public void Set(string key, object data, int cacheTime)
{ CacheItemPolicy policy = new CacheItemPolicy();policy.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime);
Cache.Add(new CacheItem(key, data), policy);}
public bool IsSet(string key)
{return (Cache[key] != null);
}
public void Invalidate(string key)
{Cache.Remove(key);
}
}
}
□ Model
public partial class Vehicle
{public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
□ 针对Vehicle的Repositoy接口:
using System.Collections.Generic;using MvcApplication1.Models;namespace MvcApplication1.Repository{public interface IVehicleRepository
{ void ClearCache();IEnumerable<Vehicle> GetVehicles();
void Insert(Vehicle vehicle); void Update(Vehicle vehicle); void SaveChanges();}
}
□ 针对Vehicle的Repositoy接口实现:
using System.Collections.Generic;using System.Data;using System.Data.Entity.Infrastructure;using System.Linq;using MvcApplication1.Cache;using MvcApplication1.Models;namespace MvcApplication1.Repository{public class VehicleRepository : IVehicleRepository
{protected DemoEntities DataContext { get; private set; }
public ICacheProvider Cache { get; set; }public VehicleRepository() : this(new DefaultCacheProvider())
{}
public VehicleRepository(ICacheProvider cacheProvider) {this.DataContext = new DemoEntities();
this.Cache = cacheProvider;}
public void ClearCache()
{ Cache.Invalidate("vehicles");}
public System.Collections.Generic.IEnumerable<Models.Vehicle> GetVehicles() {var vehicles = Cache.Get("vehicles") as IDictionary<int, Vehicle>;
if (vehicles == null)
{vehicles = DataContext.Vehicle.ToDictionary(v => v.Id);
if (vehicles.Any()) { Cache.Set("vehicles",vehicles,30);}
}
return vehicles.Values;
}
public void Update(Vehicle vehicle)
{if (vehicle != null)
{DataContext.Set<Vehicle>().Attach(vehicle);
DataContext.Entry(vehicle).State = EntityState.Modified;
}
}
public void Insert(Vehicle vehicle)
{DataContext.Set<Vehicle>().Add(vehicle);
}
public void SaveChanges()
{ //获取上下文中EntityState状态为added或modified的Vehiclevar changeobjects = DataContext.ChangeTracker.Entries<Vehicle>();
//把变化保存到数据库DataContext.SaveChanges();
//更新缓存中相关的Vehiclevar cacheData = Cache.Get("vehicles") as Dictionary<int, Vehicle>;
if (cacheData != null)
{foreach (var item in changeobjects)
{ var vehicle = item.Entity as Vehicle;cacheData[vehicle.Id] = vehicle;
}
}
}
}
}
在保存缓存Cache.Set("vehicles",vehicles,30)之前,把从上下文获取到的数据转换成IDictionary<int,T>类型vehicles = DataContext.Vehicle.ToDictionary(v => v.Id);
□ HomeController
using System.Linq;using System.Web;using System.Web.Mvc;using MvcApplication1.Models;using MvcApplication1.Repository;namespace MvcApplication1.Controllers{public class HomeController : Controller
{ public IVehicleRepository Repository { get; set; } public HomeController(IVehicleRepository repository) { this.Repository = repository;}
public HomeController() : this(new VehicleRepository())
{}
public ActionResult Index() { return View(Repository.GetVehicles());}
[HttpPost]
public ActionResult Index(FormCollection form) {Repository.ClearCache();
return RedirectToAction("Index");
}
public ActionResult Edit(int id)
{var vehicle = Repository.GetVehicles().Single(v => v.Id == id);
return View(vehicle);}
[HttpPost]
public ActionResult Edit(Vehicle vehicle) {Repository.Update(vehicle);
Repository.SaveChanges();
return RedirectToAction("Index");
}
public ActionResult Create() {return View(new Vehicle());
}
[HttpPost]
public ActionResult Create(Vehicle vehicle) {Repository.Insert(vehicle);
Repository.SaveChanges();
return RedirectToAction("Index");
}
}
}
□ Home/Index.cshtml
@model IEnumerable<MvcApplication1.Models.Vehicle>
@{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml";}
<style type="text/css"> table td {border-collapse: collapse;
border: solid 1px black;
}
</style>
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<th>编号</th>
<th>车型</th>
<th>价格</th>
<th></th>
</tr>
@foreach (var vehicle in Model)
{<tr>
<td>@vehicle.Id.ToString()</td>
<td>@vehicle.Name</td>
<td>@string.Format("{0:c}",vehicle.Price)</td>
<td>
@Html.ActionLink("编辑", "Edit", new { id=vehicle.Id })
</td>
</tr>
}
</table>
@using (Html.BeginForm()){<input type="submit" value="使缓存失效重新获取数据库数据" id="InvalidButton" name="InvalidButton"/>
}
<p>
@Html.ActionLink("创建", "Create")
</p>
□ Home/Create.cshtml
logs_code_Collapse">展开
□ Home/Edit.cshtml
展开
□ 结果:
创建或修改之前:
编辑更新:
创建:
编辑创建成功后:
□ 参考资料
※ DATA CACHING WITH .NET 4.0 AND ASP.NET MVC – PART 2