【问题标题】:ViewModel with a SelectList and a LINQ Query result with a single reocrd带有 SelectList 的 ViewModel 和带有单个记录的 LINQ 查询结果
【发布时间】:2016-10-24 19:56:03
【问题描述】:

我们如何使用以下两个属性填充 ViewModel:

  1. 来自 LINQ 查询的 query result type 仅返回一条记录
  2. SelectList 类型

换句话说,假设我们有一个基于所选 ID 仅显示单个电影的视图。并且我们想从年份的下拉列表中为该电影指定发行年份,如何在下面的 ViewModel 和 Controller 中填写 ????

注意:在以下 ViewModel 中,如果我使用 IQueryable<Movie> 类型的 myMovie 属性并将 ViewModel 的此属性分配给来自以下控制器的查询 qrySingleMovie,我会收到错误 the name qrySingleMovie does not exist in the current context

模型

public class Movie
{
    public int ID { get; set; }
    public string Title { get; set; }
    public DateTime ReleaseDate { get; set; }
    public string Genre { get; set; }
    public decimal Price { get; set; }
}

public class Year
{
  public int YearId {get; set;}
  public int MovieYear {get; set;}
}

视图模型

public class MovieGenreViewModel
{
    public ???? myMovie;
    public SelectList MovieReleaseYears;
    public int ReleasedYr { get; set; }
}

控制器

public async Task<IActionResult> Index(int MovieID)
{
    // Use LINQ to get list of Release years.
    IQueryable<string> YearQuery = from m in _context.Years
                                    orderby m.MovieYear
                                    select m.MovieYear;

    //Following query selects only a single movie based on Primary Key ID
    var qrysingleMovie = from m in _context.Movies
                 where m.ID == movieID
                 select m;

    var movieReleaseYrVM = new MovieGenreViewModel();
    movieReleaseYrVM.MovieReleaseYears = new SelectList(await YearQuery.ToListAsync());
    movieReleaseYrVM.myMovie = ????

    return View(movieReleaseYrVM);
}

更新

在我的真实项目中,模型有很多属性,对应的View也显示了大部分属性。我在想是否有一种更简单的方法来定义一个不包含模型所有属性的 ViewModel,例如 ASP.NET Article 定义了一个视图模型 MovieGenreViewModel 但是他们正在使用 List 并且在他们的 View 中他们正在迭代多部电影。我试图模仿他们的例子,但在我的例子中,它是一个带有SelectList 的记录(不是记录列表)。我怎样才能用一条记录来模拟这种场景,并且不能在 View Model 中包含该模型所具有的大量模型属性?

【问题讨论】:

  • public Movie myMovie;movieReleaseYrVM.myMovie = qrysingleMovie.Single(); 怎么样

标签: c# asp.net-mvc linq asp.net-mvc-viewmodel


【解决方案1】:

由于您要更新电影记录,因此您只需要获取电影记录的唯一 ID。那么为什么不将 MovieId 属性添加到您的视图模型。如果您更喜欢在 UI 中显示 MovieName,您也可以添加 MovieName 属性。

记住视图模型是特定于视图的。创建视图模型时,没有必要模仿整个实体模型。只添加您的视图绝对需要的那些属性。

public class MovieGenreViewModel
{
    public int MovieId;
    public string MovieName { set;get;}
    public SelectList MovieReleaseYears;
    public int ReleasedYr { get; set; }
}

现在在您的 GET 操作中设置这些属性值。

public async Task<IActionResult> Index(int MovieID)
{
    var vm = new MovieGenreViewModel();

    var movie =  _context.Movies.FirstOrDefault(x=>x.ID==movieID);

    if(movie==null)
       return Content("Movie not found"); // to do :Return a view with "not found" message

    IQueryable<string> YearQuery = from m in _context.Years
                                    orderby m.MovieYear
                                    select m.MovieYear;   

    // set the property values. 
    vm .MovieReleaseYears = new SelectList(await YearQuery.ToListAsync());
    vm .MovieId= movie.ID;
    vm .MovieName= movie.Name;

    return View(vm );
}

