我正在编写一个简单的事件规划Web应用程序(使用BreezeJS / Entity Framework) - 用户创建锦标赛实体,并要求服务器生成一个或多个建议的计划(这篇文章的目的只有一个)。
每当用户点击“生成计划”时,比赛(包括生成计划所需的大量详细信息)应提交给服务器,服务器应删除任何现有计划,生成新计划,以及客户端模型应该更新。
完全适合名为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.Context进行显式添加和保存,但确实不起作用。
修改
如果我尝试从DB中明确删除旧计划,则不会发生任何事情:
arg[typeof(Plan)].Add(_contextProvider.CreateEntityInfo(deletePlan, EntityState.Deleted););
// Add this:
context.PlanEntries.Remove(deletePlan);
context.SaveChanges();
我猜这是因为_contextProvider.Context已经在缓存中有旧计划,因此将其“隐藏在其背后”(即使用其他上下文)并没有什么区别。
如果我尝试使用_contextProvider.Context删除它,我从框架中得到一个奇怪的重复输入错误。
我的智慧结束了!
编辑2:
这是IEs开发人员工具记录的保存请求和响应中的数据。
首先请求:
{
"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": { }
}
服务器然后删除现有的计划条目(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
}
]
在此回复中,删除的实体永远不会出现,因此客户可以理解地将其留在模型中。
添加的计划(Id 11) 出现,并集成在客户端模型中。
但是从sbelinis回答Server added object showing as added in client after save changes来判断,添加的计划出现的事实可能是一个幸运的巧合:
在您的特定示例中,新实体进入了save,因为它恰好与BeforeSaveEntity方法的实体相关,但您不应该依赖它。
但是,如何正确添加实体的sbelinis示例似乎不完整(例如,它指的是其他地方未使用的局部变量saveMapAdditions)
答案 0 :(得分:0)
好的,我想出了如何解决这个问题!
我仍然无法将删除反映回客户端缓存...但是如果我的服务器端代码还从所有关系中删除了已删除的实体,则删除将被反射回来,并且实体将从客户端模型。
更新的代码(我添加了语句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););
}
}
当然,如果您在客户端本地缓存中搜索计划实体,我怀疑删除的计划仍会出现,因此它并不完美。但它对我有用!