从observableArray中删除元素

时间:2013-09-10 23:42:42

标签: c# javascript knockout.js entity-framework-5 breeze

我有一个ASP.NET MVC应用程序,它在客户端上使用durandal,knockout.js和breeze。我有一个我一再遇到的问题,但我还没有在任何地方找到它。不确定我是否已陷入独特的境地,或者我是不是以正确的方式搜寻。

我需要知道如何从observableArray中删除Breeze实体,以便提交成功(参见下面的选项A),UI反映了更改(参见下面的选项B)。

我有以下型号(缩写):

public class Donor
{
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public virtual IList<Contact> Contacts { get; set; }
}

public class Contact
{
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [ForeignKey("Donor")]
    public int? DonorId { get; set; }
    public virutal Donor Donor { get; set; }
}

我正在尝试从我的捐赠者中删除联系人。我很难在Breeze和Knockout之间获得流量,因此该项目都从observableArray中删除(带有通知),也可以通过Breeze删除。

这是我尝试过的(javascript):

选项A

function deleteContact(contact){
    viewModel.donor().contacts.remove(contact);
    contact.entityAspect.setDeleted();
    viewModel.uow.commit();
}

当我使用这种方法时,我从Breeze.WebApi中得到以下错误:

  

Int32Converter无法从System.Int64转换

我查看了堆栈,并检查了Breeze源代码(虽然我还没有配置解决方案来逐步执行它),错误来自Breeze.WebApi.EFContextProvider :: RestoreOriginal,它是将原始属性值还原到对象。我不知道为什么它认为我的价值是Int64,但我无法找到一个好的解决办法,所以我试过......

选项B

function deleteContact(contact){
    contact.entityAspect.setDeleted();
    viewModel.uow.commit();
}

这种方法允许我成功保存删除(因为该项目尚未手动从集合中删除,因此没有任何“原始值”)。但是,这里的问题是setDeleted有效地从observableArray 中删除了该项,而没有通知我的敲除绑定数组已更改。因此该项目已被删除和删除,但我的UI仍显示该项目。将来尝试调用donor().contacts.remove(contact)是徒劳的,因为observableArray不再具有该项目。

3 个答案:

答案 0 :(得分:2)

使用选项b?

后,您是否尝试在可观察数组上调用valueHasMutated()

这将通知订阅者观察者已经发生变化。

答案 1 :(得分:1)

如果您'删除'或'分离'实体,Breeze应自动从任何导航集合中删除该实体。所以我会尝试删除

 viewModel.donor().contacts.remove(contact);  

行并查看调用'setDeleted'本身是否能满足您的需求。

答案 2 :(得分:0)

TL:DR

永远不要在createEntity的初始值设定项中设置FK,同时将实体推送到父级的导航属性。保留其中任何一个,但不是两个。

基本原则

在遇到类似问题并进行了大量调查之后,我想提出一个替代答案。您正在尝试的问题不是关于如何删除项目,而是创建项目的方式。

在管理数据上下文时,Breeze非常有能力。它知道导航属性和外键以及如何处理它们。作为本地数据上下文的副作用,所有可观测量也都具有这种智能。这是您可能忽视某些内容并最终解决此问题的地方。

追踪微风的代码

具体是什么意思?好吧,有两种方法可以用于使用breeze创建具有父实体的对象。第一个是在初始化程序中设置其父ID,如下所示:

var c = manager.createEntity('contact', { 'donorId': 12, 'name': 'bob' });

另一种方法是将实体添加到breeze数据上下文中父实体的导航属性中。

var parents = ko.observableArray();
manager.runQuery(..., parents);

var c = manager.createEntity('contact', { 'name': 'bob'});
parents.contacts.push(c);

这两种情况都有其优点和缺点。当您尝试同时执行这两个操作时会出现问题:

var parents = ko.observableArray();
manager.runQuery(..., parents);

var c = manager.createEntity('contact', { 'donorId': 12, 'name': 'bob' });
parents.contacts.push(c);

Breeze在处理插入时尝试优化其查询以防止UI闪烁。调用push时,它会禁用目标observable上的通知,直到整个操作完成。然后,它将在内部调用valueHasMutated,这将触发UI刷新。问题是,调用createEntity会干扰此机制并导致通知过早重新初始化。然后push将保存此无效状态,交换它并重置它,使observable处于无效状态。

当您最终调用setDeleted时,即使数据在微风的数据上下文中被正确推送,仍会在observable上禁用通知,从而阻止UI刷新。这只会在插入新元素后发生一次。删除元素将强制将状态更改为其正确值,导航属性上的所有后续删除都将触发UI刷新。

查看您的两个选项

最后,您只需使用setDeleted从轻微导航属性中正确删除实体。无需手动将其从observable中删除,实际上,这样做会将外键重置为null,如果模型中的类型不可为空,或者在尝试时出错,则可能导致服务器上出现反序列化问题根据主键的定义方式从数据库中删除行。选项B是可以使用的选项。