微风:保存时的多对多问题

时间:2013-12-17 15:59:28

标签: breeze

我在微风应用程序中与多对多关联一直在苦苦挣扎。我在客户端和服务器端都有问题但是现在,我只是暴露了我的客户端问题。我不知道我提出的方法是否正确,我真的希望得到微风团队的反馈意见:

我的商业模式:

public class Request 
{
    public virtual IList<RequestContact> RequestContacts { get; set; }
}

public class RequestContact 
{
    public virtual Contact Contact { get; set; }
    public virtual Guid ContactId { get; set; }
    public virtual Request Request { get; set; }
    public virtual Guid RequestId { get; set; }
}


public class Contact 
{
    public virtual Client Client { get; set; }
    public virtual Guid? ClientId { get; set; }
    public virtual string Username { get; set; }
}

在我的getRequest查询的成功回调中,我向Request添加了一个contacts属性,并填充它:

request.contacts = [];
request.requestContacts.forEach(function (reqContact) {
    request.contacts.push(reqContact.contact);
});

视图绑定到控制器中定义的contacts数组:

<select ng-multiple="true" multiple class="multiselect" data-placeholder="Select Contacts" ng-change="updateBreezeContacts()" ng-model="request.contacts" ng-options="c as c.username for c in contacts | filter:{clientId: request.client.id} track by c.id"></select>

控制器:

//the collection to which the multiselect is bound: 

$scope.contacts = dataService.lookups.contacts;

只要在多选中选择或取消选择某个项目,就会调用此方法:

