MVC 4 – 多对多关系和复选框

我正在使用ASP.NET MVC 4和Entity Framework。 在我的数据库中,我有一个表Subscription ,表示对公共传输的订阅。 这个订阅可以提供对几个公共交通公司的访问(因此订阅可能有1,2,3,……公司),然后这些表之间是多对多关系(我之间有一个中间表)。

我想允许通过一个页面创建订阅,该页面将包含一个字段的订阅金额和可用的公司复选框。 每个复选框代表一个现有公司(存储在我的数据库中的公司)。

有关如何做到这一点的任何想法? 我已经阅读了这个ASP.NET MVC多重复选框,但它并没有真正帮助。

编辑:这是我的表格图。

表格图

您从两个视图模型开始。 代表选定公司的第一个……

public class CompanySelectViewModel { public int CompanyId { get; set; } public string Name { get; set; } public bool IsSelected { get; set; } } 

…以及订阅创建的第二个:

 public class SubscriptionCreateViewModel { public int Amount { get; set; } public IEnumerable Companies { get; set; } } 

然后在SubscriptionController的GET操作中,从数据库加载公司以初始化视图模型:

 public ActionResult Create() { var viewModel = new SubscriptionCreateViewModel { Companies = _context.Companies .Select(c => new CompanySelectViewModel { CompanyId = c.CompanyId, Name = c.Name, IsSelected = false }) .ToList() }; return View(viewModel); } 

现在,您有一个强类型视图来执行此操作:

 @model SubscriptionCreateViewModel @using (Html.BeginForm()) { @Html.EditorFor(model => model.Amount) @Html.EditorFor(model => model.Companies)  @Html.ActionLink("Cancel", "Index") } 

要正确呈现公司复选框,请引入编辑器模板。 它必须具有名称CompanySelectViewModel.cshtml并进入文件夹Views/Subscription/EditorTemplates (如果它不存在则手动创建这样的文件夹)。 这是一个强类型的局部视图:

 @model CompanySelectViewModel @Html.HiddenFor(model => model.CompanyId) @Html.HiddenFor(model => model.Name) @Html.LabelFor(model => model.IsSelected, Model.Name) @Html.EditorFor(model => model.IsSelected) 

Name将添加为隐藏字段以在POST期间保留名称。

显然你必须对视图进行更多的设计。

现在,您的POST操作将如下所示:

 [HttpPost] public ActionResult Create(SubscriptionCreateViewModel viewModel) { if (ModelState.IsValid) { var subscription = new Subscription { Amount = viewModel.Amount, Companies = new List() }; foreach (var selectedCompany in viewModel.Companies.Where(c => c.IsSelected)) { var company = new Company { CompanyId = selectedCompany.CompanyId }; _context.Companies.Attach(company); subscription.Companies.Add(company); } _context.Subscriptions.Add(subscription); _context.SaveChanges(); return RedirectToAction("Index"); } return View(viewModel); } 

您也可以先使用var company = _context.Companies.Find(selectedCompany.CompanyId);加载公司,而不是使用Attach var company = _context.Companies.Find(selectedCompany.CompanyId); 。 但是使用Attach您不需要往数据库进行往返加载要添加到集合中的公司。

编辑2 :在此答案中是使用相同示例模型Edit操作和视图的延续。)

编辑

你的模型并不是真正的多对多关系。 你有两个一对多的关系。 通常不需要PublicTransportSubscriptionByCompany实体。 如果该表中的复合主键由Id_PublicTransportSubscription, Id_PublicTransportCompany ,则删除id列Id_PublicTransportSubscriptionByCompanyId EF会将此表模式检测为多对多关系,并在每个实体中为订阅和公司创建一个集合。将不会为链接表创建任何实体。 我上面的代码将适用。

如果由于某种原因不想更改架构,则必须更改POST操作,如下所示:

 [HttpPost] public ActionResult Create(SubscriptionCreateViewModel viewModel) { if (ModelState.IsValid) { var subscription = new Subscription { Amount = viewModel.Amount, SubscriptionByCompanies = new List() }; foreach (var selectedCompany in viewModel.Companies.Where(c => c.IsSelected)) { var company = new Company { CompanyId = selectedCompany.CompanyId }; _context.Companies.Attach(company); var subscriptionByCompany = new SubscriptionByCompany { Company = company }; subscription.SubscriptionByCompanies.Add(subscriptionByCompany); } _context.Subscriptions.Add(subscription); _context.SaveChanges(); return RedirectToAction("Index"); } return View(viewModel); } 

我更喜欢这个答案: 在MVC创建视图中保存多对多的关系数据如果您先做数据库,那么只需跳到第1部分的viewmodel部分。

只是Slauma答案的延伸。 在我的情况下,我必须代表多对多,如产品和角色之间的表,第一列代表产品,代表角色的标题和要填充产品的复选框的复选框。 为了达到这个目的,我使用了像Slauma描述的ViewModel,但添加了另一个包含最后两个的模型,如下所示:

 public class UserViewModel { public int Id { get; set; } public string Name { get; set; } public IEnumerable Products { get; set; } } public class ProductViewModel { public int Id { get; set; } public string Name { get; set; } public IEnumerable Roles { get; set; } } public class RoleViewModel { public int Id { get; set; } public string Name { get; set; } public bool IsSelected { get; set; } } 

接下来,在Controller中我们需要填充数据:

 UserViewModel user = new UserViewModel(); user.Name = "Me"; user.Products = new List { new ProductViewModel { Id = 1, Name = "Prod1", Roles = new List { new RoleViewModel { Id = 1, Name = "Role1", IsSelected = false } // add more roles } } // add more products with the same roles as Prod1 has }; 

接下来,在视图中:

 @model UserViewModel@using (Ajax.BeginForm("Create", "User", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, UpdateTargetId = "divContainer" })) {  @foreach (RoleViewModel role in Model.Products.First().Roles.ToList()) {  }  @Html.EditorFor(model => model.Products) 
@role.Name
}

如您所见,EditorFor正在使用产品模板:

 @model Insurance.Admin.Models.ProductViewModel @Html.HiddenFor(model => model.Id)   @Model.Name  @Html.EditorFor(model => model.Roles)  

此模板使用另一个角色模板:

 @model Insurance.Admin.Models.RoleViewModel @Html.HiddenFor(model => model.Id)  @Html.EditorFor(model => model.IsSelected)  

瞧,我们有一个包含第一列产品的表,标题包含角色,表格中填充了复选框。 我们发布了UserViewModel,您将看到所有数据都已发布。