如何 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 **
好的,看看你的代码似乎有很多问题,你可能想分成多个问题。这是一个开始:
- 您的 View 上没有表单,您需要 View 上的表单和 post 按钮,以及
Controller
上标有 [Post]
的 Action 方法版本.对于旧版本,我推荐 this tutorial for asp.mvc core and this。如果您想要 "edit" 和 "display" 视图的不同页面,只需创建单独的页面,但在创建时,您需要有一个表单。
- 目前,您应该使用 Plan 作为 viewModel。通常这并不理想,但可以解释为什么我需要写一本书作为答案。链接的资源应该可以帮助您理解该部分。
- 您似乎使用的是旧版本的 asp.net mvc(也许我错了?请告诉我们是哪一个)。如果您想在客户端的特定日期添加新主题,您还必须使用 javascript (jQuery) - 或完整的 client-side 库,例如 React 或 Angular.如果您不熟悉完整的框架,请暂时使用 JQuery。检查这个 question and answer 以了解如何实现它。
- 您可能希望将 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 中链接的文章进行操作。
我正在为一个项目开发互联网课程计划应用程序。课程计划是根据以下模型构建的(在数据库优先方法中使用 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 **
好的,看看你的代码似乎有很多问题,你可能想分成多个问题。这是一个开始:
- 您的 View 上没有表单,您需要 View 上的表单和 post 按钮,以及
Controller
上标有[Post]
的 Action 方法版本.对于旧版本,我推荐 this tutorial for asp.mvc core and this。如果您想要 "edit" 和 "display" 视图的不同页面,只需创建单独的页面,但在创建时,您需要有一个表单。 - 目前,您应该使用 Plan 作为 viewModel。通常这并不理想,但可以解释为什么我需要写一本书作为答案。链接的资源应该可以帮助您理解该部分。
- 您似乎使用的是旧版本的 asp.net mvc(也许我错了?请告诉我们是哪一个)。如果您想在客户端的特定日期添加新主题,您还必须使用 javascript (jQuery) - 或完整的 client-side 库,例如 React 或 Angular.如果您不熟悉完整的框架,请暂时使用 JQuery。检查这个 question and answer 以了解如何实现它。
- 您可能希望将 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 中链接的文章进行操作。