4

我有一个在客户端上使用 durandal、knockout.js 和微风的 ASP.NET MVC 应用程序。我有一个反复遇到的问题,但我在任何地方都没有找到任何提及。不确定我是否让自己陷入了独特的境地,或者我只是没有寻找正确的方法。

我需要知道如何从 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 不再拥有该项目。

4

3 回答 3

2

您是否尝试valueHasMutated()在使用选项 b 后调用您的 observable 数组?

这将通知订阅者 observable 已更改。

于 2013-09-11T20:36:21.820 回答
1

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

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

行,看看自己调用'setDeleted'是否能满足你的需要。

于 2013-09-12T00:53:38.743 回答
0

TL:博士

切勿在 createEntity 的初始化程序中设置 FK,同时将实体推送到父级的导航属性。保留其中任何一个,但不能同时保留两者。

基本原则

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

Breeze 在管理其数据上下文方面非常有能力。它知道导航属性和外键以及如何处理它们。作为本地数据上下文的副作用,所有可观察对象也拥有这种智能。这就是你可能忽略了一些东西并最终遇到了这个问题的地方。

追踪微风的代码

这一切具体意味着什么?好吧,有两种方法可以用来使用微风创建具有父实体的对象。第一个是通过在初始化程序中设置其父 ID,如下所示:

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

另一种是通过在微风数据上下文中将实体添加到父实体的导航属性。

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 是可以选择的。

于 2014-09-19T18:37:27.920 回答