如何 post 两个或多个模型在一个表格中?

How can I post two or more models in one form?

我正在为一个项目开发互联网课程计划应用程序。课程计划是根据以下模型构建的(在数据库优先方法中使用 Entity Framework 生成):

public partial class Subject
{
    public int Id { get; set; }
    public string Hour { get; set; }
    public string Name { get; set; }
    public int DayId { get; set; }
    [Required]
    public int PlanId { get; set; }

    public virtual Day Day { get; set; }
    public virtual Plan Plan { get; set; }
}

public partial class Plan
{
    public Plan()
    {
        this.Przedmiot = new HashSet<Subjects>();
    }

    public int Id { get; set; }
    [Required]
    public string Name { get; set; }

    public virtual ICollection<Subject> Subject { get; set; }
}

我在一个视图中显示两个模型没有问题,但我似乎无法弄清楚如何在创建新计划时将两个模型post 存入数据库。我希望我的视图看起来像这样:

所以我的问题是这里最好的方法是什么,以及如何在数据库中的计划 table 中创建一条记录,并在这个视图中链接到它的许多主题记录。

编辑: 根据要求用我的显示视图编码(因为它很长,省略了不必要的部分):

@model IEnumerable<Lesson_plan.DAL.Subject>
<table style="border: 1px solid black; margin: 40px; width: 100%;">
    <tr>
        <th>Hours</th>
        <th>Monday</th>
        <th>Tuesday</th>
        <th>Wednesday</th>
        <th>Thursday</th>
        <th>Friday</th>
        <th>Saturday</th>
        <th>Sunday</th>
    </tr>

    @{
        if (Model != null)
        {
            var r = 1;
            var t = 1;
            List<string> hours = new List<string>();
            foreach (var subject in Model)
            {
                if (!hours.Contains(subject.Hour))
                {
                    <tr>
                        <td>
                            <textarea>    
                            @Html.DisplayFor(modelItem => subjest.Hour)
                            @{ hours.Add(subject.Hour); }
                            </textarea>
                        </td>

                        <td>
                            <textarea>  
                            @foreach (var subjectName in Model)
                            {
                                if (subjectName.Day.DayOfTheWeek.Equals("Monday") &&
                                    subject.Hour.Equals(subjetName.Hour))
                                {
                                    @Html.DisplayFor(modelItem => subject.Name)
                                }
                            }   
                            </textarea>
                        </td>

//and so on for every day 

                        }
                    </tr>
                    r++;
                }
            }
        }
    }
</table>

我的控制器代码 class(我用 Create 方法做了一些实验,但我在这里 post 使用原始方法):

namespace Lesson_plan.Controllers
{
    public class PlansController : Controller
    {
        private readonly LessonPlanEntities db = new LessonPlanEntities();

        // GET: Plans
        public ActionResult Index()
        {
            var plans = db.Plan.ToList();
            return View(plans);
        }

        // GET: Plans/Details/5
        public ActionResult Details(int? id)
        {
            if (id == null)
                return Create();
            var subjects = db.Subject.
                Where(x => x.PlanId == id).
                OrderByDescending(x => x.Hour).ToList();
            if (subjects.Count > 0)
                ViewBag.Title = subjects[0].Plan.Name;
            return View(subjects);
        }

        // GET: Plans/Create
        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Create(Plan plan)
        {
            if (ModelState.IsValid)
            {
                db.Plan.Add(plan);
                db.SaveChanges();
                return RedirectToAction("Index");
            }

            return View(plan);
        }

        // GET: Plans/Edit/5
        public ActionResult Edit(int? id)
        {
            if (id == null)
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            var plan = db.Plan.Find(id);
            if (plan == null)
                return HttpNotFound();
            return View(plan);
        }

