The .NET MVC Framework – Part 3 – Linking Models to Views and More!
This is the third part of the series looking at rolling out a new Microsoft .NET MVC application. I’ve taken an existing application, the prototype photo gallery I use for this blog (http://photos.tiredstudent.com) and am recreating it (with some extras) inside of the MVC framework—it’s a bit more fun than dinking with Northwind. 🙂
With that, let’s take a look at creating a data model and linking data to our controllers and then to our views. This part should fill out our Galleries controller quite nicely.
Adding a Data Model
For these examples, I’m going to use LINQ to SQL. You could just as easily use the new Entity Framework/Astoria, NHibernate, Castle, or any other you wish. I like LINQ. 🙂
I’ve pulled down a copy of the existing Photos database, so the examples you see here match what’s in production on the blog’s photo gallery. The first step is to create a LINQ-to-SQL file for the Web Gallery. It’s best, in my opinion, to place these files under the ~/Models directory since, in essence, that’s what they will become.
The two primary tables for the gallery are Galleries and WebFiles. Dragging those two onto the DBML file results in:
I’ll create a quick unit test example—I’ll assume the rest is up and going as these are no different in the MVC framework as they are in any other project. 🙂
For the example, I’ll check that my GetGalleries method returns a non-null result:
public class GalleriesModelTest
WebGalleryDataContext db = new WebGalleryDataContext();
public void GetGalleries_ReturnsAllGalleries()
List<Gallery> galleries = db.GetGalleries();
Okay, that fails; which is what it is supposed to do—since my GetGalleries method doesn’t exist yet. I want the methods to show up in side of my existing model and, thankfully, the LINQ-to-SQL classes are partial classes.
If I add a new class called WebGalleryDataContext (the same as the existing DataContext) under my model and mark it as partial, the methods will merge across classes to create a single class. It is a brilliant and extremely useful concept for extending existing classes like those created by the DBML generator.
Our GetGalleries method looks like the code snippet below. We’ll use that to build up on our Index action on the galleries controller.
public partial class WebGalleryDataContext
public List<Gallery> GetGalleries()
With that done, we’re ready to tackle creating a view and attaching some data to it—or creating an actual web page! 🙂
Creating a Gallery List with ViewData
On the first post, I created the new views for the Galleries controller; however, I didn’t do anything with them at that time—simply rendering was enough. Now, however, we want to see REAL data on those pages.
Important: There is a glitch in the current CTP (Dec 2007) of the ASP.NET 3.5 Extensions. The MVC Content View and View pages are not generating .designer files—and therefore the code behind cannot access the controls on the page. To fix this, when you create a new view that will have .NET controls on it, right click the view and click “Convert to Web Application.”
I’m not going to debate whether or not you should use strict HTML and leave code-behind out of the equation OR use ASP.NET controls and carefully monitor your own code to ensure that you’re only writing presentation logic. That’s each developer’s individual battle (or with their boss).
In my case, I see value in both. Simple iterations and layouts lend themselves well to what, quite frankly, looks like legacy ASP code. More complex data views, forms, and such lend themselves to controls and code-behind. That’s my opinion. 🙂
For our Galleries/Index action, I’d like to list out the galleries, using the GetGalleries method we created earlier, and create HTML links to Show() the gallery. To do this, use simple inline code. Why? I’m not formatting any specific data; simply generating an unordered list of links. There’s no reason to “over code” it.
<% foreach (Gallery gallery in ViewData)
(i => i.Show(gallery.Name), gallery.Name)%>
<% } %>
In our code-behind, we need to do two things.
First, tell our page what type of ViewData to expect. This is done by modifying the generic base class that each MVC View Page inherits from. In this case, we’ll pass a List<Gallery> object to the view, so that’s what we need to tell it to expect.
Second, if we plan to use any server-side controls (ASP.NET controls) on our page, we need to add a Page_Load() method to the page. Add this inside the partial class declaration of your page.
public void Page_Load()
Notice: At the time of this writing, I can’t get the ActionView to accept an Eval(“Value”) statement. I’m honestly not sure why. As soon as I figure it out (or if someone tells me ;)), I’ll post up an update.
Save and run, and we get:
Creating the Photo Gallery with ViewData
For the last example, we need to implement a third controller, the WebFilesController. For a lack of better names at the time, WebFiles are the domain objects for the photos, documents, and other… uhh.. web files stored within each gallery.
This Controller, however, won’t specify any views—since we’re simply rendering out binary objects (photos, documents, etc).
Information: Quite honestly, I’m not sure if the “viewless” controllers are intended or not—it works. I’m sure someone will come along and note what we “should” and “shouldn’t” be doing for the sake of best practices in the future.
public void Show(int id)
WebFile webFile = db.GetWebFileById(id);
Response.ContentType = webFile.ContentType;
The result allows me to call /WebFiles/Show/16 and the response pipes out the image. I’ve also created a Controller Action called ShowThumbnail to generate a thumbnail and return it to the page in a similar fashion.
Now that we can generate thumbnails, we’re ready to create our gallery.
On the Galleries/Show.aspx view, let’s add a quick server-side control—the DataList. I like the DataList control because it allows me to control the number of columns displayed, custom styles, and formatting.
<asp:DataList runat=”server” ID=”WebFileList” RepeatColumns=”3″ Width=”100%”>
<p style=”text-align: center;“>
src=”/WebFiles/ShowThumbnail/<%# Eval(“Id”) %>“
alt=”<%# Eval(“Name”) %>“ />
<%# Eval(“Name”) %>
As I mentioned earlier, the MvcToolkit’s ActionLink and Image helper methods aren’t working for me whenever I use the Eval statements—I’m still researching why, but, for now, this works out quite well.
Important: Don’t forget to set the ViewPage parameters on the MVC View’s code behind and, if you use .NET controls, add the necessary Page_Load event and control parameters.
For this page, I’ll need to attach the ViewData to the DataSource property of our DataList. Once I’ve specified the type of object in the ViewPage, full Intellisense picks up—and I can specify ViewData.WebFiles since ViewData is a Gallery object and WebFiles (through the relationship in the tables) are child records of a gallery.
public partial class Show : ViewPage<Gallery>
public void Page_Load()
WebFileList.DataSource = ViewData.WebFiles;
Our final product looks something like: