我想通过一个包含订阅金额字段Amount和可用公司的复选框的页面来允许创建订阅。每个复选框代表一个现有公司(存储在我的数据库中)。
有什么想法吗?我已经阅读了这篇ASP.NET MVC Multiple Checkboxes,但并没有真正帮助到我。
编辑:这是我的表图。
你开始时有两个视图模型。第一个代表所选公司的详细信息,例如名称、地址和行业类型。第二个视图模型是用于提交用户评论的表单,其中包含用户名、评论文本和评分。
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<CompanySelectViewModel> 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)
<input type="submit" value="Create" />
@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)
名称
作为隐藏字段添加,以便在POST期间保留名称。
显然需要对视图进行更多的样式设置。
现在,您的POST操作将如下所示:
[HttpPost]
public ActionResult Create(SubscriptionCreateViewModel viewModel)
{
if (ModelState.IsValid)
{
var subscription = new Subscription
{
Amount = viewModel.Amount,
Companies = new List<Company>()
};
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);
}
您可以使用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<SubscriptionByCompany>()
};
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);
}
EditorFor(model => model.Companies)
检测到 Companies
是一个集合,并为每个项目呈现一个局部视图。模板文件的名称(必须匹配模型名称)和路径必须精确,因为MVC会按照某些约定搜索局部视图(通常在 Views/{ControllerName}
和 Shared
中,但在名为 EditorTemplates
的子文件夹中)。 - SlaumaEntityObject
而没有POCOs吗?你可以尝试注释掉那一行。我相信EntityObject
会自动初始化其导航集合。 - SlaumaSubscriptionEditViewModel
中的Id),加载订阅,包括已关联公司的Ids,再次加载所有公司,但然后为已经与订阅相关的公司设置IsSelected
为true
。POST操作更加困难,因为现在当用户取消选中一个公司时,您需要从链接表中删除实体,并在用户选中新公司时向链接表添加实体。只要开始并在遇到问题时提出新问题即可 :) - Slauma这只是对Slauma回答的一个扩展。在我的情况下,我需要表现多对多关系,就像产品和角色之间的表格一样,第一列代表产品,标题代表角色,表格用复选框填充以选择产品的角色。 为了实现这个目标,我使用了像Slauma描述的ViewModel,但添加了另一个包含最后两个模型的模型,如下所示:
public class UserViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<ProductViewModel> Products { get; set; }
}
public class ProductViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public IEnumerable<RoleViewModel> Roles { get; set; }
}
public class RoleViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
UserViewModel user = new UserViewModel();
user.Name = "Me";
user.Products = new List<ProductViewModel>
{
new ProductViewModel
{
Id = 1,
Name = "Prod1",
Roles = new List<RoleViewModel>
{
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"
}))
{
<table>
<thead>
<tr>
<th>
</th>
@foreach (RoleViewModel role in Model.Products.First().Roles.ToList())
{
<th>
@role.Name
</th>
}
</tr>
</thead>
<tbody>
@Html.EditorFor(model => model.Products)
</tbody>
</table>
<input type="submit" name="Create" value="Create"/>
}
正如您所看到的,EditorFor正在使用产品模板:
@model Insurance.Admin.Models.ProductViewModel
@Html.HiddenFor(model => model.Id)
<tr>
<th class="col-md-2 row-header">
@Model.Name
</th>
@Html.EditorFor(model => model.Roles)
</tr>
这个模板使用另一个角色模板:
@model Insurance.Admin.Models.RoleViewModel
@Html.HiddenFor(model => model.Id)
<td>
@Html.EditorFor(model => model.IsSelected)
</td>
看,我们有一个表格,第一列是产品,标题是角色,表格中填充了复选框。我们正在发布UserViewModel,您将看到所有数据都已发布。