如何将来自不同实体的项目动态添加到 ASP.NET Core MVC 中的列表
How to dynamically add items from different entities to lists in ASP.NET Core MVC
这是我第一次接触一般的列表,我的场景有点复杂。我想添加一个 Story 类型的对象,它有一个可以动态添加的 Sentences 列表。 Sentence 与 Image 具有一对一关系,与 Audio 具有一对一关系(可选添加)。我设法将句子列表与故事对象一起添加到数据库中。但是我不知道从哪里开始其他两个实体。
以下是每个实体的模型:
public class Story
{
public int StoryId { get; set; }
public string Title { get; set; }
public string Type { get; set; }
public DateTime Date { get; set; }
public virtual IEnumerable<Sentence> Sentences { get; set; } // one to many Story-Sentence
}
句子class:
public class Sentence
{
public int Id { get; set; }
public string SentenceText { get; set; }
public virtual Audio Audio { get; set; } // one to one Sentence-Audio
public virtual Image Image { get; set; } // one to one Sentence-Image
}
图片class:
public class Image
{
[Key]
[ForeignKey("Sentence")]
public int Id { get; set; }
public string ImageSelected { get; set; }
public virtual Sentence Sentence { get; set; }
}
音频 class 与图像 class 完全一样。景色。
@model Story
<div id="editorRows">
@foreach (var item in Model.Sentences)
{
<partial name="_SentenceEditor" model="item" />
}
</div>
<a id="addItem" asp-action="BlankSentence" asp-controller="StoryTest">Add Sentence...</a> <br />
<input type="submit" value="Finished" />
局部视图
@model Sentence
<div class="editorRow">
@using (Html.BeginCollectionItem("sentences"))
{
<span>Name: </span> @Html.EditorFor(m => m.SentenceText);
}
@using (Html.BeginCollectionItem("sentences"))
{
<span>Image: </span> @Html.EditorFor(m => m.Image.ImageSelected);
}
<a href="#" class="deleteRow">delete</a>
</div>
我有一些 javascript 可以动态添加和删除行。
最后在控制器中我只是将模型保存在数据库中
[HttpPost]
public async Task<IActionResult> AddTwo(Story model/*IEnumerable<Sentence> sentence*/)
{
if (ModelState.IsValid)
{
_db.Story.Add(model);
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(model);
}
简而言之,我想在句子中添加图像和音频。并且还能够正确访问整行进行编辑。
你有几个选择
选项 1 是为所有 child object 设置一个 table,其中包含所有类型 object 的所有字段。有些可能对所有 object 都是通用的,例如标题,其他将适用于一种类型object。您需要一个名为 ObjectTypeId 之类的字段 - 您可以将其 link 转换为 table,每个类型都有一个文本名称。您还可以为此 ID 创建一个枚举,以允许您使用 C# switch 和 case 语句来决定如何显示 objects、保存值等。
选项 2 是在不同的 table 中使用不同的 object。然后你的句子有多个可为空的字段 links 到每种类型的 object table,其中只有一个有一个整数 - 其余的为空(除非你以某种方式想要有多个 objects 在同一个句子中)。您可以再次使用 ObjectTypeId 或依赖 link 到 children 不为空的列之一。与选项 1 一样,您可能出于某种原因想要一个空句 - 需要考虑的事情
我根据你的代码做了一个demo。您可以将数据存储在 formData 中,然后使用 ajax 将其传递给控制器操作。
主视图:
@model Story
<form asp-action="AddSentence" method="post">
<div id="editorRows">
<input type="hidden" name="" value="@Model.StoryId" />
@foreach (var item in Model.Sentences)
{
<partial name="_SentenceEditor" model="item" />
}
</div>
<a id="addItem" asp-action="BlankSentence" asp-controller="StoryTest">Add Sentence...</a>
<br />
<input type="submit" id="submit" value="Finished" />
</form>
@section scripts {
<script>
$("#submit").click(function (e) {
e.preventDefault();
var formData = new FormData();
$("input[name='Audio.AudioSelected']").each(function (i) {
var AudioSelected = $(this).val();
formData.append("Sentences[" + i + "].Audio.AudioSelected", AudioSelected);
});
$("input[name='Image.ImageSelected']").each(function (i) {
var ImageSelected = $(this).val();
formData.append("Sentences[" + i + "].Image.ImageSelected", ImageSelected);
});
$("input[name='SentenceText']").each(function (i) {
var SentenceText = $(this).val();
formData.append("Sentences[" + i + "].SentenceText", SentenceText);
});
$.ajax({
method: 'post',
url: "StoryTest/AddSentence",
data: formData,
processData: false,
contentType: false,
success: function () {
}
});
});
$("#addItem").click(function () {
$.ajax({
url: this.href,
cache: false,
success: function (html) { $("#editorRows").append(html); }
});
return false;
});
$("a.deleteRow").on("click", function () {
$(this).parents("div.editorRow:first").remove();
return false;
});
</script>
}
局部视图:
@model Sentence
<div class="editorRow">
<span>Name: </span> @Html.EditorFor(m => m.SentenceText)
<span>Audio: </span> @Html.EditorFor(m => m.Audio.AudioSelected)
<span>Image: </span> @Html.EditorFor(m => m.Image.ImageSelected)
<a href="#" class="deleteRow">delete</a>
</div>
控制器:
public IActionResult Index()
{
Story story = new Story
{
Sentences = new List<Sentence>
{
new Sentence { Id = 1, SentenceText = "AAA"
, Audio = new Audio{ AudioSelected = "True"}
, Image = new Image{ ImageSelected = "True"} },
new Sentence { Id = 2, SentenceText = "BBB"
, Audio = new Audio{ AudioSelected = "False"}
, Image = new Image{ ImageSelected = "False"}},
new Sentence { Id = 3, SentenceText = "CCC"
, Audio = new Audio{ AudioSelected = "True"}
, Image = new Image{ ImageSelected = "False"}}
}
};
return View(story);
}
[HttpPost]
public async Task<IActionResult> AddSentence(Story model)
{
if (ModelState.IsValid)
{
_db.Story.Add(model);
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(model);
}
public IActionResult BlankSentence()
{
return PartialView("_SentenceEditor", new Sentence());
}
结果:
选项 3 - 这是我有时使用的选项,但它确实在某种程度上断开了业务模型与数据库的连接 - 但为了完整起见,我将其包括在内。
您有一个包含多种类型的简单记录。您只需添加一个包含不同类型数据的额外字段。我将其设为 varchar(MAX) 并将其命名为 JsonData。
然后您要做的是基于数据库创建部分模型 class 并添加所有属性以获取和设置您想要的类型。这些很简单 one-liners.
您还可以有更复杂的实例,例如可能是列表的列表,这不会增加数据库上下文代码的复杂性。
与数据库上下文 class 版本相比,您的视图模型和视图并没有真正改变。如果您使用的是 SQL 服务器,您还可以将 JSON 转换为可以包含在查询中的列
/// <summary>
/// Gets or sets card instance from JSON data for resource. Null if not a card
/// </summary>
public Card CardGet
{
get
{
if (this.ResourceTypeEnum == ResourceTypeEnumeration.Card)
{
return JsonSerializer.Deserialize<Card>(this.JsonData);
}
// not a card
return null;
}
set
{
this.JsonData = JsonSerializer.Serialize(value);
}
}
这是我第一次接触一般的列表,我的场景有点复杂。我想添加一个 Story 类型的对象,它有一个可以动态添加的 Sentences 列表。 Sentence 与 Image 具有一对一关系,与 Audio 具有一对一关系(可选添加)。我设法将句子列表与故事对象一起添加到数据库中。但是我不知道从哪里开始其他两个实体。
以下是每个实体的模型:
public class Story
{
public int StoryId { get; set; }
public string Title { get; set; }
public string Type { get; set; }
public DateTime Date { get; set; }
public virtual IEnumerable<Sentence> Sentences { get; set; } // one to many Story-Sentence
}
句子class:
public class Sentence
{
public int Id { get; set; }
public string SentenceText { get; set; }
public virtual Audio Audio { get; set; } // one to one Sentence-Audio
public virtual Image Image { get; set; } // one to one Sentence-Image
}
图片class:
public class Image
{
[Key]
[ForeignKey("Sentence")]
public int Id { get; set; }
public string ImageSelected { get; set; }
public virtual Sentence Sentence { get; set; }
}
音频 class 与图像 class 完全一样。景色。
@model Story
<div id="editorRows">
@foreach (var item in Model.Sentences)
{
<partial name="_SentenceEditor" model="item" />
}
</div>
<a id="addItem" asp-action="BlankSentence" asp-controller="StoryTest">Add Sentence...</a> <br />
<input type="submit" value="Finished" />
局部视图
@model Sentence
<div class="editorRow">
@using (Html.BeginCollectionItem("sentences"))
{
<span>Name: </span> @Html.EditorFor(m => m.SentenceText);
}
@using (Html.BeginCollectionItem("sentences"))
{
<span>Image: </span> @Html.EditorFor(m => m.Image.ImageSelected);
}
<a href="#" class="deleteRow">delete</a>
</div>
我有一些 javascript 可以动态添加和删除行。
最后在控制器中我只是将模型保存在数据库中
[HttpPost]
public async Task<IActionResult> AddTwo(Story model/*IEnumerable<Sentence> sentence*/)
{
if (ModelState.IsValid)
{
_db.Story.Add(model);
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(model);
}
简而言之,我想在句子中添加图像和音频。并且还能够正确访问整行进行编辑。
你有几个选择
选项 1 是为所有 child object 设置一个 table,其中包含所有类型 object 的所有字段。有些可能对所有 object 都是通用的,例如标题,其他将适用于一种类型object。您需要一个名为 ObjectTypeId 之类的字段 - 您可以将其 link 转换为 table,每个类型都有一个文本名称。您还可以为此 ID 创建一个枚举,以允许您使用 C# switch 和 case 语句来决定如何显示 objects、保存值等。
选项 2 是在不同的 table 中使用不同的 object。然后你的句子有多个可为空的字段 links 到每种类型的 object table,其中只有一个有一个整数 - 其余的为空(除非你以某种方式想要有多个 objects 在同一个句子中)。您可以再次使用 ObjectTypeId 或依赖 link 到 children 不为空的列之一。与选项 1 一样,您可能出于某种原因想要一个空句 - 需要考虑的事情
我根据你的代码做了一个demo。您可以将数据存储在 formData 中,然后使用 ajax 将其传递给控制器操作。
主视图:
@model Story
<form asp-action="AddSentence" method="post">
<div id="editorRows">
<input type="hidden" name="" value="@Model.StoryId" />
@foreach (var item in Model.Sentences)
{
<partial name="_SentenceEditor" model="item" />
}
</div>
<a id="addItem" asp-action="BlankSentence" asp-controller="StoryTest">Add Sentence...</a>
<br />
<input type="submit" id="submit" value="Finished" />
</form>
@section scripts {
<script>
$("#submit").click(function (e) {
e.preventDefault();
var formData = new FormData();
$("input[name='Audio.AudioSelected']").each(function (i) {
var AudioSelected = $(this).val();
formData.append("Sentences[" + i + "].Audio.AudioSelected", AudioSelected);
});
$("input[name='Image.ImageSelected']").each(function (i) {
var ImageSelected = $(this).val();
formData.append("Sentences[" + i + "].Image.ImageSelected", ImageSelected);
});
$("input[name='SentenceText']").each(function (i) {
var SentenceText = $(this).val();
formData.append("Sentences[" + i + "].SentenceText", SentenceText);
});
$.ajax({
method: 'post',
url: "StoryTest/AddSentence",
data: formData,
processData: false,
contentType: false,
success: function () {
}
});
});
$("#addItem").click(function () {
$.ajax({
url: this.href,
cache: false,
success: function (html) { $("#editorRows").append(html); }
});
return false;
});
$("a.deleteRow").on("click", function () {
$(this).parents("div.editorRow:first").remove();
return false;
});
</script>
}
局部视图:
@model Sentence
<div class="editorRow">
<span>Name: </span> @Html.EditorFor(m => m.SentenceText)
<span>Audio: </span> @Html.EditorFor(m => m.Audio.AudioSelected)
<span>Image: </span> @Html.EditorFor(m => m.Image.ImageSelected)
<a href="#" class="deleteRow">delete</a>
</div>
控制器:
public IActionResult Index()
{
Story story = new Story
{
Sentences = new List<Sentence>
{
new Sentence { Id = 1, SentenceText = "AAA"
, Audio = new Audio{ AudioSelected = "True"}
, Image = new Image{ ImageSelected = "True"} },
new Sentence { Id = 2, SentenceText = "BBB"
, Audio = new Audio{ AudioSelected = "False"}
, Image = new Image{ ImageSelected = "False"}},
new Sentence { Id = 3, SentenceText = "CCC"
, Audio = new Audio{ AudioSelected = "True"}
, Image = new Image{ ImageSelected = "False"}}
}
};
return View(story);
}
[HttpPost]
public async Task<IActionResult> AddSentence(Story model)
{
if (ModelState.IsValid)
{
_db.Story.Add(model);
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
return View(model);
}
public IActionResult BlankSentence()
{
return PartialView("_SentenceEditor", new Sentence());
}
结果:
选项 3 - 这是我有时使用的选项,但它确实在某种程度上断开了业务模型与数据库的连接 - 但为了完整起见,我将其包括在内。
您有一个包含多种类型的简单记录。您只需添加一个包含不同类型数据的额外字段。我将其设为 varchar(MAX) 并将其命名为 JsonData。
然后您要做的是基于数据库创建部分模型 class 并添加所有属性以获取和设置您想要的类型。这些很简单 one-liners.
您还可以有更复杂的实例,例如可能是列表的列表,这不会增加数据库上下文代码的复杂性。
与数据库上下文 class 版本相比,您的视图模型和视图并没有真正改变。如果您使用的是 SQL 服务器,您还可以将 JSON 转换为可以包含在查询中的列
/// <summary>
/// Gets or sets card instance from JSON data for resource. Null if not a card
/// </summary>
public Card CardGet
{
get
{
if (this.ResourceTypeEnum == ResourceTypeEnumeration.Card)
{
return JsonSerializer.Deserialize<Card>(this.JsonData);
}
// not a card
return null;
}
set
{
this.JsonData = JsonSerializer.Serialize(value);
}
}