我们有一个轻而易举的客户解决方案,其中向父实体显示其子列表。我们对某些子实体进行硬删除。现在,当用户是执行删除操作的用户时,没有问题,但是当其他用户执行操作时,似乎没有办法使已经加载到缓存中的子项失效。我们对父级执行一个新查询并扩展到子级,但是即使数据库未返回它们,微风也会附加已经听到的所有其他子级。
我的问题:难道不应该轻而易举地意识到我们正在通过expand进行加载,从而在从db重新加载结果之前从缓存中完全删除所有子级吗?如果不是这种情况,我们还能怎么做呢?
谢谢
是的,这确实是一个好点。
删除只是每项数据管理工作的一个可怕的麻烦。无论是否使用Breeze,这都是事实。它只会引起上下上下的心痛。这就是为什么我建议软删除而不是硬删除的原因。
但是您不在乎我的想法...所以我会继续。
让我直接说一下。没有简单的方法可以正确地实现缓存清除方案。我将描述我们将如何做到(我敢肯定有些细节被忽略了),您会明白为什么这样做很困难,在不正常的情况下却毫无结果。
当然,最有效,最强力的方法是在查询之前清空缓存。如果这样做的话,您可能也没有缓存,但是我想我会提一下。
在继续之前,请记住我刚刚提到的技术,并且如果您的UI(或其他任何东西)持有对您要删除的实体的引用,那么实际上所有可能的解决方案都是无用的。
哦,您会从缓存中删除它们的。但是,无论现在持有对它们的引用,将继续对处于“已分离”状态的实体对象(即虚影)进行引用。确保没有发生是您的责任;如果Breeze知道,它就不会知道,也不会做任何事情。
第二种不太平淡的方法(由杰伊建议)是
现在,查询成功后,您便拥有了清除缓存的明确道路。
这是一个简单的代码示例,因为它与TodoLists及其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);
});
这种方法至少存在四个问题:
每个被查询的实体都是一个幽灵。如果您的UI正在显示任何查询的实体,它将显示幻影。即使在服务器上根本没有触摸实体(99%的时间),也是如此。太糟糕了。您必须重新粉刷整个页面。
您也许可以做到这一点。但是从很多方面来说,这种技术几乎和第一种技术一样不切实际。这意味着在任何地方进行任何查询后,ever view都可能处于无效状态。
分离实体有副作用。依赖于您分离的对象的所有其他实体都会立即(a)更改并且(b)成为孤立对象。如下面的“孤立”部分所述,无法从中轻松恢复。
此技术清除了您正在查询的实体之间的所有未决更改。我们很快就会看到如何处理。
如果查询由于某种原因失败(连接丢失?),则无任何显示。除非您记得删除的内容,否则在查询失败的情况下可以将这些实体放回缓存中。
为什么要提及一种可能具有有限实用价值的技术?因为这是方法3可行的一步
我将要描述的方法通常称为“标记和扫描”。
在本地运行查询并按照上述说明计算inCache
实体列表。这个时候,就不会从缓存中删除这些实体。查询成功后,我们将删除保留在该列表中的实体……但不是现在。
如果查询的MergeOption
是“ PreserveChanges”(默认情况下为“ PreserveChanges”),请从inCache
列表(而不是从管理器的缓存中)删除列表中所有有待更改的实体。我们这样做是因为无论服务器上实体的状态如何,此类实体都必须保留在缓存中。这就是“ PreserveChanges”的意思。
我们可以在第二种方法中做到这一点,以避免删除具有未保存更改的实体。
订阅EntityManager.entityChanged
活动。在您的处理程序中,从inCache
列表中删除“已更改的实体”,因为该实体由查询返回并合并到缓存中这一事实表明您该实体仍然存在于服务器上。这是一些代码:
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);
}
}
}
如果查询失败,请忘记所有这些
如果查询成功
退订: manager.entityChanged.unsubscribe(handlerId);
订阅孤儿检测处理程序
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);
});
取消订阅孤立检测程序
分离实体可能会产生副作用。假设我们有Products
,每个产品都有一个Color
。其他一些用户讨厌“红色”。她删除了一些红色产品,并将其余的更改为“蓝色”。然后,她删除了“红色” Color
。
您对此一无所知,并且天真地重新查询Colors
。“红色”颜色消失了,您的清理过程将其与缓存分离。立即Product
修改每个缓存。Breeze不知道新产品Color
应该是什么,因此它将Product.colorId
每个以前的“红色”产品的FK设置为零。
没有Color
id = 0,因此所有这些产品都处于无效状态(违反了参照完整性约束)。他们没有Color
父母。他们是孤儿。
两个问题:您怎么知道这件事发生在您身上,您怎么办?
当您分离“红色”颜色时,检测微风会更新受影响的产品。
您可以听取PropertyChanged
在分离过程中引发的事件。这就是我在代码示例中所做的。从理论上(我认为是“实际上”),PropertyChanged
在分离过程中唯一可以触发事件的是“孤立”副作用。
你是做什么?
colorId
为删除的“红色”颜色同样无效的前者?没有好的答案。您可以使用前两个选项来选择邪恶。我可能会选择第二个,因为它似乎破坏性最小。这将使产品处于“不变”状态,表示不存在Color
。
当您查询最新产品时,情况并不差很多,其中一个产品是指
Color
您没有缓存的新产品(“香蕉”)。
从技术上讲,“刷新”选项似乎是最好的。这很笨拙。它可以轻松地级联成一长串异步查询,这可能需要很长时间才能完成。
完美的解决方案使我们无法掌握。
哦,对了。您的UI可能仍会显示您分离的(较少)实体,因为您认为它们已在服务器上删除。您必须从用户界面中删除这些“鬼”。
我确定您可以弄清楚如何删除它们。但是您必须首先了解它们。
您可以遍历所显示的每个实体,并查看其是否处于“已分离”状态。UCK!
更好的是,我认为如果清理机制发布了一个(custom?)事件,其中包含您在清理过程中分离的实体的列表,而该列表为inCache
。然后,您的订户知道哪些实体必须从显示中删除...并可以做出适当的响应。
ew!我确定我已经忘记了一些东西。但是现在您了解了问题的严重性。
那确实有可能。如果您可以安排服务器在删除任何实体时通知客户端,则可以在您的UI中共享该信息,并且可以采取措施来删除死角。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句