Building Applications with Anonymous Types

In this third part of a three-part series on anonymous types, you'll learn how to combine them with other technologies to make such useful applications as a stock ticker. This article is excerpted from chapter one of LINQ Unleashed, written by Paul Kimmel (Sams, 2008; ISBN: 0672329832).

Contributed by
Rating: 5 stars5 stars5 stars5 stars5 stars / 2
November 17, 2009
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

Databinding Anonymous Types

Some interesting startups got blown up when the stock market bubble burst, such as PointCast. PointCast searched the web—based on criteria the user provided—and displayed stock prices on a ticker and news in a browsable environment. One of the possible kinds of data was streaming stock prices. (Thankfully, the 1990s day-trading craze is over, but the ability to get such data is still interesting.)

This section looks at how you can combine cool technologies, such as anonymous types, AJAX, HttpWebRequests, HttpWebResponses, and queries to Yahoo!’s stock-quoting capability, and assemble a web stock ticker. Aside from the code, a demonstration of data-binding anonymous types, and a brief description of what role the various technology elements are playing, this book doesn’t elaborate in detail on features like AJAX (because of space and topic constraints). (For more information on web programming, see Stephen Walther’s ASP.NET 3.5 Unleashed.)

The sample (in Listing 1.11) is actually very easy to complete, but uses some very cool technology and plumbing underneath. In the solution, a website project was created. The application contains a single .aspx web page. On that page, a ScriptManager, UpdatePanel (both AJAX controls), a DataList, Label, and AJAX Timer are added. The design-time view of the page is shown in Figure 1.4 and the runtime view is shown in Figure 1.5. (Listing 1.12 shows the settings for the Web controls.)


Figure 1-4.  Just five controls and you have an asynchronous AJAX page.


Figure 1-5.  A very simple design but the code is actually updating the stock prices every 10 seconds with that postback page flicker.

Because of anonymous types, the code to actually query the stock process from Yahoo! is very short (see Listing 1.11).

LISTING 1.11  This Code Uses HttpWebRequest and HttpWebResponse to Request Stock Quotes from Yahoo!

using System;
using System.Data;
using System.Diagnostics;
using System.Configuration;
using System.Collections;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls;
using System.Xml.Linq;
using System.Web.Services ;
using System.Net;
using System.IO;
using System.Text;

namespace DataBindingAnonymousTypes
{
  public partial class _Default : System.Web.UI.Page
  {
    protected void Page_Load(object sender, EventArgs e)
    {
      Update();
    }

    private void Update()
    {
      var quote1 = new {Stock="DELL", Quote=GetQuote("DELL")};
      var quote2 = new {Stock="MSFT", Quote=GetQuote("MSFT")};
      var quote3 = new {Stock="GOOG", Quote=GetQuote("GOOG")};

      var quotes = new object[]{ quote1, quote2, quote3 };
      DataList1.DataSource = quotes;
      DataList1.DataBind();
      Label3.Text = DateTime.Now.ToLongTimeString();
    }

    protected void Timer1_Tick(object sender, EventArgs e)
    {
        //Update();
    }

    public string GetQuote(string stock)
    {
      try
      {
       
return InnerGetQuote(stock);
      }
      catch(Exception ex)
      {
        Debug.WriteLine(ex.Message);
        return "N/A";
      }
    }

    private string InnerGetQuote(string stock) 
    {
      string url = @"http://quote.yahoo.com/d/quotes.csv?s={0}&f=pc";
      var request = HttpWebRequest.Create(string.Format(url, stock));

      using(var response = request.GetResponse())
      {
        using(var reader = new StreamReader(response.GetResponseStream(), 
          Encoding.ASCII))
        {
          return reader.ReadToEnd();
        }
      }
    }
  }
}

The method InnerGetQuote has a properly formatted uniform resource locator (URL) query for the Yahoo! stock-quoting feature. Next, an HttpWebRequest sends the URL query to Yahoo! Then, the HttpWebResponse—returned by request.GetResponse—is requested and a StreamReader reads the response. Easy, right?

