BreezeJS:为什么在Before / AfterSaveEntitiesDelegate中删除不会传播回客户端?

时间:2015-09-05 16:47:52

标签: breeze

我正在编写一个简单的事件规划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)

1 个答案:

答案 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););
    }
}

当然,如果您在客户端本地缓存中搜索计划实体,我怀疑删除的计划仍会出现,因此它并不完美。但它对我有用!