        // POST: Plans/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "Id,Nazwa")] Plan plan)
        {
            if (ModelState.IsValid)
            {
                db.Entry(plan).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(plan);
        }

        // GET: Plans/Delete/5
        public ActionResult Delete(int? id)
        {
            if (id == null)
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            var plan = db.Plan.Find(id);
            if (plan == null)
                return HttpNotFound();
            return View(plan);
        }

        // POST: Plans/Delete/5
        [HttpPost]
        [ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            var plan = db.Plan.Find(id);
            db.Plan.Remove(plan);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
                db.Dispose();
            base.Dispose(disposing);
        }
    }
}

编辑2 使用表单创建视图的代码:

@model Lesson_plan.DAL.Plan

@using (Html.BeginForm("Create", "Plans"))
{
    <div>
        @Html.LabelFor(plan => plan.Name)<br/>
        @Html.TextAreaFor(plan => plan.Name)
    </div>
    <table style="border: 1px solid black; margin: 40px; width: 100%;">
        <tr>
            <th>Hours</th>
            <th>Monday</th>
            <th>Tuesday</th>
            <th>Wednesday</th>
            <th>Thursday</th>
            <th>Friday</th>
            <th>Saturday</th>
            <th>Sunday</th>
        </tr>
            @{
                for (int i = 0; i < 7; i++)
                {
                    <tr>  
                        //what sould i put here ?                    
                        <td><textarea></textarea></td>
                        <th><textarea></textarea></th>
                        <th><textarea></textarea></th>
                        <th><textarea></textarea></th>
                        <th><textarea></textarea></th>
                        <th><textarea></textarea></th>
                        <th><textarea></textarea></th>
                        <th><textarea></textarea></th>
                    </tr>
                }
            }
    </table>
    <p>
        <a type="button" class="btn btn-info" href=@Url.Action("Index")>Back</a>
        <input type="submit" value="Create"/>
    </p>
}

最好的方法是设计一个 ViewModel class,它可以保存表单上需要提交的所有数据,用于两个计划 and/or 主题。

在接收此 ViewModel 的操作方法中,您可以分离数据,并基于此执行任何插入 and/or 更新以将其存储在数据库中。

只有 Controller 应该知道您的数据库 classes。 ViewModel 和 Database 对象之间的所有映射都应由控制器完成,并且 View 不应受此影响或阻碍。

通过将多个模型组合成一个视图模型,可以将它们发送到视图。但是,您的问题似乎有所不同,所以这里是长答案:

至于数据库设计问题,我假设您使用的是关系存储,并且一个主题可以出现在多个计划中,因此 many-to-many 应该就位。您可以阅读有关该主题的更多信息 here

至于您的 Web 应用程序,您通常可以简单地创建一个 ViewModel,其中包含您想要的两个模型。如果您没有详细说明您的问题,那将是正确的答案。但是,在您的情况下,您的两个 "model" 类 已经链接,因此您的模型可以只是一个计划。

由于我假设您使用的是 EF6(基于您生成的 类),您计划的主题将与计划一起 post 退回。

我要做的是使用 singleton 将当前存在的主题缓存在您的应用程序中的某个位置,然后在 post 返回时,您应该为您的计划迭代缓存的主题,检查是否它们已经存在于您的数据库中。如果他们不这样做 - 首先插入它们,如果他们这样做 - 只需将它们添加到计划中的 ICollection 中。那么您应该知道如何使用 EF 插入计划。

一路上可能会有更多的颠簸,但您可以针对这些问题提出新的问题 - 您现在的问题太宽泛了。

我正在努力帮助您使某些东西正常工作,尽管它可能并不理想。一旦开始工作,我建议您阅读有关放置业务逻辑的位置(以及如何使控制器变小)的内容。 This文章可能是一个好的开始。

我建议的另一件事是将您的数据访问保持在一个单独的层中。 Goodle“3 层架构”并检查 this video 存储库模式。

祝你好运!

** Post OP-edit **

好的,看看你的代码似乎有很多问题,你可能想分成多个问题。这是一个开始:

  1. 您的 View 上没有表单,您需要 View 上的表单和 post 按钮,以及 Controller 上标有 [Post] 的 Action 方法版本.对于旧版本,我推荐 this tutorial for asp.mvc core and this。如果您想要 "edit" 和 "display" 视图的不同页面,只需创建单独的页面,但在创建时,您需要有一个表单。
  2. 目前,您应该使用 Plan 作为 viewModel。通常这并不理想,但可以解释为什么我需要写一本书作为答案。链接的资源应该可以帮助您理解该部分。
  3. 您似乎使用的是旧版本的 asp.net mvc(也许我错了?请告诉我们是哪一个)。如果您想在客户端的特定日期添加新主题,您还必须使用 javascript (jQuery) - 或完整的 client-side 库,例如 React 或 Angular.如果您不熟悉完整的框架,请暂时使用 JQuery。检查这个 question and answer 以了解如何实现它。
  4. 您可能希望将 API 方法和普通的旧 MVC 方法分开到不同的项目中。

** 编辑 2 ** 现在看到您的计划创建代码,您似乎已经正确地读到 @Html.TextAreaFor(plan => plan.Name) 是创建可以 post 发送到服务器的输入的方法。

但是您仅将其用于您的计划名称。

不要使用下面的文本区域(我假设这些是主题名称),而是使用类似

的东西
@for(int i = 0; i < plan.Subject.Count(); i++)
{
    @Html.TextAreaFor(plan => plan.Subject[i].Name)
}

假设您作为模型传递给视图的计划有任何主题,这将是列出它们并使它们可编辑的方式。

现在,您似乎想根据星期几将它们分开,因此您可能需要进行一些过滤,例如:

var monday = 1;
@for(int i = 0; i < plan.Subject.Where(s => s.DayId == monday).OrderBy(s => s.Hour).Count(); i++)

并将其粘贴到七列中的每一列中。这将为其中已存在的每个主题创建文本区域。

至于在视图上动态创建新的,请参阅上面的第 3 点。如果您还没有任何主题,请在您的数据库中创建一些假主题,这样您就可以在客户端上呈现一些主题并检查它们的源代码,然后按照我在#3 中链接的文章进行操作。