【发布时间】:2016-04-02 16:54:27
【问题描述】:
我创建了一个支持我的 asp.net mvc 控制器的服务。该服务返回一个列表。它是我在创建和编辑视图中呈现的自定义字段列表。由于这些字段是在我想在可用时返回缓存时定义的,当不可用时,创建缓存。由于我正在测试,我还没有定义缓存过期。
当我执行编辑操作时,此服务有助于将查询值映射到自定义字段的缓存列表。发生的事情是我在缓存中的对象被修改了。
我很熟悉 MemoryCache 包含一个引用并且不包含该对象的副本。我不明白为什么 MemoryCache 被修改,当我实际使用一个对象时 - 在我看来 - 不是对缓存的引用并且已传递给方法并且没有定义 ref 或 out 参数。对我来说,参考的范围完全不同?
我尝试了各种方法,但我错过了导致这种行为的基本问题,我真的很想弄清楚这里发生了什么。是否有更广泛的参考范围。局部变量是否仍在方法之间共享?
这是服务中的方法,它要么返回缓存的信息,要么查询数据库并将结果存储在缓存中。它由创建和编辑操作使用。请注意,value 属性被定义为 null,因此 Create 操作以空字段开始。
public IList<CustomField> GetCustomFields()
{
var result = MemoryCache.Default["cache_customfield"] as List<CustomField>;
if (result == null)
{
result = session.Query<CustomField>()
.AsEnumerable()
.Select(c => new CustomField
{
Id = c.Id,
Name = c.Name,
Value = null
})
.ToList();
MemoryCache.Default["cache_customfield"] = result;
}
return result;
}
public static IList<CustomField> MapValues(IList<CustomField> fields, IDictionary<string,string> values = null)
{
// the cached information still has value properties that are null
var a = MemoryCache.Default["cache_customfield"] as List<CustomField>;
foreach (var field in fields.OrderBy(x => x.Name))
{
var persistedValue = string.Empty;
values?.TryGetValue(field.Id, out persistedValue);
field.Value = persistedValue;
}
// the cached information suddenly has value properties that are defined, however the 'fields' parameter has no reference to the original information?!
var b = MemoryCache.Default["cache_customfield"] as List<CustomField>;
return fields;
}
我怀疑这些对情况有多大影响,但这些是控制器上用于创建和编辑的操作。
public ActionResult Create()
{
var ticketService = BusinessServiceFacade.GetTicketService(RavenSession);
var vm = new TicketViewModel();
vm.Controls = ControlViewModel.CreateControls(ticketService.GetCustomFields());
return View(vm);
}
public ActionResult Edit(string id)
{
var ticketService = BusinessServiceFacade.GetTicketService(RavenSession);
var ticket = RavenSession.Load<Ticket>(id);
var customfieldValues = ticket.Attributes.ToDictionary(x => x.Name, x => x.Value);
var vm = new TicketViewModel(ticket);
var listOfCustomFields = TicketService.MapValues(ticketService.GetCustomFields(), customfieldValues);
vm.Controls = ControlViewModel.CreateControls(listOfCustomFields);
return View(vm);
}
所以本质上,当 fields 参数具有自己的范围(不是 ref 或 out)时,为什么我的缓存在 MapValues 方法中被修改。真的很想了解这里发生了什么。
更新:
通过提供新的列表参考进行修改后,我没有注意到任何变化。
看起来引用仍然从局部变量传递到新创建的参数。一件事是用新创建的 CustomField 对象完全建立一个新列表,但如果可能的话,我想避免这种情况。
我可能犯了一个简单的错误。
public ActionResult Create()
{
var ticketService = BusinessServiceFacade.GetTicketService(RavenSession);
var vm = new TicketViewModel();
var fields = ticketService.GetCustomFields();
vm.Controls = ControlViewModel.CreateControls(new List<CustomField>(fields));
return View(vm);
}
public ActionResult Edit(string id)
{
var ticketService = BusinessServiceFacade.GetTicketService(RavenSession);
var ticket = RavenSession.Load<Ticket>(id);
var customfieldValues = ticket.Attributes.ToDictionary(x => x.Name, x => x.Value);
var vm = new TicketViewModel(ticket);
var fields = ticketService.GetCustomFields();
var listOfCustomFields = TicketService.MapValues(new List<CustomField>(fields), customfieldValues);
vm.Controls = ControlViewModel.CreateControls(listOfCustomFields);
return View(vm);
}
解决方案
做一个深拷贝。
public static IList<CustomField> MapValues(IList<CustomField> fields, IDictionary<string,string> values = null)
{
// break reference, deep copy to new list
var oldList = (List<CustomField>) fields;
var newList = oldList.ConvertAll(c => new CustomField(c.Id, c.Name, c.Visible, c.Type, c.TypeFormat, c.Value));
foreach (var field in newList.OrderBy(x => x.Name))
{
var persistedValue = string.Empty;
values?.TryGetValue(field.Id, out persistedValue);
field.Value = persistedValue;
}
return newList;
}
【问题讨论】:
标签: c# asp.net-mvc caching