首页 > 解决方案 > 如何遍历 MultiSelectList 发布的值并将每个值插入 ASP.Net MVC 5 中的新数据库行?

问题描述

我已经访问了我能找到的所有搜索结果,但我被困住了。

我正在使用 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

标签: c#asp.net-mvcrazor

解决方案


这是问题(实际上是一个忽略):

 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 的责任从您手中移开,这意味着更少的编码。


推荐阅读