9

我正在使用 ASP.NET MVC 4 和实体框架。在我的数据库中,我有一个Subscription表,它表示对公共交通的订阅。这个订阅可以提供对几家公共交通公司的访问(所以一个订阅可以有 1、2、3、... 公司),那么它是这些表之间的多对多关系(我在它们之间有一个中间表)。

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

关于如何做到这一点的任何想法?我已经阅读了这个ASP.NET MVC Multiple Checkboxes但它并没有真正的帮助。

编辑:这是我的表格图。

表格图

4

3 回答 3

20

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

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; }
}

然后在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);
}

现在,您有一个用于此操作的强类型视图:

@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:在这个答案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);
}
于 2013-05-05T11:22:19.730 回答
1

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

于 2014-10-09T13:22:21.257 回答
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>

瞧,我们有一个包含第一列 Products 的表,标题包含 Roles,表中填充了复选框。我们正在发布 UserViewModel,您将看到所有数据都已发布。

于 2015-08-21T08:20:43.383 回答