在调用importEntities时未维护某些导航属性?

时间:2018-01-23 11:19:02

标签: javascript breeze

Breeze的EntityManager(javascript)有一对我们用来实现撤消功能的方法。当对话框打开时,我们使用exportEntities拍摄EntityManager的快照。如果用户取消对话框,我们会调用importEntities并将调用的输出传递给exportEntities。我们还传入MergeStrategy OverwriteChanges以确保快照中的版本“撤消”目标中的任何更改。

由于Added处理具有存储生成密钥的新(importEntities)实体的方式,此处需要一些额外的逻辑。

  

...可以为新实体分配新的临时键值。这个可以   如果在关闭并重新启动后导入,则会发生   应用。您不应该假设导入的实体将具有   与导出时相同的临时密钥。一个关键的价值   可能需要进行更改以防止导入的临时密钥   新实体与实体的临时键值冲突   已经在目标经理中。

请参阅Export and import entities页面上标有“导入新实体”的部分。

注意:应用程序似乎无需关闭并重新启动应用程序以应用此行为(与上述文档相反)。

不仅分配了新密钥,而且还将这些实体作为新实例导入。现有实例在EntityManager中保持不变。我们在importEntities完成后手动删除这些(以及在拍摄快照后添加的任何其他内容)。

我们考虑在调用importEntities之前删除所有实体,这样我们就不必在之后有选择地删除它们,但意识到这意味着我们在修复对原始引用的方面会有更多的工作实体实例。实际上,从EntityManager外部到我们删除的实体的引用需要手动修复。

乍一看,好像保持了导航属性(EntityManager中的实体之间的引用)。即引用在importEntities期间替换的实体(因为它具有临时密钥)的导航属性将更新为指向新实体。因此,尽管EntityManager之外的任何引用都需要修复,但其中的内容却没有。

我们后来发现了一种不维护导航属性的情况。考虑这种情况。

entityA - EntityState.Unchanged

entityB - EntityState.Added

entityA.navigationPropery = entity

entityA - EntityState.Modified

entityB - EntityState.Added

importEntities完成后,entityB已替换为与原始实例具有不同临时密钥的新实例。我希望entityA.navigationProperty能够引用这个新实体,但它会继续保留对原始实体的引用。在我们的设置中,原始实体在EntityManager之后从importEntities手动分离,导航属性最终为空引用,并且临时ID与{{1}中的任何实体都不匹配}。最终,这会导致EntityManager期间数据库中出现外键冲突。

请注意,我们看到导航属性正确维护,其中entityA具有状态saveChanges

我在DocCode测试中复制了这个问题,但没有成功(测试通过)。我猜测C#和JavaScript的实现有什么不同?是否有一些针对JavaScript的类似资源?还是其他一些推荐的再现问题的方法?

EntityState.added

我们如何确保维护所有导航属性?

我们可以采取其他方法来实现撤销,这不会产生这个问题(也许不需要我们必须手动修复对实体的外部引用)?

注意:我们正在使用[TestMethod] public async Task ImportChangedEntityAndMaintainNavigationPropertyReferencesToIt() { var manager = new EntityManager(_serviceName); await manager.FetchMetadata(); // Metadata must be fetched before CreateEntity() can be called var order = manager.CreateEntity<Order>(new {OrderID = 1}, EntityState.Unchanged); //var order = manager.CreateEntity<Order>(EntityState.Unchanged); var employee = manager.CreateEntity<Employee>(); order.Employee = employee; var exportData = manager.ExportEntities(); var imported = manager.ImportEntities(exportData, new ImportOptions(MergeStrategy.OverwriteChanges)); var importedOrder = imported.ImportedEntities.OfType<Order>().Single(); Assert.AreSame(order, importedOrder); var managerOrder = manager.GetEntities<Order>().Single(); Assert.AreSame(order, managerOrder); Assert.AreEqual(importedOrder.Employee.EntityAspect.EntityState, EntityState.Added); Assert.AreNotSame(importedOrder.Employee, employee); //Entities with temporary keys are replaced with new instances. Assert.IsFalse(importedOrder.Employee.EntityAspect.EntityKey.Equals(employee.EntityAspect.EntityKey)); //Entities with temporary keys are assigned new ones. } 版本1.3,这是目前npm上的最新版本。

1 个答案:

答案 0 :(得分:0)

As far as Breeze knows, an import bundle could have been created at any time in the past, and for any reason.

So Breeze defensively assumes that any entities in the import bundle that are in the Added state are not really the same as any similar entities that are already in the cache. If they have the same temporary keys as entities in the cache, then the imported entities will get new temporary keys so that they will not conflict with any existing temporary keys.

As you've observed, Breeze does merge other entities (those in the Deleted, Modified, or Unchanged states). So a possible workaround for your problem is to:

  1. Before exporting, change all entities in the Added state to Modified
  2. Export
  3. Perform any edits on the entities
  4. If "undo" is required, import
  5. Repeat 1-4 as necessary
  6. Before calling SaveChanges, process each entity with a negative key value:
    • If the entity is now Modified, change to Added
    • If the entity is now Deleted, change to Detached

The downside of this is if anything in your UI treats Added entities differently from Modified ones, it won't work. You would need to put a separate flag on the entity to tell the UI that it's really added.

I will look into the possibility of adding a flag on importEntities that will allow for merging of Added entities that have the same temporary key.

UPDATE: The mergeAdds flag has been added in breeze 1.7.0. So now you can do this:

entityManager.importEntities(exportData, {   
    mergeAdds: true,
    mergeStrategy: breeze.MergeStrategy.OverwriteChanges
});

to merge added entities that have the same key.