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

Tra*_*ffy 8 c# checkbox asp.net-mvc many-to-many entity-framework

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

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

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

编辑:这是我的表格图.

表格图

Sla*_*uma 19

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

public class CompanySelectViewModel
{
    public int CompanyId { get; set; }
    public string Name { get; set; }
    public bool IsSelected { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

public class SubscriptionCreateViewModel
{
    public int Amount { get; set; }
    public IEnumerable<CompanySelectViewModel> Companies { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后在SubscriptionControllers 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);
}
Run Code Online (Sandbox Code Playgroud)

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

@model SubscriptionCreateViewModel

@using (Html.BeginForm()) {

    @Html.EditorFor(model => model.Amount)

    @Html.EditorFor(model => model.Companies)

    <input type="submit" value="Create" />
    @Html.ActionLink("Cancel", "Index")
}
Run Code Online (Sandbox Code Playgroud)

要正确呈现公司复选框,请引入编辑器模板.它必须具有名称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)
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

编辑

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

如果由于某种原因不想更改架构,则必须更改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);
}
Run Code Online (Sandbox Code Playgroud)

  • @Traffy:这是一项更多的工作.您将拥有类似的视图模型(但包括在`SubscriptionEditViewModel`中的Id),加载订阅,包括已经相关公司的ID,再次加载*所有*公司,但然后将'IsSelected`设置为'true`为公司已经与订阅有关.POST操作更加困难,因为现在您需要在用户取消选中公司时从链接表中删除实体,并在用户检查新公司时将实体添加到链接表中.刚开始并在遇到问题时打开新问题:) (2认同)