如何遍历 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 模型中,我创建了两个新方法AddGenreDeleteGenre。在控制器中,我添加了四个新的IEnumerable&lt;int&gt; 变量: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 已经获得 IEnumerable GenreId。请看我的回答。 当我将对象更改为数组时,我收到一个错误,即 MovieGenresDirectorsRatings 不包含 CMovie (genr.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&lt;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

在视图中不预先选择具有所选值的MultiselectList

如何遍历 CloudFormation 模板中的值

如何遍历 Hashmap、打印键/值并删除 Rust 中的值?