Last listened to The Getaway by Immortal Technique (on 10 Mar 2010, 14:39)

DataBinding with Anonymous Types in C#

I wanted to use an ASP.NET repeater to bind some data to a web form, but the data in the code came from two different places. I used LINQ to join my two data collections together, but the problem was that this resulted in my query returning an IEnumerable of anonymous types. I thought that this would mean I'm not going to be able to bind these anonymous type objects to my repeater without creating a strongly typed class or struct.

It turns out that I was wrong: I can bind anonymous types to a repeater! That's pretty cool, and I'll show you how I did it. My LINQ query looked like this:

Customer[] customers = GetCustomers();
Country[] countries = GetCountries();

var query = from customer in customers
            join country in countries on customer.CountryID equals country.CountryID
            where customer.Age > 21 &&
                  country.Name == "United Kingdom"
            select new
                       {
                           CustomerName = customer.Name,
                           CountryName = country.Name
                       };

MyRepeater.DataSource = query.ToArray();
MyRepeater.DataBind();

As you can see, I'm using CustomerName from one type of object, and CountryName from another type, and joining these together into a new anonymous type.

After this, in my HTML/ASCX I can use a repeater like this:

<asp:Repeater ID="MyRepeater" runat="server">
    <ItemTemplate>
        <p>
            <%# Eval("CustomerName") %>
            lives in
            <%# Eval("CountryName") %>
        </p>
    </ItemTemplate>
</asp:Repeater>

When you think about it, this actually makes sense. Anonymous types of course are real types - it's just that the compiler generates the type for me, so I don't have to.

I use last.fm and let it's audioscrobbler services keep a track of whatever music I listen to. As you can see, just below the navigation bar on this website I use its API to display the last track I listened to.

Here's what it looks like when it's in action:

It's incredibly easy to add this onto your website too, if you're an ASP.NET developer. Here's how.

Create a new User Control in your website, and call it MostRecentTrack.ascx. In the codebehind file, copy and paste the following:

protected XElement MostRecentTrack { get; set; }

protected void Page_Load(object sender, EventArgs e)
{
    GetLatestTrack();

    if (MostRecentTrack == null)
        LatestTrackHolder.Visible = false;
}


private void GetLatestTrack()
{
    MostRecentTrack = Cache["MostRecentTrack"] as XElement;
    if (MostRecentTrack != null) return;

    string xmlLocation =
        string.Format("http://ws.audioscrobbler.com/1.0/user/{0}/recenttracks.xml",
                      LastFmUsername.Text);

    try
    {
        MostRecentTrack = XDocument.Load(xmlLocation).Descendants("track").First();
        Cache.Insert("MostRecentTrack",
                     MostRecentTrack,
                     null,
                     DateTime.MaxValue,
                     TimeSpan.FromMinutes(10));
    }
    catch
    {
        // Catch everything so that any failure doesn't bring the website down
        MostRecentTrack = null;
    }
}

In the HTML section of your control, put the following code:

<asp:PlaceHolder ID="LatestTrackHolder" runat="server">
    <p class="LatestTrack">
        Last listened to <%= (string) MostRecentTrack.Element("name") %>
        by <%= (string) MostRecentTrack.Element("artist") %>
        (on <%= (string) MostRecentTrack.Element("date") %>)
    </p>
    <asp:Literal ID="LastFmUsername" runat="server"
                 Visible="false"
                 Text="YOUR_USERNAME_HERE" />
</asp:PlaceHolder>

This code uses System.Linq and System.Xml.Linq so don't forget to add the relevant references. Also, don't forget to put your real last.fm username in the hidden Literal in your HTML code. When you've created your control, add it to your page like this:

<%@ Register Src="~/MyUserControls/MostRecentTrack.ascx"
    TagName="MostRecentTrack" TagPrefix="lastFm" %>

<lastFm:MostRecentTrack runat="server" />

I've got it below my navigation menu on my master page, so it shows up on every page.