ADAM & ASP.NET MVC, Part 2

It’s been over a month since my first article about ADAM & MVC, so this one is long overdue! We’ve been working hard on the next version of our platform, but I managed to sneak in a couple of hours to bring you an update.

In my previous article I demonstrated how to set up a new ASP.NET MVC 2 project to work with ADAM, including how authentication can be handled. Assuming you’re all set up now, we’ll continue and actually show the user some assets from the system.

There are many approaches one can take to take information from a data store and put it in the view. For smaller applications, you can simply grab the objects as they come from the store and put them in the view as is, using the same model. A different school of thought would separate these into the data model, the domain model and the view model. Which is the right way? Well, as always, it depends. If you need to add significant functionality to the model that is beyond the scope of ADAM, you may be more interested in creating a domain model that captures your business requirements better, and use ADAM as an object store for all the data. You might even have a completely separate view model if you feel that there is a need for the distinction. For this article, however, I chose simplicity, primarily to keep this article brief.

What do we need for this article? Well, we’ll need a controller that acts on assets and that controller will need an instance of Application so it can get to the data. To make our life a bit easier, I decided to make a custom base controller, AdamController:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class AdamController : Controller
{
    private readonly Application _Application;

    public AdamController()
    {
        this._Application = Authentication.TryLogOnWithAuthenticationTicket();
    }

    protected Application Application
    {
        get
        {
            return this._Application;
        }
    }

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (this.Application == null)
        {
            filterContext.Result = new HttpUnauthorizedResult();
            return;
        }

        base.OnActionExecuting(filterContext);
    }
}

This is a pretty simple class since we implemented the required functionality last time. The only think worth mentioning is that on each action that is executing, we check to make sure that acquiring the Application worked. This is because we want to make sure that the user is logged in and we can actually get to the data, and the AuthorizeAttribute is not enough because the FormsAuthentication session may still be valid while the ADAM session is not. You can omit this override, but then you’d have to check the Application property for null values in each action, which I want to avoid right now.

Next, we’ll make our first controller, and have it return something interesting. Behold the AssetController class:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Authorize]
public class AssetController : AdamController
{
    //
    // GET: /Asset/

    public ActionResult Index()
    {
        var records = new RecordCollection(this.Application);
        records.Load();

        return View(records);
    }
}

Also, this is still pretty easy. We’re using the Core API to get us a RecordCollection with some data in it (actually, this loads all of the records in our database) and return it. Since our ADAM entities are just objects, we can pass them to the view as is. Here’s our view:

ASP.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Adam.Core.Records.RecordCollection>" %>
<%@ Import Namespace="Adam.Core.Records" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
 Index
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Index</h2>

    <ul>
    <%
        foreach (Record record in this.Model)
        {
            var field = record.Fields.GetField<Adam.Core.Fields.TextField>("AdamTV_Title");
    %>

        <li>
            <%=this.Html.Encode(field != null ? field.Value : "N/A")%>
            - <%=this.Html.ActionLink("Details", "Details", new {id = record.Id})%>
            - <%=this.Html.ActionLink("Download", "Download", new {id = record.Id})%>
        </li>

    <%  }   %>
    </ul>

</asp:Content>

As you can see, that’s a lot of work just to get the title field of the asset. This is because ADAM of course has a pretty detailed model that allows us to put in different types of fields, and each field can have multiple values for each language that is defined in the system. Yet, it doesn’t have to clog up our view as much like this. We can write an HTML helper to assist us:

C#
1
2
3
4
5
6
7
8
9
public static class HtmlHelpers
{
    public static string TextField(this HtmlHelper helper, FieldContainerCollection fields, string name)
    {
        var field = fields.GetField<TextField>(name);

        return field != null ? field.Value : "N/A";
    }
}

HTML Helpers are used throughout the MVC framework, but are really just extension methods on the HtmlHelper class that produce HTML output. Now, our view looks more like this:

ASP.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Adam.Core.Records.RecordCollection>" %>
<%@ Import Namespace="Adam.Core.Records" %>
<%@ Import Namespace="AdamMVC" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
 Index
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Index</h2>

    <ul>
    <%
        foreach (Record record in this.Model)
        {%>

        <li>
            <%=this.Html.Encode(this.Html.TextField(record.Fields, "AdamTV_Title"))%>
            - <%=this.Html.ActionLink("Details", "Details", new {id = record.Id})%>
            - <%=this.Html.ActionLink("Download", "Download", new {id = record.Id})%>
        </li>

    <%  }%>
    </ul>

</asp:Content>
I’m linking to actions that don’t exist yet. Of course, I was going to show them to you:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public ActionResult Details(Guid id)
{
    var record = new Record(this.Application);
    record.Load(id);

    return View(record);
}

public ActionResult Preview(Guid id)
{
    var record = new Record(this.Application);
    record.Load(id);

    return File(record.GetPreview().GetPath(), "image/jpeg");
}

public ActionResult Download(Guid id)
{
    var record = new Record(this.Application);
    record.Load(id);

    FileVersion file = record.Files.LatestMaster;

    var type = new FileType(this.Application);
    type.Load(file.FileTypeId);

    string path = file.GetPath(null);
    var result = new FilePathResult(path, type.MimeType);
    result.FileDownloadName = Path.GetFileName(path);
    return result;
}

Actions that return files are pretty easy to do in ASP.NET MVC, as you can see. The last one is a bit more complex since I want to return the correct MIME type and I also want the returned filename to be correct, or the user would be downloading a file with the id of the asset as name.

Lastly, I give you the view that goes with the Details action:

ASP.NET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Adam.Core.Records.Record>" %>
<%@ Import Namespace="AdamMVC" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
 Details
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Details</h2>

    <dl>
        <dt>Title:</dt>
        <dd><%=this.Html.Encode(this.Html.TextField(this.Model.Fields, "AdamTV_Title"))%></dd>
    </dl>    

    <dl>
        <dt>Description:</dt>
        <dd><%=this.Html.Encode(this.Html.TextField(this.Model.Fields, "AdamTV_Description"))%></dd>
    </dl>
    
    <p>Preview:</p>
    <img src="<%=this.Url.Action("Preview", new {id = this.Model.Id})%>" alt="Preview" />
    
    <p>
        <%=this.Html.ActionLink("Download", "Download", new {id = this.Model.Id}, null)%>
        (Size: <%=this.Model.Files.Master.Versions.Latest.Filesize / 1024%> KB)
    </p>

</asp:Content>

Do note that the Index action returns all of the assets, so don’t run this on a particularly large production database.

By now you should be familiar with fetching records from the ADAM database and putting them in views. As stated before, this is only a starting point. Nothing prevents you from abstracting the ADAM objects away and using your own view model or domain model, especially if you need to integrate with other datasources as well or if there are other requirements that force you to do this.

Next time, I’ll take on searching and paging.

Sample Code

The article contains sample code project(s).
You must be logged in to view or download sample code.
Sign in now

Comments

Leave a comment
You must be logged in to post comments.
Sign in now
 
 
Technical
Business
rss feed