现在在您看来,您将电影 id 保存在一个隐藏字段中,这样当您提交表单时,它将被提交给处理表单提交的 HttpPost 操作方法。

@model MovieGenreViewModel
@using(Html.BeginForm("AssignYear","Home"))
{
    <p>@Model.MovieName</p>
    @Html.HiddenFor(s=>s.MovieId)
    @Html.DropDownListFor(d=>d.ReleasedYr, Model.MovieReleaseYears)
    <input type="submit"/>
}

您可以使用与 AssignYear 操作方法参数相同的视图模型

[HttpPost]
public ActionResult AssignYear(MovieGenreViewModel model)
{
  // check model.MovieId and model.ReleasedYr
  // to do : Save data and return something
}

如果您不希望仅将视图所需的属性添加到视图模型,但希望显示电影的大量属性,则可以考虑向视图模型添加 Movie 类型的新属性

public class MovieGenreViewModel
{
    public Movie Movie { set;get;}
    public SelectList MovieReleaseYears;
    public int ReleasedYr { get; set; }
}

您现有的 LINQ 语句返回一个元素的集合。您不能将一组电影分配给电影类型的属性。您可以使用 FirstOrDefault() 方法来获取单个对象。

var movie =  _context.Movies.FirstOrDefault(x=>x.ID==movieID);
 //After null check
vm.Movie = movie;

【讨论】:

  • 阅读您建议的解决方案后,我在帖子中添加了更新部分。在我的真实项目中,该模型具有大量属性,我试图找到一条更短的路线来在视图中显示这些属性以及来自不同表(实体)的一些下拉列表。
  • 您想要视图中的所有这些大量属性吗?正如我之前所说,视图模型应该包含视图绝对需要的属性。
  • 不幸的是,客户希望相应的页面显示每个选定 ID 的表(模型)中的所有这些字段(属性) - 以及一些下拉列表
  • 您可以添加Movie 类型的属性并根据需要分配和使用。
  • 这正是我想要做的。但是,当我将该属性分配给您在回复中建议的 LINQ 查询时,我收到错误:Cannot implicitly convert type 'myProject.Models.Movie' to 'System.Linq.IQueryable&lt;myProject.Models.Movie&gt;' 如何解决此错误?看来您的建议与我正在尝试做的非常接近。
【解决方案2】:

为什么不能使用 Movie 作为“myMovie”属性的类型? 然后,您可以使用该对象显示名称,例如 movie.Name。 您还可以将movieId 存储在隐藏字段中,这样当您回发时,您可以在服务器端使用它。 您可以使用 LINQ 从数据库中检索该对象,如下所示:

var qrysingleMovie = _context.Movies.Single(m=> m.ID == movieID);

或者,您可以使用 2 个属性而不是整个对象。一个用于标题,即:字符串 MovieName,另一个用于 id int MovieId

public string MovieName  { get; set; }
public int MovieId  { get; set; }

注意:请记住,使用“Single”意味着您需要将电影保存在数据库中,否则会引发异常。

【讨论】:

  • 我收到错误:Cannot implicitly convert type 'myProject.Models.Movie' to 'System.Linq.IQueryable&lt;myProject.Models.Movie&gt;'
  • 请参阅此处的文档:msdn.microsoft.com/en-us/library/bb155325(v=vs.110).aspx 返回序列的唯一元素,如果序列中不完全有一个元素,则抛出异常。 或者你可以要么使用 SingleOrDefault 如果找不到记录,则基本上返回 null 。 返回序列的唯一元素,如果序列为空,则返回默认值;如果序列中有多个元素,此方法将引发异常。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-10
  • 1970-01-01
  • 1970-01-01
  • 2013-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多