微风:子实体已被其他人删除后,在重新加载父实体后它们仍然出现 [英] Breeze: When child entities have been deleted by someone else, they still appear after reloading the parent

查看:90
本文介绍了微风:子实体已被其他人删除后,在重新加载父实体后它们仍然出现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个轻轻松松的客户解决方案,其中显示父实体及其子列表。我们对某些子实体进行硬删除。现在,当用户是执行删除操作的用户时,没有问题,但是当其他用户执行操作时,似乎没有办法使已经加载到缓存中的子项失效。我们与父级进行新查询并扩展到子级,但是微风将它已经听说过的所有其他子级附加,即使数据库未返回它们也是如此。

We have a breeze client solution in which we show parent entities with lists of their children. We do hard deletes on some child entities. Now when the user is the one doing the deletes, there is no problem, but when someone else does, there seems to be no way to invalidate the children already loaded in cache. We do a new query with the parent and expanding to children, but breeze attaches all the other children it has already heard of, even if the database did not return them.

我的问题:难道不应该轻而易举地意识到我们正在通过expand进行加载,从而在从db重新加载结果之前从缓存中完全删除所有子级吗?

My question: shouldn't breeze realize we are loading through expand and thus completely remove all children from cache before loading back the results from the db? How else can we accomplish this if that is not the case?

谢谢

推荐答案

是的,这是非常好的一点。

Yup, that's a really good point.

删除对于每个数据管理工作来说都是一个可怕的麻烦。无论是否使用Breeze,这都是事实。它只会引起上下上下的心痛。这就是为什么我建议使用软删除而不是硬删除的原因。

Deletion is simply a horrible complication to every data management effort. This is true no matter whether you use Breeze or not. It just causes heartache up and down the line. Which is why I recommend soft deletes instead of hard deletes.

但是您不在乎我的想法,所以我会继续。

But you don't care what I think ... so I will continue.

让我直接说一下。 没有简单的方法可以让您正确实施缓存清除方案。我将描述我们怎么做(我敢肯定有些细节被忽略了),您会明白为什么这样做很困难,在不正常的情况下却徒劳无功。

Let me be straight about this. There is no easy way for you to implement a cache cleanup scheme properly. I'm going to describe how we might do it (with some details neglected I'm sure) and you'll see why it is difficult and, in perverse cases, fruitless.

当然,最有效,最强力的方法是在查询之前清空缓存。如果这样做的话,您可能还没有缓存,但我想我会提一下。

Of course the most efficient, brute force approach is to blow away the cache before querying. You might as well not have caching if you do that but I thought I'd mention it.

在继续之前,请记住我刚刚提到的技术,并且如果您的UI(或其他任何东西)持有对您要删除的实体的引用,则所有可能的解决方案都是没有用的。

Before I continue, remember the technique I just mentioned and indeed all possible solutions are useless if your UI (or anything else) is holding references to the entities that you want to remove.

哦,您可以将它们从缓存中删除。但是,无论现在保留对它们的引用,将继续对处于已分离状态的实体对象(幽灵)的引用。确保没有发生是您的责任;微风不知道,也不会做任何事情。

Oh, you'll remove them from cache alright. But whatever is holding references to them now will continue to have a reference to an entity object which is in a "Detached" state - a ghost. Making sure that doesn't happen is your responsibility; Breeze can't know and couldn't do anything about it if it did know.

第二种不太钝的方法(由Jay建议)是

