0

我有一个 Web 服务,它返回一些数据,然后将其转换为服务器上的新实体,然后传递给客户端,用户可以在其中编辑它们。如果他选择SaveChanges,实体应该被提交到服务器并插入到数据库中。我有两个问题:

  • 当实体返回给客户端时,Breeze 将它们标记为EntityState.New
  • Breeze 预计,未更改的实体具有主键集。因为返回的新实体都没有键集(Int32 类型的键值等于 0),Breeze 认为服务器返回了同一实体的多个实例

为了演示该问题,更改ToDosController.ToDosAngularJS 示例中的方法以匹配以下内容:

[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客户端上的一些或服务器上的一些额外数据/属性来实现这一点,但找不到。

更新:

澄清:

当用户选择并编辑源自其他来源的实体之一时,我尝试支持这样一种场景,该实体稍后应添加到我的数据库中。

详细说明:

  1. 客户端使用搜索条件作为方法参数调用服务器。服务器方法返回IEnumerable<Customer>- 它返回IQueriable<Customer>

  2. 服务器查询后端 Web 服务(CRM 系统)并将后端 Web 服务的结果转换为Customer实体。结果返回给客户端。后端 Web 服务对客户端不可用。

  3. 客户端向用户显示结果

  4. 用户选择实体之一并编辑其属性(例如更改客户名称或地址)

  5. 选定的客户被添加到客户端的实体管理器中。它应该在EntityState.Added

  6. em.SaveChanges被调用,它将Added实体提交给服务器

  7. 服务器将新客户插入数据库,数据库生成新的主键,返回给客户端,其中 EM 更新实体键并将实体状态设置为EntityState.Unchanged

也许我需要分离的实体,正确的问题是:“如何在不将实体添加到实体管理器的情况下将实体返回给客户端?” (它们将在上面的步骤 5 中添加)。

PS:一种解决方案是使用自定义的非实体数据类型(例如 CustomerFromCRM)作为我的服务器方法的结果。然后我会将它们转换为客户端的实体 Customer。但我想避免创建额外的类。

UPDATE2:我在这里发现了一个类似的问题(没有接受的答案):是否有一种简单的方法可以将缓存中的实体标记为“已添加”?

4

3 回答 3

1

我想我明白了。外部服务为潜在的 新实体提供数据。显而易见的事情是将该数据作为 Customer 以外的某种类型发送,可能是匿名类型;有趣的是,您在制作客户对象的服务器上遇到了麻烦;何必?

无论如何,您获取这些非客户数据并以您建议的方式在客户端上创建新客户。

如果您决定在服务器上使用 Customer 类型,您可以编写一个自定义 JasonResultsAdapter,告诉轻风在查询时不要缓存数据(清除每个节点上的 $type)。仅将此适配器用于此查询!

但首先我会重新考虑为什么您首先将他们作为客户发送,因为他们确实不是。

于 2013-08-30T03:33:44.863 回答
1

没有 EntityState.New 这样的东西。当您在客户端上创建新实体并将其添加到 EM 时,EntityState 将设置为已添加。调用 em.SaveChanges 后,新实体将保存在数据库中,其 EntityState 将更新为未更改。如果查询数据,服务器将返回带有 EntityState.Unchanged 的​​实体。如果您对这些实体中的任何一个进行更改,EntityState 将设置为 Modified(如果已更新)或 Deleted(如果您调用 EntityAspect.setDeleted)。

在您的代码段中,您只是返回 2 个对象。它们尚未保存到数据库中,因此尚未设置任何键值。

您的问题尚不清楚您到底要做什么...

  • 您是否尝试使用一些初始数据?如果是这样,您应该改为播种您的数据库。
  • 您只是想创建新实体吗?如果是这样,你为什么要在服务器上这样做?您应该在客户端上执行此操作。

IE

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();
于 2013-08-29T18:28:36.537 回答
0

回答我自己的问题:

EntityManager我能够通过以某种方式修改微风源代码来完成这项工作,如果设置了指定的标志,它不会将实体附加到。我可以定义 custom MergeStreategy,但是因为我不想在微风代码中弄乱枚举,所以我在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并且它们正确地插入到数据库中作为新记录。

这也解决了轻率将两个不同的项目(都具有未初始化的密钥)视为同一个项目的问题,因为它期望它们具有现有的唯一密钥。

于 2013-09-03T15:29:39.947 回答