我正在编写一个简单的事件规划web应用程序(使用BreezeJS/实体框架)-用户创建一个锦标赛实体,并要求服务器生成一个或多个建议的计划(只是一个为这篇文章的目的)。
每当用户点击"生成计划"时,比赛(包括生成计划所需的大量细节)应提交给服务器,服务器应删除任何现有计划,生成新计划,并更新客户端模型。
非常适合名为save的,我想!
问题是最后一步:更新客户端模型。服务器添加的计划实体在客户机中按预期显示,但是删除被忽略。也就是说,客户端最终得到了新的和旧的计划!
这是我的命名保存:
[注意:这个问题的描述和代码省略了很多不相关的细节(比如20个属性和实体类型),以保持问题的大小]
[HttpPost]
public SaveResult MyNamedSave(JObject saveBundle)
{
_contextProvider.BeforeSaveEntitiesDelegate = RecalculatePlan;
return _contextProvider.SaveChanges(saveBundle);
}
private Dictionary<Type, List<EntityInfo>> RecalculatePlan(Dictionary<Type, List<EntityInfo>> arg)
{
// See https://stackoverflow.com/questions/14517945/using-this-context-inside-beforesaveentity:
var readonlyContext = new PontifexContext();
foreach (var eventInfo in arg[typeof(Tournament)])
{
var tournament = (Tournament)eventInfo.Entity;
var deletePlan = readonlyContext.Plans.First(p => p.TournamentId == tournament.Id);
arg[typeof(Plan)].Add(_contextProvider.CreateEntityInfo(deletePlan, EntityState.Deleted););
var addPlan = new Plan {TournamentId = tournament.Id, };
arg[typeof(Plan)].Add(_contextProvider.CreateEntityInfo(addPlan, EntityState.Added););
}
}
我试图使用命名保存的东西,他们不打算做(即删除和添加实体)?
PS:我尝试做一个显式的添加和保存使用readonlyContext和_contextProvider。上下文,但是真的不起作用。
编辑:如果我尝试像下面这样显式地从DB中删除旧的计划,什么也不会发生:
arg[typeof(Plan)].Add(_contextProvider.CreateEntityInfo(deletePlan, EntityState.Deleted););
// Add this:
context.PlanEntries.Remove(deletePlan);
context.SaveChanges();
我猜是因为_contextProvider。上下文已经在缓存中有旧的计划,所以"在背后"删除它(即使用另一个上下文)不会产生影响。
如果我然后尝试删除它使用_contextProvider。上下文,我从框架得到一个奇怪的重复输入错误。
我已经无计可施了!
编辑2:
这是保存请求和响应中的数据,由ie开发人员工具记录。
请求:
{
"entities": [
{
"Id": 1,
"EventName": "Test Tournament",
"EventTime": "2015-03-21T20:00:00.000Z",
"entityAspect": {
"entityTypeName": "Tournament:#Pontifex.Model",
"defaultResourceName": "Tournaments",
"entityState": "Unchanged",
"originalValuesMap": { },
"autoGeneratedKey": {
"propertyName": "Id",
"autoGeneratedKeyType": "Identity"
}
}
}
],
"saveOptions": { }
}
服务器然后删除现有的Plan条目(Id=10),并添加一个新的(Id=11),我直接在DB中使用SELECT进行验证。很好。
但是响应是:
[
{
"$id": "1",
"$type": "Pontifex.Model.Tournament, Pontifex.Server",
"Id": 1,
"EventName": "Test Tournament",
"EventTime": "2015-03-21T20:00:00.000",
"Plans": [
{
"$id": "17",
"$type": "Pontifex.Model.Plan, Pontifex.Server",
"Id": 11,
"TournamentId": 1,
"Tournament": { "$ref": "1" }
}
],
"BoardPlan": null
}
]
在此响应中,删除的实体从未出现,因此客户端可以理解地将其留在其模型中。
添加的Plan (Id 11) 不出现,并且集成在客户端模型中。
BUT:从shbelinis对Server added object的回答来看,在保存更改后客户端显示为已添加,添加的Plan出现的事实可能是一个幸运的巧合:
在您的特定示例中,新实体被保存,因为它恰好与beforeaveentity方法的实体相关,但您不应该依赖它。
但是,如何正确地添加实体的示例似乎不完整(例如,它引用了一个本地变量saveMapAdditions,而该变量在其他地方没有使用)
好了,我知道如何解决这个问题了!
我仍然不能得到删除反映回客户端缓存…但是,如果我的服务器端代码也从所有关系中删除了已删除的实体,则删除将被反射回来,并且实体将从客户端模型中消失。
更新后的代码(我添加了语句tournament.Plans.Remove(deletePlan)
):
[HttpPost]
public SaveResult MyNamedSave(JObject saveBundle)
{
_contextProvider.BeforeSaveEntitiesDelegate = RecalculatePlan;
return _contextProvider.SaveChanges(saveBundle);
}
private Dictionary<Type, List<EntityInfo>> RecalculatePlan(Dictionary<Type, List<EntityInfo>> arg)
{
// See http://stackoverflow.com/questions/14517945/using-this-context-inside-beforesaveentity:
var readonlyContext = new PontifexContext();
foreach (var eventInfo in arg[typeof(Tournament)])
{
var tournament = (Tournament)eventInfo.Entity;
var deletePlan = readonlyContext.Plans.First(p => p.TournamentId == tournament.Id);
arg[typeof(Plan)].Add(_contextProvider.CreateEntityInfo(deletePlan, EntityState.Deleted););
// Workaround: Remove the deleted plan from all relations:
tournament.Plans.Remove(deletePlan);
var addPlan = new Plan {TournamentId = tournament.Id, };
arg[typeof(Plan)].Add(_contextProvider.CreateEntityInfo(addPlan, EntityState.Added););
}
}
当然,如果您在客户端本地缓存中搜索Plan实体,我怀疑删除的计划仍然会出现,因此它并不完美。但它对我有用!