All of this code is run by the Update method. Update creates anonymous types containing a Stock and Quote field (which are populated by the GetQuote and InnerGetQuote methods). An anonymous array of these quote objects is created and all of this is bound to the DataList. The DataList itself has template controls that are data bound to the Stock and Quote fields of the anonymous type. Figure 1.6 shows the template design of the DataList. The very easy binding statement is shown in Figure 1.7.


Figure 1-6.  The template view of the DataList is two Label controls and the | character.


Figure 1-7.  The binding statements for bound template controls have been very short (as shown) since Visual Studio 2005.

All of the special features, such as template editing and managing bindings, are accessible through the DataList Tasks button, which is shown to the right of the DataList in Figure 1.4. You can also edit elements such as binding statements directly in the ASP designer. Listing 1.12 shows the ASP/HTML for the web page.

LISTING 1.12  The ASP That Creates the Page Shown in Figure 1.4 (Design Time) and Figure 1.5 (Runtime)

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs"
➥Inherits="DataBindingAnonymousTypes._Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
➥ “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

 

<html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <div>

    </div>
    <asp:UpdatePanel ID="UpdatePanel1" runat="server" EnableViewState="False">
      <ContentTemplate>
        <asp:DataList ID="DataList1" runat="server" RepeatDirection="Horizontal">
          <itemtemplate>
            <asp:Label ID="Label1" runat="server" Text=’<%# Bind("Stock") %>’>
            ➥</asp:Label>
            &nbsp;<asp:Label ID="Label2" runat="server" Text=’<%# Bind("Quote") %>’>
            ➥</asp:Label>
            &nbsp;|
          </itemtemplate>
        </asp:DataList>
        <asp:Label ID="Label3" runat="server" Text="Label"></asp:Label>
      </ContentTemplate>
      <triggers>
        <asp:asyncpostbacktrigger ControlID="Timer1" EventName="Tick" />
      </triggers>
    </asp:UpdatePanel>
    <asp:Timer ID="Timer1" runat="server" Interval="10000" ontick="Timer1_Tick">
    </asp:Timer>
    </form>
</body>
</html>

 

The really neat thing about this application (besides getting stock quotes) is that the postbacks happen transparently with AJAX. The way AJAX works is that an asynchronous postback happens and all of the code runs except the part that renders the new page data. Instead, text is sent back and JavaScript updates small areas of the page.

The underlying technology for AJAX is an XHTMLRequest, and this technology in its raw form has been around for a while. But, the raw form required wiring up callbacks and spinning your own JavaScript. You can still handcraft AJAX code of course, but now there are web controls, such as the UpdatePanel and Timer, that take care of the AJAX plumbing for you.

The elements that initiate the AJAX behavior are called triggers. Triggers can really be any postback event. Listing 1.12 uses the AJAX Timer’s Tick event. (And, if you want this to actually look like a ticker, play with some styles and add some color.)

Testing Anonymous Type Equality

Anonymous type equality is defined very deliberately. If any two or more anonymous types have the same order, number, and member declaratory type and name, the same anonymous type class is defined. In this instance, it is permissible to use the referential equality operator on these types. If any of the order, number, and member declarator type and name is different, a different anonymous type definition is defined for each. And, of course, testing referential integrity produces a compiler error.


NOTE

It is possible to use reflection to get type information about anonymous types, and you might want to do this, occasionally, for anonymous types returned from methods. However, the actual name of the anonymous type can vary between compilations, so devising a way to use the class name probably has no reliable uses.


If you want to test member equality, use the Equals method (defined by all objects). Anonymous types with the same order, type, and name, type, and value of member declarators also produce the same hash; the hash is the basis for the equality test. Listing 1.13 provides some samples of anonymous types followed by equality tests and comments indicating those that produce the same anonymous types and those that have member-wise equality.

LISTING 1.13  Various Anonymous Types with Annotations

var audioBook = new {Artist="Bob Dylan",
  Song="I Shall Be Released"}; // anonymous type 1
var songBook1 = new {Artist="Bob Dylan",
  Song="I Shall Be Released"}; // also anonymous type 1
var songBook2 = new {Singer="Bob Dylan",
  Song="I Shall Be Released"}; // anonymous type 2