A second, less blunt approach (suggested by Jay) is to


  • 首先将查询应用于缓存

  • 对结果进行迭代,并针对每个

    • 沿展开路径分离每个子实体。

    • 分离该顶部级别实体

    现在,查询成功后,您将有一条清晰的路

    Now when the query succeeds, you have a clear road for it to fill the cache.

    这里是一个简单的代码示例,因为它与TodoLists及其TodoItems的查询有关:

    Here is a simple example of the code as it relates to a query of TodoLists and their TodoItems:

    var query = breeze.EntityQuery.from('TodoLists').expand('TodoItems');
    
    var inCache = manager.executeQueryLocally(query);
    inCache.slice().forEach(function(e) {
        inCache = inCache.concat(e.TodoItems);
    });
    
    inCache.slice().forEach(function(e) {
        manager.detachEntity(e);
    });
    

    此方法至少存在四个问题:

    There are at least four problems with this approach:


    1. 每个查询的实体都是一个幽灵。如果您的UI显示 any 所查询的实体,它将显示幻影。即使在服务器上根本没有触摸实体(99%的时间),也是如此。太糟糕了。您必须重新粉刷整个页面。

    1. Every queried entity is a ghost. If your UI is displaying any of the queried entities, it will be displaying ghosts. This is true even when the entity was not touched on the server at all (99% of the time). Too bad. You have to repaint the entire page.

    您也许可以做到。但是从很多方面来说,这种技术几乎和第一种技术一样不切实际。这意味着在任何地方进行任何查询之后,永远视图都处于潜在的无效状态。

    You may be able to do that. But in many respects this technique is almost as impractical as the first. It means that ever view is in a potentially invalid state after any query takes place anywhere.

    分离实体会产生副作用。依赖于您分离的对象的所有其他实体都会立即(a)更改并且(b)成为孤立对象。如下面的孤立部分所述,无法从中轻松恢复。

    Detaching an entity has side-effects. All other entities that depend on the one you detached are instantly (a) changed and (b) orphaned. There is no easy recovery from this, as explained in the "orphans" section below.

    此技术清除了查询的实体之间所有未决的更改。

    This technique wipes out all pending changes among the entities that you are querying. We'll see how to deal with that shortly.

    如果查询由于某种原因而失败(连接丢失?),则无任何显示。除非您记得删除的内容,否则在查询失败的情况下可以将这些实体放回缓存。

    If the query fails for some reason (lost connection?), you've got nothing to show. Unless you remember what you removed ... in which case you could put those entities back in cache if the query fails.



    <为何提到一种可能具有有限实用价值的技术?因为这是朝着#3迈进的第一步,所以可能会起作用

    Why mention a technique that may have limited practical value? Because it is a step along the way to approach #3 that could work

    我将要描述的方法通常称为标记和扫描。

    The approach I'm about to describe is often referred to as "Mark and Sweep".


    1. 运行查询在本地并按照上述方法计算实体的 inCache 列表。这次,不要从缓存中删除那些实体。如果查询成功,我们将删除查询成功后仍保留在此列表中的实体 ...。

    1. Run the query locally and calculate theinCache list of entities as just described. This time, do not remove those entities from cache. We WILL remove the entities that remain in this list after the query succeeds ... but not just yet.

    MergeOption 是 PreserveChanges(默认情况下),请从 inCache 列表中删除每个实体(而不是从经理的缓存!)具有待处理的更改。我们这样做是因为无论服务器上实体的状态如何,此类实体都必须保留在缓存中。这就是 PreserveChanges的意思。

    If the query's MergeOption is "PreserveChanges" (which it is by default), remove every entity from the inCache list (not from the manager's cache!) that has pending changes. We do this because such entities must stay in cache no matter what the state of the entity on the server. That's what "PreserveChanges" means.


    我们本可以在第二种方法中这样做,以避免删除具有未保存更改的实体。

    We could have done this in our second approach to avoid removing entities with unsaved changes.


  • 订阅 EntityManager.entityChanged 事件。在您的处理程序中,从 inCache 列表中删除已更改的实体,因为该实体由查询返回并合并到缓存中的事实表明它仍然存在服务器。这是一些代码:

  • Subscribe to the EntityManager.entityChanged event. In your handler, remove the "entity that changed" from the inCache list because the fact that this entity was returned by the query and merged into the cache tells you it still exists on the server. Here is some code for that:

    var handlerId = manager.entityChanged.subscribe(trackQueryResults);
    
    function trackQueryResults(changeArgs) {
        var action = changeArgs.entityAction;
        if (action === breeze.EntityAction.AttachOnQuery ||
            action === breeze.EntityAction.MergeOnQuery) {
            var ix = inCache.indexOf(changeArgs.entity);
            if (ix > -1) {
                inCache.splice(ix, 1);
            }
        }
    }
    


  • 如果查询失败,忘记所有这些

  • If the query fails, forget all of this

    如果查询成功


    1. 取消订阅: manager.entityChanged.unsubscribe(handlerId);

    使用孤立检测处理程序进行订阅

    subscribe with orphan detection handler

    var handlerId = manager.entityChanged.subscribe(orphanDetector);
    
    function orphanDetector(changeArgs) {
        var action = changeArgs.entityAction;
        if (action === breeze.EntityAction.PropertyChange) {
           var orphan = changeArgs.entity;
           // do something about this orphan
        }
     }
    


  • 分离保留在 inCache 列表中的每个实体。

    inCache.slice().forEach(function(e) {
        manager.detachEntity(e);
    });
    


  • 取消订阅孤立检测程序

  • unsubscribe the orphan detection handler




  • 孤立检测器?



    分离一个实体可以拥有边效果。假设我们有个产品,每个产品都有一个颜色。其他一些用户讨厌红色。她删除了一些红色产品,并将其余的更改为蓝色。然后,她删除了红色 颜色

    Orphan Detector?

    Detaching an entity can have side-effects. Suppose we have Products and every product has a Color. Some other user hates "red". She deletes some of the red products and changes the rest to "blue". Then she deletes the "red" Color.

    您对此一无所知,并且天真地重新查询颜色。 红色颜色消失了,您的清理过程将其与缓存分离。立即修改缓存中的每个产品。 Breeze不知道新的 Color 应该是什么,因此它将FK Product.colorId 设置为零每个以前的红色产品。

    You know nothing about this and innocently re-query the Colors. The "red" color is gone and your cleanup process detaches it from cache. Instantly every Product in cache is modified. Breeze doesn't know what the new Color should be so it sets the FK, Product.colorId, to zero for every formerly "red" product.

    没有id == 0的颜色,因此所有这些产品都在无效状态(违反参照完整性约束)。他们没有 Color 父级。他们是孤儿。

    There is no Color with id=0 so all of these products are in an invalid state (violating referential integrity constraint). They have no Color parent. They are orphans.

    两个问题:您怎么知道这件事发生在您身上以及您做什么?

    Two questions: how do you know this happened to you and what do your do?

    检测
    当您分离红色颜色时,Breeze会更新受影响的产品。

    Detection Breeze updates the affected products when you detach the "red" color.

    您可以收听在分离过程中引发的 PropertyChanged 事件。这就是我在代码示例中所做的。从理论上(并且我认为是实际上),在分离过程中唯一会触发 PropertyChanged 事件的事件是孤立副作用。

    You could listen for a PropertyChanged event raised during the detach process. That's what I did in my code sample. In theory (and I think "in fact"), the only thing that could trigger the PropertyChanged event during the detach process is the "orphan" side-effect.

    您做什么?


    • 将孤儿留在

    • 恢复为删除的红色颜色同样无效的前 colorId

    • 刷新孤儿以获取其新的颜色状态(或发现它已被删除)?

    没有好答案。您可以使用前两个选项来选择邪恶。我可能会选择第二个,因为它似乎破坏性最小。这将使产品处于不变状态,指向不存在的颜色

    There is no good answer. You have your pick of evils with the first two options. I'd probably go with the second as it seems least disruptive. This would leave the products in "Unchanged" state, pointing to a non-existent Color.


    当您查询最新产品时,情况并不差很多,其中之一是指您没有的新颜色(香蕉)

    刷新选项在技术上似乎是最好的。这很笨拙。它可以轻松地级联成一长串的异步查询,这些查询可能需要很长时间才能完成。

    The "refresh" option seems technically the best. It is unwieldy. It could easily cascade into a long chain of asynchronous queries that could take a long time to finish.

    完美的解决方案逃不过我们的掌握。

    The perfect solution escapes our grasp.

    哦,对了...您的UI可能仍在显示您分离的(更少)实体,因为您相信它们在服务器上被删除。您必须从用户界面中删除这些幽灵。

    Oh right ... your UI could still be displaying the (fewer) entities that you detached because you believe they were deleted on the server. You've got to remove these "ghosts" from the UI.

    我确定您可以弄清楚如何删除它们。但是,您必须先了解它们是什么。

    I'm sure you can figure out how to remove them. But you have to learn what they are first.

    您可以遍历所显示的每个实体,并查看其是否处于分离状态。 UCK!

    You could iterate over every entity that you are displaying and see if it is in a "Detached" state. YUCK!

    更好的是,我认为如果清理机制发布了一个(自定义?)事件,其中包含您在清理过程中分离的实体的列表……而该列表是 inCache 。然后,您的订户知道必须从显示中删除哪些实体...并且可以适当地进行响应。

    Better I think if the cleanup mechanism published a (custom?) event with the list of entities you detached during cleanup ... and that list is inCache. Your subscriber(s) then know which entities have to be removed from the display ... and can respond appropriately.

    哇!我确定我忘记了一些东西。但是现在您已经了解了问题的严重性。

    Whew! I'm sure I've forgotten something. But now you understand the dimensions of the problem.

    这确实有可能。如果您可以安排服务器在删除任何实体时通知客户端,则可以在您的用户界面中共享该信息,并且可以采取措施来删除死角。

    That has real possibilities. If you can arrange for the server to notify the client when any entity has been deleted, that information can be shared across your UI and you can take steps to remove the deadwood.

    这篇关于微风:子实体已被其他人删除后,在重新加载父实体后它们仍然出现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