丢弃已保存的实体

时间:2014-07-23 11:22:46

标签: breeze

我有一个分布式系统,用户可以在其中对一个数据库进行更改。为了说明问题,我们假设我们有以下实体:

public class Product{
   public int Id{get;set;}
   public List<ProductOwner> ProductOwners{get;set;}

}
public class ProductOwner{
  public int ProductId { get; set; }
  [ForeignKey("ProductId")]
  [Inversroperty("ProductOwners")]
  public Product Product{ get; set; }

  public int OwnerId { get; set; }
  [ForeignKey("OwnerId")]
  public Owner Owner{ get; set; }
}

public class Owner{
   public int Id{get;set;}
}

我们还假设我们有两个用户,UserOneUserTwo连接到系统。 UserOne添加Product1并指定Owner1作为所有者。因此,使用ProductOwner1

创建了新的key=[Product1.Id, Owner1.Id]

UserTwo执行相同的操作,创建另一个带ProductOwner2的实例key=[Product1.Id, Owner1.Id]。这将导致服务器端出现EF异常,这是预期的,因为数据库中已存在key=[Product1.Id, Owner1.Id]的行。

问题 通过在UserOneUserTwo计算机上进行某种实时数据刷新(我已经这样做)并在服务器上运行验证任务以忽略而不是保存,可以部分解决上述问题已存在于DB中的实体。 剩下的问题是如何告诉微风用户2&#39;要将ProductOwner2标记为已保存并将其状态从Added更改为Unchanged的计算机?

2 个答案:

答案 0 :(得分:2)

我认为这是一个很好的问题,并且已经提出足够多的内容,我希望能够根据上述情况来讨论我将如何做到这一点,希望其他人能够找到一个很好的方法来实现这个来自Breeze.js透视。这个答案并没有真正解决服务器逻辑,所以它最多也是不完整的。

第1步 - 打开网络广告

首先,我们需要一些方法来告诉其他连接的客户端已经发生了变化。如果您使用ASP.NET MVC堆栈并且有许多其他工具,SignalR是一种很好的方法。

关键是我们不需要有一个很好的方法来传递数据并强制它进入客户端的缓存,我们只需要一种轻量级的方式来告诉客户端某些信息已经存在改变了,如果他们关心这个来刷新一些东西。我在这个领域的建议是使用有效负载告诉客户端实体类型和Id改变了什么,或者给客户端一个资源让他们知道要刷新的实体集合。 JSON有效负载的两个例子在这里运行良好 -

{
    "entityChanges": [
        {
            "id": "123",
            "type": "product",
            "new": false
        },
        {
            "id": "234",
            "type": "product",
            "new": true
        }
    ],
    collectionChanges: [
        {
            "type": "productOwners"
        }
    ]
}

在这种情况下,我们只是告诉客户,ID为123234的产品已更改,而234恰好是新实体。我们不会将有关哪些属性已更改的任何数据推送到客户端,因为他们有责任决定是刷新还是重新查询数据。还有可能告诉客户端刷新整个集合,就像在第二个数组中一样,但我将重点关注第一个例子。

第2步 - 处理更改

好的,我们从Web套接字获得了一个有效负载,我们需要传递给某个分析器以决定是否重新查询。我在这里的建议是检查该实体是否存在于缓存中,如果存在,则刷新它。如果JSON中的标志显示它是一个新实体,我们可能 需要重新查询它。这是一些基本逻辑 -

function checkForChanges (payload) {
    var parsedJson = $.parse(payload);
    $.each(parsedJson.entityChanges, function (index, item) {
        // If it is a new entity,
        if (item.new === true) {
            // Go get it from the database
            manager.fetchEntityByKey(item.type, item.id)
                .then(fetchSucceeded).fail(fetchFailed);
        } else {

            // Check local cache first
            var localentity = manager.getEntityByKey(item.type, item.id);
            // And if we have a local copy already,
            if (localentity) {
                // Go refresh it from the database
                manager.fetchEntityByKey(item.type, item.id)
                    .then(fetchSucceeded).fail(fetchFailed);
            }
        }
    }
}

现在你的应用程序中可能还有一些需要处理的额外逻辑,但我们是在坚果壳中 -

  1. 打开与客户端的轻量级连接以仅侦听更改
  2. 为发生这些更改时创建处理程序
  3. 在如何查询或刷新数据时应用一些逻辑
  4. 此处的一些注意事项是您可能希望根据各种条件使用不同的合并策略。例如,如果实体已经有了更改,您可能希望保留更改,就好像它是一个始终处于不稳定状态的实体,您可能想要覆盖更改。

    http://www.breezejs.com/sites/all/apidocs/classes/MergeStrategy.html

    希望这能提供一些见解,如果它没有直接回答你的问题,我会为排除答案而道歉:)

答案 1 :(得分:1)

是否有可能在breeze客户端上捕获实体框架/唯一键约束错误,并通过创建新的实体管理器(使用createEmptyCopy方法),加载相关的ProductOwner记录并使用它们来确定ProductOwner在原始entityManager中记录的内容需要使用实体的entityAspect的setUnchanged方法设置为“未更改”。完成此“同步”后,可以重试保存更改。

换句话说,客户很乐观,保存会成功但可以在必要时恢复。服务器仍然无视潜在的竞争条件,并且没有自定义代码。

蛮力的做法,如果我说明显的那就道歉。