var audioBook1 = new {Song="I Shall Be Released",
  Artist="Bob Dylan"}; // anonymous type 3

audioBook.Equals(songBook1);       // true everything the same
audioBook.Equals(songBook2);     // first member declarators different
songBook1.Equals(songBook2);     // member declarator-names differ
audioBook1.Equals(audioBook);     // member declarators in different orders

The anonymous types audioBook and songBook1 produce the same anonymous type. These are the only two that produce the same hash and, as a result, the Equals method returns true. The other anonymous types are similar, but either the member declarators are different—songBook1 uses the member declarator Artist and songBook2 uses Singer—or the order of the declarators are different—referring to audioBook and audioBook1.

Using Anonymous Types with LINQ Queries

The most significant attribute of anonymous types in conjunction with LINQ is that they support hierarchical data shaping without writing all of the plumbing code or resorting to SQL. Data shaping is roughly transforming data from one composition to another. LINQ lets you do this with natural queries, and anonymous types give you a place to store the results of these queries.

This whole book is about LINQ, so Listing 1.14 shows a couple of LINQ examples without getting too far ahead in upcoming chapter material. Again, each example also has a brief description.

LISTING 1.14  A Couple of Simple LINQ Queries to Play With Demonstrating Future Topics Such as Sorting and Projections

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AnonymousTypeWithQuery
{
  class Program
  {
    static void Main(string[] args)
    {
      var numbers = new int[] {1, 2, 3, 4, 5, 6, 7};
      var all = from n in numbers orderby n descending select n;

      foreach(var n in all)
        Console.WriteLine(n);

      var songs = new string[]{"Let it be", “I shall be released"};
      var newType = from song in songs select new {Title=song};

      foreach(var s in newType)
        Console.WriteLine(s.Title);

      Console.ReadLine();
    }
  }
}

The first query—from n in numbers orderby n descending select n—sorts the integers 1 to 7 in reverse order and stuffs the results in the anonymous type all. The second query—from song in songs select new {Title=song}—shapes the array of strings in songs to an enumerable collection of anonymous objects with a property Title. (The second example takes an array of strings and shapes it into an array of objects with a well-named property.)

Introducing Generic Anonymous Methods

For newer programmers, word reuse can be confusing. For example, anonymous methods are unrelated to anonymous types except to the extent that it means the type of the method is unnamed. Anonymous methods are covered in this section because they are valuable and worth covering, but, for the most part, this section switches topics.

Anonymous methods behave like regular methods except that they are unnamed. They were introduced as an alternative to defining delegates that did very simple tasks, where full-blown methods amounted to more than just extra typing. Anonymous methods also evolved further into Lambda Expressions, which are even shorter (terse) methods. Chapter 5, “Understanding Lambda Expressions and Closures,” delves deeper into the evolution of methods. For now, this section takes an introductory look at anonymous generic methods.

An anonymous method is like a regular method but uses the delegate keyword, and doesn’t require a name, parameters, or return type. Listing 1.15 shows a regular method (used as a delegate for the CancelKeyPress event, Ctrl+C in a console application) and an anonymous delegate that performs the same role.

LISTING 1.15  A Regular Method and Anonymous Method Handling the CancelKeyPress Event in a Console Application

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AnonymousMethod
{
  class Program
  {
   
static void Main(string[] args)
   
{
      // ctrl+c
      Console.CancelKeyPress += new ConsoleCancelEventHandler
        (Console_CancelKeyPress);

      // anonymous cancel delegate
      Console.CancelKeyPress +=
        delegate

        {
          Console.WriteLine("Anonymous Cancel pressed");
        };

      Console.ReadLine();

    }

    static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) 
    {
      Console.WriteLine("Cancel pressed");
    }
  }
}


TIP

To quickly stub out an event-handling method, type the object.eventname, the += operator, and press the Tab key twice.


The regular method (used as a delegate) is named ConsoleCancelEventHandler. Although the double-Tab trick generates these stubbed delegates for you, they are overkill for one-line event handlers. The second statement that begins with the Console.CancelKeyPress += delegate demonstrates an anonymous method (delegate) that is equivalent to the longer form of the method. Notice that because the parameters in the delegate aren’t used, they are omitted from the anonymous delegate. You have the option of using the parameter types and names if they are needed in the delegate.

