我有一个返回一些数据的Web服务,然后将其转换为服务器上的新实体,然后传递给客户端,用户可以在其中编辑它们。如果他选择SaveChanges
,则应将实体提交给服务器并插入数据库。我有两个问题:
EntityState.New
要演示此问题,请更改AngularJS示例中的ToDosController.ToDos
方法以匹配以下内容:
[HttpGet]
public IEnumerable<TodoItem> Todos()
{
return new TodoItem[]
{
// Keys are not set because (equals to 0) these are new entities
new TodoItem() { Description="First item"},
new TodoItem() { Description="Second item"},
};
}
运行示例时,HTML页面将显示两行,两行都有描述“第二项”。如果我在服务器上显式设置了这些项的ID(我不想这样做,因为密钥是由数据库生成的),问题就不会显现出来了。
问题:如何正确地从服务器返回实体,以便将它们标记为EntityState.New
,并在调用SaveChanges
时将它们保存到数据库中(使用生成的密钥)。
我希望客户端上有一些MergeStrategy
或服务器上有一些额外的数据/属性来实现这一点,但无法找到。
更新
澄清:
我试图支持用户选择和编辑其中一个来自其他来源的实体的情况,这些实体应该稍后添加到我的数据库中。
详情:
客户端使用搜索条件作为方法参数调用服务器。服务器方法返回IEnumerable<Customer>
- 不返回IQueriable<Customer>
服务器查询后端Web服务(CRM系统)并将后端Web服务的结果转换为Customer
个实体。结果将返回给客户端。客户端无法使用后端Web服务。
客户端将结果显示给用户
用户选择其中一个实体并编辑其属性(例如更改客户名称或地址)
选定的客户将添加到客户端的实体管理器中。它应该在EntityState.Added
em.SaveChanges
,将Add
ed实体提交给服务器
服务器将新客户插入数据库,数据库生成新的主键,返回给客户端,EM更新实体密钥并将实体状态设置为EntityState.Unchanged
也许我需要分离的实体,正确的问题是:“如何在不将实体添加到实体管理器的情况下将实体返回给客户端?”(它们将在上面的步骤5中添加)。
P.S: 一种解决方案是使用自定义的非实体数据类型(例如CustomerFromCRM)作为我的服务器方法的结果。然后我会将它们转换为客户端的实体客户。但我想避免创建其他类。
UPDATE2:我在这里找到了一个类似的问题(没有接受的答复):is there an easy way to mark an entity in the cache as "added"?
答案 0 :(得分:1)
没有EntityState.New这样的东西。 当您在客户端上创建新实体并将其添加到EM时,EntityState将下注设置为已添加。一旦调用em.SaveChanges,新实体将保存在DB中,其EntityState将更新为Unchanged。 如果查询数据,服务器将返回EntityState.Unchanged的实体。如果您对这些实体中的任何一个进行更改,EntityState将设置为Modified(如果已更新)或Deleted(如果您调用EntityAspect.setDeleted)。
在您的代码段中,您只是返回2个对象。它们尚未保存到数据库中,因此尚未设置任何键值。
目前还不清楚你的问题到底是做什么的......
即
var todoItem1 = em.createEntity("TodoItem", { description: "First Item" });
var todoItem2 = em.createEntity("TodoItem", { description: "Second Item" });
修改强>
我的情况仍然不清楚,但这是一个达到目标的解决方案:
1-确保手动向您的实体添加临时ID(临时ID必须是唯一的):
[HttpGet]
public IEnumerable<TodoItem> Todos() {
return new TodoItem[] {
new TodoItem() { Id=-1001, Description="First item"},
new TodoItem() { Id=-1002, Description="Second item"}
};
}
2-您会注意到这些实体在客户端上将具有EntityState.Unchanged,因此您将它们分离:
var todoItem1 = data.results[0];
var todoItem2 = data.results[1];
todoItem1.entityAspect.setDetached();
todoItem2.entityAspect.setDetached();
3-现在您可以根据需要操作它们,如果您决定保存它们,则在进行saveChanges调用之前将它们添加到管理器中:
manager.addEntity(todoItem1);
manager.addEntity(todoItem2);
manager.saveChanges();
答案 1 :(得分:1)
我想我明白了。外部服务为潜在新实体提供数据。显而易见的是将该数据作为除Customer以外的某种类型发送,也许是匿名类型;有趣的是你在制作Customer对象的服务器上遇到了麻烦;为什么要这么麻烦?
无论如何,您采用这些非客户数据并以您建议的方式在客户端上创建新客户。
如果您的心脏在服务器上使用Customer类型,则可以编写一个自定义JasonResultsAdapter,告诉breeze在查询时不要缓存数据(清除每个节点上的$ type)。仅将此适配器用于此查询!
但首先我会重新考虑为什么你首先将它们作为客户发送,因为它们确实不是。
答案 2 :(得分:0)
回答我自己的问题:
我能够通过某种方式修改breeze源代码来实现这一点,如果设置了指定的标志,它不会将实体附加到EntityManager
。我可以定义自定义MergeStreategy
,但由于我不想在breeze代码中弄乱枚举,我已经在从FetchAsDetached
的自定义实现返回的对象上定义了自定义归档JsonResultAdapter.visitNode
方法
我必须通过以下方式更改mergeEntity
功能:
更改第13334行:
if (targetEntity) {
到
if (targetEntity && ! (meta.FetchAsDetached == true)) {
在第13380行附近添加附加条件,以便在必要时跳过附加实体:
if (!(meta.FetchAsDetached == true)) { // attach only if necessarry
attachEntityCore(em, targetEntity, EntityState.Unchanged);
targetEntity.entityAspect.wasLoaded = true;
em.entityChanged.publish({ entityAction: EntityAction.AttachOnQuery, entity: targetEntity });
}
这使我能够从处于分离状态的服务器获取潜在的新实体。后来,我能够将它们添加到EntityManager
,并且它们正确地将数据库作为新记录插入到数据库中。
这也解决了微风处理两个不同的项目(都使用非初始化的密钥)作为同一个项目的问题,因为它期望'他们有现有的唯一密钥。