我正在使用ASP.NET MVC 4和Entity Framework。在我的数据库中,我有一个订阅表,表示对公共交通的订阅。此订阅可以提供对多个公共交通公司的访问权限(因此订阅可以包含1,2,3 ......公司),然后这些表之间是多对多关系(我有他们之间的中间表。)
我想允许通过一个页面创建订阅,该页面将包含订阅的字段 Amount 以及按复选框列出的可用公司。每个复选框代表一个现有公司(存储在我的数据库中的公司)。
有关如何做到这一点的任何想法?我已经阅读了这个ASP.NET MVC Multiple Checkboxes,但它并没有真正帮助。
编辑:这是我的表格图。
答案 0 :(得分:19)
您从两个视图模型开始。第一个代表选定公司......
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)
Name
被添加为隐藏字段,以便在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 :在this answer中是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);
}
答案 1 :(得分:1)
我更喜欢这个答案:Saving Many to Many relationship data on MVC Create view 如果您先做数据库,那么只需跳到第1节的viewmodel部分。
答案 2 :(得分:1)
只是对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; }
}
接下来,在Controller中我们需要填充数据:
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,您将看到所有数据都已发布。