我一直在微风应用程序中与多对多关联进行斗争一段时间。我在客户端和服务器端都有问题,但是现在,我只介绍客户端问题。我不知道我想出的方法是否正确,我真的很想从微风团队那里得到有关以下方面的反馈:我的商业模式: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查询的成功回调中,向请求添加一个contacts属性,并填充它:request.contacts = [];request.requestContacts.forEach(function (reqContact) { request.contacts.push(reqContact.contact);});该视图绑定到在控制器中定义的联系人数组:<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个实体发送到服务器:第一个具有“已删除”状态的联系人,同一联系人再次具有“已添加”状态,最后是另一个已选择的联系人,也具有“已添加”状态。与多对多协会合作时该怎么办?我实际上收到服务器错误(“非null属性引用了null或瞬态值Business.Entities.RequestContact.Request”),但是在得出任何结论之前,我想知道我在客户端所做的是正确。 最佳答案 服务器端您首先要解决服务器端建模问题。我在对您的问题的评论中指出没有PK。我建议您先解决该问题,然后再与客户打扰。客户端我对这种情况有很长的经验。对我而言,典型案例是可以拥有任意多个角色的用户,并且他/他拥有的角色在UserRoles表中。典型的用户界面:选择并展示用户通过前面的复选框显示该用户的所有可能角色的列表如果用户具有该角色,则选中该复选框;未经检查,如果他/她没有喔喔很多次,我看到人们将所有可能角色的列表绑定到UserRole实体的列表。这很少起作用。我经常看到人们在用户单击复选框时创建和销毁UserRole实体。这很少起作用。我经常看到UserRole实体已添加和删除,以及已添加和删除到缓存。这通常是致命的,因为客户端此时无法跟踪UserRole实体是否对应于数据库中的记录。如果我正确阅读了您的代码,那么您正在犯所有这些错误。项目ViewModel代替当我将该用户的角色表示为“ Item ViewModel”实例的列表并推迟实体操作直到保存用户的选择时,我获得了更大的成功。在我们的讨论中,我们将此对象称为UserRoleVm。它可能在JavaScript中定义如下{ role, isSelected, userRole}制作屏幕时,填充一个UserRoleVm实例列表,每个Role实例一个使用适当的role实体设置每个虚拟机的Role属性将视图绑定到vm.role.name当且仅当这样的实体已经存在时,才使用相关用户的userRole实体设置每个vm的UserRole属性如果虚拟机具有isSelected=true并且未删除userRole,则设置虚拟机的vm.userRole.entityAspect.entityState。将虚拟机的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”,已被“取消选中”,但仍未保存;如何发生?),通过调用vm.userRole.entityAspect.rejectChanges()进行还原现在调用manager.saveChanges()。如果保存成功,一切都很好。如果失败,最干净的方法是调用manager.rejectChanges()。这将清理甲板(并丢弃用户自上次保存以来所做的任何更改)。无论哪种方式,都像我们一开始那样从头开始重建列表。理想情况下,在异步保存成功或失败返回之前,不要让用户对用户角色进行更多更改。我敢肯定,您可以比这更聪明。但是这种方法是可靠的。变异不要为UserRoleVm.userRole所困扰。不要在UserRole中携带现有的UserRoleVm实体。而是在初始化UserRole属性时参考用户的缓存UserRoleVm.isSelected实体。然后在准备存储期间评估列表,并根据相同的逻辑查找并调整缓存的UserRole实例。启用“保存”按钮(12月19日更新)山姆问: 当EntityManager更改时,“保存”按钮的Disabled属性绑定到设置为true的属性。但是,由于我的ViewModel不是EntityManager的一部分,因此当用户添加/删除联系人时,这不会更改附加到EntityManager的模型。因此,永远不会启用“保存”按钮(除非我更改模型的另一个属性)。您能想到一种解决方法吗?是的,我可以想到几个。使用get和set方法将isSelected属性定义为ES5属性;在set方法中,您向外部VM发出UserRoleVm实例已更改的信号。这是可能的,因为如果Angular和Breeze一起工作,则必须使用ES5浏览器。将ngClick(或ngChanged)添加到绑定到外部vm中的函数的复选框html中,例如, ... ng-model =“ role.isSelected” ng-click =“ vm.userRoleClicked(role)” ...利用angular支持的“视图更改”检测(我认为是“ isPristine”)。我通常不这样走,所以我不知道细节。只要您不允许用户离开此屏幕并返回并希望保留对UserRoleVm列表的未保存更改,此方法就可行。vm.userRoleClicked可以将vm.hasChanges属性设置为true。将保存按钮的isEnabled绑定到vm.hasChanges。现在,当用户单击复选框时,保存按钮将亮起。如前所述,保存按钮单击操作会在userRoleVm列表上进行迭代,从而创建和删除UserRole实体。当然,这些动作由EntityManager检测到。您可能会更喜欢。您的UserRoleVm类型可以在创建时记录其原始选定状态(userRoleVm.isSelectedOriginal),而您的vm.userRoleClicked方法可以评估整个列表,以查看当前任何选定状态是否不同于其原始选定状态...并设置相应地。这完全取决于您的UX需求。 重建列表时,请不要忘记清除vm.hasChanges。我想我更喜欢#2;对我来说,这似乎既简单又清晰。2014年2月3日更新:punker中的示例我写了我在这里描述的a plunker to demonstrate the many-to-many checkbox technique。 readme.md解释了所有内容。关于breeze - 轻而易举:保存时的多对多问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20638851/
10-10 05:37