Using Anonymous Generic Methods

Delegates are really just methods that are used (mostly) as event handlers. Generic methods are those that have parameterized types. (Think replaceable data types.) Therefore, anonymous generic delegates are anonymous methods that are associated with replaceable parameterized types. A very useful type is Func<T> (and Func<T, T1, ... Tn>, demonstrated in Listing 1.16). This generic delegate (defined in the System namespace) can be assigned to delegates and anonymous delegates with varying return types and parameters, which makes it a very flexible delegate holder.

LISTING 1.16  Demonstrating How to Use System.Func to Define an Essentially Nested Implementation of the Factorial Function

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AnonymousGenericDelegate
{
  class Program
  {
   
static void Main(string[] args)
    {
     
System.Func<long, long> Factorial =
        delegate(long n)
        {
         
if(n==1) return 1;
          long result=1;
          for(int i=2; i<=n; i++)
           
result *= i;
          return result;
        };

      Console.WriteLine(Factorial(6));
      Console.ReadLine();
    }
  }
}

For all intents and purposes, Factorial is a nested function. Listing 1.16 used Func<long, long>, where the first long parameter represents the return type and the second is the parameter. Notice that the listing also used a named parameter for the anonymous delegate.

Implementing Nested Recursion

Now, you can have a little fun bending and twisting the Factorial function to use recursion. The challenge is that the named delegate is not named until after the delegate definition—the name being Factorial. Hence, you can’t use the name in the anonymous delegate itself, but you can make it work.

There is a class called StackFrame. StackFrame permits getting methods (and information from the call stack) and you can use this class and reflection to invoke the anonymous delegate recursively. (This code is obviously esoteric—referred to this as programmer esoterrorism—but it is fun and demonstrates a lot of features of the framework in a little bit of space, as shown in Listing 1.17.)

LISTING 1.17  Nested, Recursive Anonymous Generic Methods—as a Routine Practice

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace AnonymousGenericRecursiveDelegate
{
  class Program
  {
    static void Main(string[] args)
    {
     
Func<long, long> Factorial =
        delegate(long n)
        {
          return n > 1 ?
            n * (long)(new StackTrace()
            .GetFrame(0).GetMethod().Invoke(null, new object[]{n-1}))
            : n;
       
};

      Console.WriteLine(Factorial(6));
      Console.ReadLine();
    }
  }
}

Again, writing code like the Factorial delegate in Listing 1.17 is only fun for the writer, but elements of it do have utility. For example, anonymous delegates like the Factorial can be useful for one-time, simple event handling. Assigning behaviors to the Func<T> delegate type effectively makes nested functions and reusable delegates that can be passed as arguments, a very dynamic way to program. Getting the StackFrame can be a great way to create a utility that tracks function calls during debugging—like writing the StackTrace to the Debug window in a way that is useful to you—and reflection has many uses.

Reflection can be useful for dynamically loaded assemblies, as demonstrated by NUnit and Visual Studio’s unit testing.

Summary

This chapter examined anonymous types in detail. Anonymous types are strong types where the compiler does the work of figuring out the actual type and writing the class implementation, if the anonymous type is a composite type.

As you see anonymous types used throughout the book for query results, remember anonymous types are immutable, the same type is code generated if the member declaratory—field name—type, number, and order are identical.

blog comments powered by Disqus
DATABASE ARTICLES

- How To Install DotNetNuke with MySQL
- Manage Projects with SQL Server Management S...
- Query Editing and Regular Expressions with S...
- Using SQL Server Management Studio Tools
- SQL Server Management Studio
- Exporting a MySQL Database to Excel Using OD...
- Controlling Databases with SQL Server 2005 D...
- Using Recovery Models with SQL Server 2005 D...
- Handling Database Properties for the SQL Ser...
- Managing Permissions with the SQL Server 200...
- SQL Server 2005 Database Engine Security
- Administering SQL Server 2005 Database Engine
- Building Applications with Anonymous Types
- A Closer Look at Anonymous Types
- Programming with Anonymous Types

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 6 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials