我在微风应用程序中与多对多关联一直在苦苦挣扎。我在客户端和服务器端都有问题但是现在,我只是暴露了我的客户端问题。我不知道我提出的方法是否正确,我真的希望得到微风团队的反馈意见:
我的商业模式:
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});
用例场景:
在处理多对多关联时,这应该是什么?
我实际上得到一个服务器错误(“not-null属性引用null或瞬态值Business.Entities.RequestContact.Request”)但在得出任何结论之前,我想知道我在客户端做了什么 - 这是正确的。
答案 0 :(得分:8)
首先要处理服务器端建模问题。我在你对你的问题的评论中注意到没有PK。我建议你先解决问题,然后再打扰客户。
我对这种情况有很长的经验。对我来说,规范案例是一个用户,他可以拥有任意数量的角色,而他/她拥有的角色位于UserRoles表中。
典型的用户界面:
我很多时候看到人们将所有可能的角色列表绑定到UserRole
实体列表中。这很少奏效。
很多时候,当用户点击复选框时,我看到有人创建并销毁UserRole
实体。这很少奏效。
我经常看到添加和删除UserRole
实体,并添加和删除到缓存中。这通常是致命的,因为客户端忘记了UserRole
实体此时是否与数据库中的记录相对应。
如果我正确地阅读了你的代码,你就会让每个人都犯错误。
当我将此用户的角色表示为&#34; Item ViewModel&#34;的列表时,我取得了更大的成功。实例并推迟实体操作,直到保存用户的选择为止。
对于我们的讨论,让我们将此对象称为 UserRoleVm 。它可能在JavaScript
中定义如下{
role,
isSelected,
userRole
}
构建屏幕时,
填充UserRoleVm
个实例列表,每Role
个
使用相应的role
实体
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
实例。
当EntityManager发生更改时,“保存”按钮的已禁用属性绑定到设置为true的属性。但是,由于我的ViewModel不是EntityManager的一部分,因此当用户添加/删除联系人时,这不会更改附加到EntityManager的模型。因此,永远不会启用“保存”按钮(除非我更改了模型的其他属性)。你能想到一个解决方法吗?
是的,我可以想到几个。
使用get和set方法将isSelected
属性定义为ES5属性;在set方法中,您向外部 VM发信号通知UserRoleVm
实例已更改。这是可能的,因为如果你让Angular和Breeze一起工作,你必须使用ES5浏览器。
将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>
利用有角度的本机支持&#34;视图已更改&#34;检测(&#34; isPristine&#34;我认为)。我通常不会这样,所以我不知道细节。只要您不允许用户离开此屏幕并返回预期未保存的UserRoleVm
列表更改已被保留,它就可行。
vm.userRoleClicked
可以将vm.hasChanges
属性设置为true。将保存按钮绑定到vm.hasChanges
的isEnabled。现在,当用户单击复选框时,保存按钮会亮起。
如前所述,保存按钮点击操作会迭代userRoleVm
列表,创建和删除UserRole
实体。当然,EntityManager
会检测到这些操作。
你可以变得更加漂亮。您的UserRoleVm
类型可以在创建时记录其原始选定状态(userRoleVm.isSelectedOriginal
),并且您的vm.userRoleClicked
方法可以评估整个列表,以查看当前所选状态是否与其原始选定状态不同。 。并相应地设置vm.hasChanges
。这一切都取决于您的用户体验需求。
重建列表时,不要忘记清除
vm.hasChanges
。
我认为我更喜欢#2;这对我来说似乎最容易和最清楚。
我写过a plunker to demonstrate the many-to-many checkbox technique我在这里描述过。 readme.md 解释了所有。
答案 1 :(得分:1)
Breeze.js客户端不支持&#34;多对多&#34;这个时候的关系。您必须将联结/映射表公开为实体。有关此同一主题的其他几篇文章可供使用。
我们计划在未来增加许多支持。对不起,但还没有约会......