$scope.updateBreezeContacts = function () {
  //wipe out all the RequestContact entities
  $scope.request.requestContacts.forEach(function (req) {
    req.entityAspect.setDeleted();
  });

  //populate the RequestContact based on selected contacts 
  for (var i = 0; i < $scope.request.contacts.length; i++) {
     var requestContact = dataService.createRequestContact($scope.request,    $scope.request.contacts[i]);
     $scope.request.requestContacts.push(requestContact);
  }

其中dataService的createRequestContact方法实际上是这样做的:

manager.createEntity('RequestContact', { request: myRequest, contact: myContact});

用例场景:


  • 请求中有一个选定的联系人。
  • 用户取消选择联系人,然后从列表中选择另一个联系人。
  • 然后她决定重新选择之前未被选中的那个。我们现在有两个选定的联系人。
  • 用户点击保存按钮,然后调用saveChanges。 Breeze将3个实体发送到服务器:第一个联系人处于“已删除”状态,同一联系人再次处于“已添加”状态,最后是另一个已选中的联系人,也处于“已添加”状态。

在处理多对多关联时,这应该是什么?

我实际上得到一个服务器错误(“not-null属性引用null或瞬态值Business.Entities.RequestContact.Request”)但在得出任何结论之前,我想知道我在客户端做了什么 - 这是正确的。

2 个答案:

答案 0 :(得分:8)

服务器侧

首先要处理服务器端建模问题。我在你对你的问题的评论中注意到没有PK。我建议你先解决问题,然后再打扰客户。

客户端

我对这种情况有很长的经验。对我来说,规范案例是一个用户,他可以拥有任意数量的角色,而他/她拥有的角色位于UserRoles表中。

典型的用户界面:

  • 选择并展示用户
  • 使用前面的复选框
  • 显示该用户的所有可能角色的列表
  • 如果用户具有该角色,则选中该复选框;如果他/她不
  • ,则取消选中

呃哦

我很多时候看到人们将所有可能的角色列表绑定到UserRole 实体列表中。这很少奏效。

很多时候,当用户点击复选框时,我看到有人创建并销毁UserRole 实体。这很少奏效。

我经常看到添加和删除UserRole 实体,并添加和删除到缓存中。这通常是致命的,因为客户端忘记了UserRole实体此时是否与数据库中的记录相对应。

如果我正确地阅读了你的代码,你就会让每个人都犯错误。

项目ViewModel

当我将此用户的角色表示为&#34; Item ViewModel&#34;的列表时,我取得了更大的成功。实例并推迟实体操作,直到保存用户的选择为止。

对于我们的讨论,让我们将此对象称为 UserRoleVm 。它可能在JavaScript

中定义如下
{
    role,
    isSelected,
    userRole
}

构建屏幕时,

  • 填充UserRoleVm个实例列表,每Role

  • 一个
  • 使用相应的role实体

  • 设置每个vm的Role属性
  • 将视图绑定到vm.role.name

  • 设置每个vm的userRole属性与相关用户的UserRole实体当且仅当此类实体已存在 < / p>

  • 如果vm有isSelected=true并且userRole未被删除,请设置vm&#39; vm.userRole.entityAspect.entityState

  • 将vm&#39; isSelected绑定到复选框

现在用户可以随意检查和取消选中。

在此过程中,我不会创建/删除/修改任何UserRole实体。我等待UI信号保存(无论发生什么信号)。

在保存准备期间,我遍历UserRoleVm个实例

列表
  • 如果未选中且没有vm.userRole,则不执行任何操作

  • 如果未选中,则vm.userRole,然后vm.userRole.entityAspect.setDeleted()。如果vm.userRole.entityAspect.entityState已分离(意味着它之前处于已添加状态),请设置vm.userRole = null。

  • 如果选中并且没有vm.userRole,请创建新的UserRole并将其分配给vm.userRole

  • 如果选中并vm.userRole,则vm.userRole.entityAspect.entityState

    • 不变,什么都不做
    • 修改(为什么?怎么办?),通过调用vm.userRole.entityAspect.rejectChanges()
    • 来恢复
    • 已删除(必须是预先存在的UserRole,其未经检查&#34;但仍未保存;这是怎么发生的?),请致电vm.userRole.entityAspect.rejectChanges()

现在致电manager.saveChanges()

  • 如果保存成功,一切都很顺利。

  • 如果失败,最干净的方法是致电manager.rejectChanges()。这将清除甲板(并丢弃用户自上次保存后所做的任何更改)。

  • 无论哪种方式,都像我们在开始时那样从头开始重建列表。

理想情况下,在异步保存成功返回之前,您不允许用户对用户角色进行更多更改。

我相信你可以比这更聪明。但这种方法很有效。

<强>变异 不要为UserRoleVm.userRole而烦恼。不要在UserRole中携带现有的UserRoleVm实体。相反,在初始化UserRole属性时,请参考用户的缓存UserRoleVm.isSelected实体。然后在保存准备期间评估列表,根据相同的逻辑查找和调整缓存的UserRole实例。

启用“保存”按钮(12月19日更新)

萨姆问道:

  

当EntityManager发生更改时,“保存”按钮的已禁用属性绑定到设置为true的属性。但是,由于我的ViewModel不是EntityManager的一部分,因此当用户添加/删除联系人时,这不会更改附加到EntityManager的模型。因此,永远不会启用“保存”按钮(除非我更改了模型的其他属性)。你能想到一个解决方法吗?

是的,我可以想到几个。

  1. 使用get和set方法将isSelected属性定义为ES5属性;在set方法中,您向外部 VM发信号通知UserRoleVm实例已更改。这是可能的,因为如果你让Angular和Breeze一起工作,你必须使用ES5浏览器。

  2. 将ngClick(或ngChanged)添加到与外部 vm中的函数绑定的复选框html中,例如

    <li ng-repeat="role in vm.userRoles">
        ...
        <input type="checkbox" 
               ng-model="role.isSelected" 
               ng-click="vm.userRoleClicked(role)"</input>
     ...
    </li>
    
  3. 利用有角度的本机支持&#34;视图已更改&#34;检测(&#34; isPristine&#34;我认为)。我通常不会这样,所以我不知道细节。只要您不允许用户离开此屏幕并返回预期未保存的UserRoleVm列表更改已被保留,它就可行。

  4. vm.userRoleClicked可以将vm.hasChanges属性设置为true。将保存按钮绑定到vm.hasChanges的isEnabled。现在,当用户单击复选框时,保存按钮会亮起。

    如前所述,保存按钮点击操作会迭代userRoleVm列表,创建和删除UserRole 实体。当然,EntityManager会检测到这些操作。

    你可以变得更加漂亮。您的UserRoleVm类型可以在创建时记录其原始选定状态(userRoleVm.isSelectedOriginal),并且您的vm.userRoleClicked方法可以评估整个列表,以查看当前所选状态是否与其原始选定状态不同。 。并相应地设置vm.hasChanges。这一切都取决于您的用户体验需求。

      

    重建列表时,不要忘记清除vm.hasChanges

    我认为我更喜欢#2;这对我来说似乎最容易和最清楚。

    2014年2月3日更新:plunker中的一个例子

    我写过a plunker to demonstrate the many-to-many checkbox technique我在这里描述过。 readme.md 解释了所有。

答案 1 :(得分:1)

Breeze.js客户端不支持&#34;多对多&#34;这个时候的关系。您必须将联结/映射表公开为实体。有关此同一主题的其他几篇文章可供使用。

我们计划在未来增加许多支持。对不起,但还没有约会......