There are times when you will need to show multiple models on a single MVC view. In this post I will show how to accomplish that.
I will be using my Library solution for this tutorial. In this case I will be using the Models related to Movies.
Those models are Movie, Actor, Genre, and Format. These are all tables in the database as well. However, there are also some foreign key tables to handle the one to many relationships that are possible with Movie to Actor, Movie to Genre, Movie to Format and.
The view will be displaying information on the selected movie from my database, including the different actors, the formats I have the movie in, and the different genres the movie fits in.
example
- Title
- A Million Ways To Die In The West
- Rating
- NR
- Stars
- 4.00
- Actors
- Seth MacFarlane
Neil Patrick Harris
Liam Neeson
Sarah Silverman
Charlize Theron
- Formats
- Digital
- Genres
- Comedy
Western
The tables are set up as such
USE [Library]
GO
/****** Object: Table [dbo].[ActorMovies] Script Date: 11/21/2023 3:51:21 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[ActorMovies](
[MovieID] [int] NOT NULL,
[ActorID] [int] NOT NULL
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Actors] Script Date: 11/21/2023 3:51:21 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Actors](
[ActorID] [int] IDENTITY(1,1) NOT NULL,
[FirstName] [nvarchar](30) NOT NULL,
[LastName] [nvarchar](40) NOT NULL,
PRIMARY KEY CLUSTERED
(
[ActorID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Formats] Script Date: 11/21/2023 3:51:21 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Formats](
[FormatID] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[Book] [bit] NOT NULL,
PRIMARY KEY CLUSTERED
(
[FormatID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Genres] Script Date: 11/21/2023 3:51:21 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Genres](
[Genre] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](100) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Genre] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[MovieFormat] Script Date: 11/21/2023 3:51:21 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[MovieFormat](
[MovieID] [int] NOT NULL,
[FormatID] [int] NOT NULL
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[MovieGenres] Script Date: 11/21/2023 3:51:21 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[MovieGenres](
[MovieID] [int] NOT NULL,
[Genre] [int] NOT NULL
) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Movies] Script Date: 11/21/2023 3:51:21 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Movies](
[MovieID] [int] IDENTITY(1,1) NOT NULL,
[Title] [nvarchar](400) NOT NULL,
[Rating] [nvarchar](5) NULL,
[Stars] [decimal](18, 0) NULL,
PRIMARY KEY CLUSTERED
(
[MovieID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[ActorMovies] WITH CHECK ADD CONSTRAINT [FK_ActorMovie] FOREIGN KEY([MovieID])
REFERENCES [dbo].[Movies] ([MovieID])
GO
ALTER TABLE [dbo].[ActorMovies] CHECK CONSTRAINT [FK_ActorMovie]
GO
ALTER TABLE [dbo].[ActorMovies] WITH CHECK ADD CONSTRAINT [FK_MovieActor] FOREIGN KEY([ActorID])
REFERENCES [dbo].[Actors] ([ActorID])
GO
ALTER TABLE [dbo].[ActorMovies] CHECK CONSTRAINT [FK_MovieActor]
GO
ALTER TABLE [dbo].[MovieFormat] WITH CHECK ADD CONSTRAINT [FK_FormatMovie] FOREIGN KEY([FormatID])
REFERENCES [dbo].[Formats] ([FormatID])
GO
ALTER TABLE [dbo].[MovieFormat] CHECK CONSTRAINT [FK_FormatMovie]
GO
ALTER TABLE [dbo].[MovieFormat] WITH CHECK ADD CONSTRAINT [FK_MovieFormat] FOREIGN KEY([MovieID])
REFERENCES [dbo].[Movies] ([MovieID])
GO
ALTER TABLE [dbo].[MovieFormat] CHECK CONSTRAINT [FK_MovieFormat]
GO
ALTER TABLE [dbo].[MovieGenres] WITH CHECK ADD CONSTRAINT [FK_GenreMovie] FOREIGN KEY([MovieID])
REFERENCES [dbo].[Movies] ([MovieID])
GO
ALTER TABLE [dbo].[MovieGenres] CHECK CONSTRAINT [FK_GenreMovie]
GO
ALTER TABLE [dbo].[MovieGenres] WITH CHECK ADD CONSTRAINT [FK_MovieGenre] FOREIGN KEY([Genre])
REFERENCES [dbo].[Genres] ([Genre])
GO
ALTER TABLE [dbo].[MovieGenres] CHECK CONSTRAINT [FK_MovieGenre]
GO
and the Models are set up as such
public class Actor
{
public int ActorId { get; set; }
[Required, Display(Name ="First Name"), MaxLength(30)]
public string FirstName { get; set; }
[Required, Display(Name ="Last Name"), MaxLength(40)]
public string LastName { get; set; }
public string Name { get; set; }
}
public class Format
{
public int FormatID { get; set; }
[Required, MaxLength(50)]
public string Name { get; set; }
[Required]
public bool Book { get; set; }
}
public class Genre
{
[Display(Name ="Genre")]
public int GenreId { get; set; }
[Required, MaxLength(100)]
public string Name { get; set; }
}
public class Movie
{
public int MovieId { get; set; }
[Required,MaxLength(400)]
public string Title { get; set; }
[MaxLength(5)]
public string Rating { get; set; }
public decimal? Stars { get; set; }
public int FormatId { get; set; }
[Display(Name ="Format")]
public string FormatName { get; set; }
}
The first thing we have to do is add some code to the movie model. We need to let the model know that Actor, Genre and Format are collections. This will allow for us to list the information for these on our view.
so we add this to the Movie model
public virtual ICollection<Actor> Actor { get; set; }
public virtual ICollection<Genre> Genre { get; set; }
public virtual ICollection<Format> Format { get; set; }
public Movie()
{
Actor = new HashSet<Actor>();
Genre = new HashSet<Genre>();
Format = new HashSet<Format>();
}
Now the class has the collections for the information so all we need to do is add the method to fill in the list.
public Movie GetMovie(int MovieId)
{
List<Actor> actorList = null;
List<Format> formatList = null;
List<Genre> genreList = null;
using (SqlConnection oConn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
using (SqlCommand oCmd = new SqlCommand())
{
try
{
oCmd.CommandText = "GetMovie";
oCmd.CommandType = CommandType.StoredProcedure;
oCmd.Parameters.Add("@MovieId",SqlDbType.Int).Value = MovieId;
oCmd.Connection = oConn;
oConn.Open();
using (SqlDataReader reader = oCmd.ExecuteReader())
{
Movie movie = new Movie();
while (reader.Read())
{
movie.MovieId = MovieId;
movie.Title = reader[0].ToString();
movie.Rating = reader[1].ToString();
movie.Stars = (decimal)reader[2];
}
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
using (SqlCommand cmd = new SqlCommand())
{
try
{
cmd.CommandText = "ListFormatsByMovie";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@MovieId", SqlDbType.Int).Value = MovieId;
cmd.Connection = conn;
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
formatList = new List<Format>();
while (rdr.Read())
{
Format format = new Format
{
Name = rdr[0].ToString()
};
formatList.Add(format);
}
}
}
catch (Exception ex)
{
Errors.ErrorOccured(ex, "MovieId = " + MovieId);
return null;
}
finally
{
conn.Close();
cmd.Dispose();
}
}
}
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
using (SqlCommand cmd = new SqlCommand())
{
try
{
cmd.CommandText = "ListActorsByMovie";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@MovieId", SqlDbType.Int).Value = MovieId;
cmd.Connection = conn;
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
actorList = new List<Actor>();
while (rdr.Read())
{
Actor actor = new Actor
{
FirstName = rdr[0].ToString(),
LastName = rdr[1].ToString()
};
actorList.Add(actor);
}
}
}
catch (Exception ex)
{
Errors.ErrorOccured(ex, "MovieId = " + MovieId);
return null;
}
finally
{
conn.Close();
cmd.Dispose();
}
}
}
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
using (SqlCommand cmd = new SqlCommand())
{
try
{
cmd.CommandText = "ListGenresByMovie";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@MovieId", SqlDbType.Int).Value = MovieId;
cmd.Connection = conn;
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
genreList = new List<Genre>();
while (rdr.Read())
{
Genre genre = new Genre
{
Name = rdr[0].ToString()
};
genreList.Add(genre);
}
}
}
catch (Exception ex)
{
Errors.ErrorOccured(ex, "MovieId = " + MovieId);
return null;
}
finally
{
conn.Close();
cmd.Dispose();
}
}
}
movie.Format = formatList;
movie.Actor = actorList;
movie.Genre = genreList;
return movie;
}
}
catch (Exception ex)
{
Errors.ErrorOccured(ex);
return null;
}
finally
{
oConn.Close();
oCmd.Dispose();
}
}
}
}
Then it is just call the method from the controller
public ActionResult Details(int?id)
{
try
{
if(id == null)
{
return RedirectToAction("Index");
}
Movie movie = new Movie();
var model = movie.GetMovie((int)id);
return View(model);
}
catch (Exception ex)
{
Errors.ErrorOccured(ex);
}
return RedirectToAction("InternalServerError", "Error");
}
And display in the view
@model Library.Models.Movie
@{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<div>
<h4>Movie</h4>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Title)
</dt>
<dd>
@Html.DisplayFor(model => model.Title)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Rating)
</dt>
<dd>
@Html.DisplayFor(model => model.Rating)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Stars)
</dt>
<dd>
@Html.DisplayFor(model => model.Stars)
</dd>
<dt>
Actors
</dt>
<dd>
@foreach (var actor in Model.Actor)
{
@actor.FirstName <span> </span>@actor.LastName <br />
}
</dd>
<dt>
Formats
</dt>
<dd>
@foreach (var frmt in Model.Format)
{
@frmt.Name <br />
}
</dd>
<dt>
Genres
</dt>
<dd>
@foreach (var genre in Model.Genre)
{
@genre.Name <br />
}
</dd>
</dl>
</div>
OK now you can see how to display multiple models in a single view. I hope this helps.