如何遍历 MultiSelectList 发布的值并将每个值插入 ASP.Net MVC 5 中的新数据库行?
Posted
技术标签:
【中文标题】如何遍历 MultiSelectList 发布的值并将每个值插入 ASP.Net MVC 5 中的新数据库行?【英文标题】:How to loop through MultiSelectList posted values and insert each into a new database row in ASP.Net MVC 5? 【发布时间】:2018-04-29 02:23:00 【问题描述】:我已经访问了所有可以找到的搜索结果,但我被卡住了。
我正在使用 ASP.Net MVC 5 开发“DVD 商店”网站,但在将 MultiSelectList 值插入数据库时遇到了一些困难。数据库结构有一个多对多表,其中存储电影 ID、流派 ID 和主键。还有一个电影表,其中包含电影名称、成本、图像路径、导演、评级等字段。
我的插入逻辑适用于将数据放入电影表中,但我的创建视图中有一个多选列表,该列表是从数据库中的电影类型列表中填充的。当我选择一个列表项时,ID 插入到 moviegenre 表中就好了。当我选择多个时,只插入一个 ID。我想为每个选择插入一个新行,其中包含电影 ID 和流派 ID(即,如果选择了 3 个流派,则为每行创建 3 个具有相同电影 ID 但流派 ID 不同的新行)。
如何遍历发布的 MultiSelectList 数据并为每个值插入一个新行?
这是我视图中的代码:
@html.ListBoxFor(r => r.CMovie.GenreId, new MultiSelectList(Model.CGenreList, "Id", "Description"), new @class = "form-control" )
我的控制器:
[HttpPost]
public ActionResult Create(MovieGenresDirectorsRatings mgdr) // The ViewModel
try
mgdr.CMovie.Insert();
return RedirectToAction("Index");
catch (Exception ex)
throw ex;
return View(mgdr);
视图模型:
public class MovieGenresDirectorsRatings
public IEnumerable<int> GenreId get; set;
public CGenreList CGenreList get; set;
public CDirectorList CDirectorList get; set;
public CFormatList CFormatList get; set;
public CRatingList CRatingList get; set;
public CGenre CGenre get; set;
public CMovie CMovie get; set;
还有我在模型中的插入逻辑:
public void Insert()
using (myEntities dc = new myEntities())
try
tblMovie movie = new tblMovie();
// Add movie to tblMovie
movie.Id = 1;
if (dc.tblMovies.Any())
movie.Id = dc.tblMovies.Max(p => p.Id) + 1;
this.Id = movie.Id;
movie.Title = this.Title;
movie.Description = this.Description;
movie.ImagePath = this.ImagePath;
movie.Cost = this.Cost;
movie.RatingId = this.RatingId;
movie.FormatId = this.FormatId;
movie.DirectorId = this.DirectorId;
try
tblMovieGenre genre = new tblMovieGenre();
genre.Id = 1;
if (dc.tblMovieGenres.Any())
genre.Id = dc.tblMovieGenres.Max(p => p.Id) + 1;
// THIS IS THE PART that I'm struggling with.
// I think the data is there, I'm just not sure how to access it
foreach (var GenreId in GenreId) // This line is probably wrong, but I'm not sure how to access the data
genre.GenreId = this.GenreId.FirstOrDefault();
genre.MovieId = movie.Id;
dc.tblMovieGenres.Add(genre);
catch (Exception ex)
throw ex;
dc.tblMovies.Add(movie);
// Commit changes
dc.SaveChanges();
catch (Exception ex)
throw ex;
我已经尝试过 foreach 循环和 for 循环,但我无法让它工作。我做错了什么?
编辑 #1: 进行一些更改后,这是我在 CMovie 类中的(当前和非工作的)完整插入逻辑。当我只从 MultiSelectList 中选择一种“类型”时,它可以正常工作并正确插入到两个表中。但是,当我从 MultiSelectList 中选择两个或多个“流派”时,会出现“值不能为空,参数名称:项目”错误。
public void Insert()
using (dbEntities2 oDc = new dbEntities2())
try
tblMovie movie = new tblMovie();
// Add movie to tblMovie
movie.Id = 1;
if (oDc.tblMovies.Any()) // If table is not empty
movie.Id = oDc.tblMovies.Max(p => p.Id) + 1;
this.Id = movie.Id;
movie.Title = this.Title;
movie.Description = this.Description;
movie.ImagePath = this.ImagePath;
movie.Cost = this.Cost;
movie.RatingId = this.RatingId;
movie.FormatId = this.FormatId;
movie.DirectorId = this.DirectorId;
try
foreach (var GenreId in GenreIds)
tblMovieGenre genre = new tblMovieGenre();
genre.Id = 1;
if (oDc.tblMovieGenres.Any())
genre.Id = oDc.tblMovieGenres.Max(p => p.Id) + 1; // genre.Id is set to the highest id in the table, +1
genre.Id = this.Id;
genre.GenreId = GenreId;
genre.MovieId = movie.Id;
oDc.tblMovieGenres.Add(genre);
catch (Exception ex)
throw ex;
oDc.tblMovies.Add(movie);
// Commit changes
oDc.SaveChanges();
catch (Exception ex)
throw ex;
`
编辑 2:我找到了解决问题的方法。希望这可以帮助其他有同样问题的人。我将创建更改为使用下拉列表而不是多选列表,并修改了编辑方法以允许更新多个流派。
在CMovie
模型中,我创建了两个新方法AddGenre
和DeleteGenre
。在控制器中,我添加了四个新的IEnumerable<int>
变量:oldGenreIds、newGenreIds、adds 和 deletes。
然后我从IEnumerable
删除和添加列表中列出:
IEnumerable<int> deletes = oldGenreIds.Except(newGenreIds);
IEnumerable<int> adds = newGenreIds.Except(oldGenreIds);
deletes.ToList().Foreach(a => mgdr.CMovie.DeleteGenre(id, a));
adds.ToList().Foreach(a => mgdr.CMovie.AddGenre(id, a));
然后调用 update 方法,设置更改后的值(包括电影标题、描述、图像路径等):
mgdr.CMovie.Update();
通过将 ForEach 逻辑移动到控制器中,我能够多次调用 AddGenre
方法 - 当直接在 Insert
方法中调用它时,我无法做到这一点。
【问题讨论】:
【参考方案1】:您的 post 方法应该接受数组而不是单个对象。
[HttpPost]
public ActionResult Create(MovieGenresDirectorsRatings[] mgdr) // The ViewModel
foreach(var genr in mgdr)
try
genr.CMovie.Insert(); //inserting each object received from view.
return RedirectToAction("Index");
catch (Exception ex)
throw ex;
return View(mgdr);
这个想法是从视图中接收所有对象。即使您的视图正在发布多选列表中的所有项目,您也需要在控制器中使用类似数组的结构来从视图中获取数据。一旦你在控制器中获得了这些数据,你就可以遍历所有这些数据,并一一插入。
【讨论】:
这是不正确的,因为 OP 已经获得 IEnumerablegenr.CMovie.Insert()
) 的定义
您的答案被选中,因为它对帮助我解决问题最有帮助。我最终找到了一个替代解决方案(见编辑#2),但你很有帮助。谢谢。【参考方案2】:
这是问题所在(实际上是一个忽略):
tblMovieGenre genre = new tblMovieGenre();
// code...
foreach (var GenreId in GenreId)
genre.GenreId = this.GenreId.FirstOrDefault();
// code
dc.tblMovieGenres.Add(genre);
所以在上面的代码中看到你创建了一个tblMovieGenre
,然后在你的循环中你一遍又一遍地添加tblMovieGenres
的相同实例。所以基本上你添加了一个 tblMovieGenres
,其中包含循环中最后一次迭代的值。
修复
要解决此问题,请在循环内移动实例化:
foreach (var GenreId in GenreId)
tblMovieGenre genre = new tblMovieGenre();
// code...
dc.tblMovieGenres.Add(genre);
其他建议
1
.NET 不鼓励使用匈牙利符号,因此在数据库表前加上 tbl
不仅是符号问题,而且会使代码更难阅读,尤其是在使用 ORM 时。因此,如果您从表名中删除 tbl
,您的代码将是:
MovieGenere
而不是tblMovieGenre
。
2
另外,如果我查看一行代码并且可以确定对象的类型,我总是使用var
来代替。像这样:
tblMovieGenre genre = new tblMovieGenre();
var genre = new tblMovieGenre();
这是个人喜好(少打字)。
但如果我无法通过读取单行来确定类型,那么我不使用var
:
tblMovieGenre genre = GetMovie();
3
如果你让你的表主键列identity
从1开始,那么你就不需要这样的代码:
movie.Id = 1;
if (dc.tblMovies.Any())
movie.Id = dc.tblMovies.Max(p => p.Id) + 1;
每当您在代码中创建一个新对象时,它的 ID 将为 0,当您将其添加到数据库时,EF 会将其视为新记录并为其生成新标识。这将管理 ID 的责任从您手中移开,这意味着更少的编码。
【讨论】:
首先,感谢您抽出宝贵时间回复。我忘了提到这是针对学校项目的,我的导师建议使用匈牙利符号,以及如何实例化对象(tblMovieGenre genre = new tblMovieGenre()
),所以这是我被教导的唯一方法。当我在 foreach 循环中实例化 tblMovieGenre 时,它在插入一个时仍然有效,但是当我从多选列表中选择多个类型时,它会引发“值不能为空,参数名称:项目”异常。你的解决方案是有道理的,我只需要弄清楚如何实现它。
我也试过这个 for 循环:for (var i = 0; i<GenreId.GetLength(0); i++) tblMovieGenre genre = new tblMovieGenre(); genre.Id = 1; if (dc.tblMovieGenres.Any()) // CODE
但 foreach 循环也不起作用。
@mark 请发布CGenre
类的代码,以便我了解为什么会抛出该错误。
CGenre 类用于将带有 Id 和 Description(流派名称)的流派插入到 tblGenre 表(填充视图上的选择列表)中。我觉得我可以通过为该表添加一个带有单独插入逻辑的 CMovieGenre 类来轻松解决此问题,但讲师特别告诉我们不要创建该类。如果你还想看 CGenre 类,我会贴出代码,但它与插入到 movieGenre 表中完全无关。
对不起,我的意思是tblMovieGenre
。以上是关于如何遍历 MultiSelectList 发布的值并将每个值插入 ASP.Net MVC 5 中的新数据库行?的主要内容,如果未能解决你的问题,请参考以下文章
如何让 MultiSelectList 绑定到我的数据模型?
ViewBag 在创建 MultiSelectList 时的奇怪之处
从SelectList获取MultiSelectList的SelectListItems