Blog Posts Tagged Linq

Using Ext JS Grids with ASP.Net MVC - Basic Example

Following my post "Ext JS JsonStore and Linq to JSON Gotcha" I've had a few people asking for a more complete example of how to use Ext JS Grids and data stores (in particular the JsonStore) with ASP.Net MVC and LINQ to SQL. Since writing that blog post I've learnt a lot more about the ASP.Net MVC framework and the Ext JS framework so I thought it would be helpful to publish a complete working demo. So, here's an example that displays a simple Ext JS Grid populated from the Northwind database Products table. The example shows how to use an Ext JS Grid with a JsonStore that supports server side paging and sorting.

Download this example as a working Visual Studio 2008 MVC (Version 1.0) application.

Requirements

Overview

So take a look at the code. It's very simple and straightforward! Here's what you should see when you run the project.

MVC ExtJS Grid screen shot

In brief the Index action method of the Products controller handles a vanilla HTTP GET request for the Products index page (/Products/). The view it returns is an almost empty page that simply exposes an element that we can render the Ext JS GridPanel to.

When the index page loads, the load event creates the Ext JS components needed including the JsonStore. It then renders the grid and loads the data store thus triggering an AJAX request to the same URL (this time as a POST, which is the only method supported by the JsonStore component). A different Index action method in the controller handles the POST and if it's an Ajax request it pulls the products from the database and returns JSON in a format expected by the JsonStore.

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Index(int Skip, int Take, string SortDir, string Sort) 
{ 
  if (Request.IsAjaxRequest()) 
  { 
    var products = productsRepository.GetAllProducts(); 

    var results = (new 
    { 
      totalRecords = products.Count(), 
      products = products.OrderBy(Sort + " " + SortDir).Skip(Skip).Take(Take) 
    }); 

    return Json(results); 
  } 
  else 
  { 
    return RedirectToAction("Index"); 
  } 
}

Some Points of Interest

Where I define the JsonStore component in the ext-lib.js file you will notice I have re-defined the default paging parameter names to match my own code behind. Also notice I am redefining some JsonReader parameters (totalProperty and root) to match the JSON data returned by the controller action method. These steps aren't strictly necessary for this example but I have included it for reference. Alternatively you could use the default Ext JS parameter names and ensure your c# code matches.

Northwind.ProductStore = function(config) {
    Northwind.ProductStore.superclass.constructor.call(this, Ext.apply({
        paramNames: {
            "start": "Skip",
            "limit": "Take",
            "sort": "Sort",
            "dir": "SortDir"
        },
        totalProperty: 'totalRecords',
        root: 'products',
        remoteSort: true,
        autoLoad: false,
        id: "ProductID",
        method: "POST"
    }, config));
};

The config parameter of the ProductStore constructor above is used to allow us to pass in additional, or override the existing, configuration parameters of the component. 

Also in ext-lib.js file you will notice a column renderer method. This is a helper function called during  grid rendering to format the data in certain columns. When the columns are specified in the ProductColumnModel you will see I have configured the Stock column to use the stockRenderer method to render the column data.

The stockRenderer method shows how you can reference the data row currently being rendered by the grid and manipulate the HTML rendered in the grid cell. Here I simply add a style to any products that have a stock level less than the reorder level and that are also discontinued.

The column renderer for the Price column uses a built in formatter to render the data as a currency.

The Discontinued column uses a special Ext JS column type to render the boolean value.

Summary

Well, I hope you have found this interesting and helpful. I'm hoping to expand on some of the concepts here and provide examples of some more advanced capabilities and features I have implemented using the ASP.Net MVC and Ext JS frameworks.

Published: Tuesday, 21 July 2009 04:48 PM by Steve

Tags: MVC Ext JS ASP.Net Linq

Comments: 8 Comments

Integrating a Blogger Atom Feed into an ASP.Net MVC Application

Part One: Configure your Blogger Settings, Consuming Atom feeds with ASP.Net

Part Two: Configure your MVC Application - Routes, Controllers... ACTION!

Part Three: Create a Tag Cloud (Display all current labels with post counts)

Part Four: Create Archive Links

Part Five: Add Blog Search Capabilities

Part Six: Integrating Comments

Part Seven: Disadvantages to this approach and how to improve things

In this series of posts I'm going to describe how to integrate an atom feed into an ASP.Net MVC application based on my experience integrating my Blogger blog into the www.iqode.com site.

In this example I'm going to publish the blog to my server using the Blogger FTP publishing feature. You could simply consume a remote atom feed and display it in your MVC application but I want to create a much tighter integration. FTP Publishing throws up some interesting challenges when integrating into an MVC application and I'm going to elaborate more on this in my next post.

Configure your Blogger Settings

I'll assume you already have a Blogger blog, if not then it only takes a few minutes to sign up to Blogger and make your first post.

OK, now you're signed into Blogger you need to go to your Blogger Settings tab aand follow these instructions on setting up FTP Publishing.

Now go to your Archiving tab and, for now, set Archiving setting to No Archive. Scroll down and make sure Enable Post Pages is set to "Yes". Save your settings and move on to the Site Feed tab and complete the relevant Site Feed values. You also need to set the Blog Posts Feed drop down to "Full" then save your settings.

Now you have your blog set up and you're publishing your blog files and full feeds to your own server via FTP. Now open your MVC application in Visual Studio.

Create Your Model Object

An examination of the Atom feed xml will reveal the following structure:

<feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
    <id>tag:blogger.com,1999:blog-8097474</id>
    <updated>2008-09-16T16:53:51.743+01:00</updated>
    <title type='text'>IQODE Limited</title>
    <subtitle type='html'>ATTRACTIVE, INTUITIVE AND ACCESSIBLE WEBSITES, BUILT WITH WEB STANDARDS</subtitle>
    <link rel='alternate' type='text/html' href='http://www.iqode.com/Blog/Browse/'/>
    <link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8097474/posts/default'/>
    <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.iqode.com/Feeds/atom.xml'/>
    <author>
        <name>Steve</name>
        <uri>http://www.blogger.com/profile/15887521996067704936</uri>
        <email>noreply@blogger.com</email>
    </author>
    <generator version='7.00' uri='http://www.blogger.com'>Blogger</generator>
    <openSearch:totalResults>2</openSearch:totalResults>
    <openSearch:startIndex>1</openSearch:startIndex>
    <openSearch:itemsPerPage>25</openSearch:itemsPerPage>
    <entry>
        <id>tag:blogger.com,1999:blog-8097474.post-2242729009616470352</id>
        <published>2008-08-21T18:09:00.002+01:00</published>
        <updated>2008-09-15T14:29:52.307+01:00</updated>
        <category scheme='http://www.blogger.com/atom/ns#' term='News'/>
        <title type='text'>New IQODE Website Goes Live</title>
        <content type='html'>&lt;h4&gt;"Sometimes you've just got to let it go and get it out there"&lt;/h4&gt;&lt;p&gt;A comment from the audience during the Q&amp;amp;A session after &lt;a class="external" href="http://skillswap-brighton.org/2008/07/28/skillswap-goes-pretty/"&gt;the latest Brighton Skillswap&lt;/a&gt; persuaded me to finally publish the latest IQODE website.&lt;/p&gt;&lt;p&gt;So without further ado I give you... oh, you're already here.&lt;/p&gt;</content>
        <link rel='alternate' type='text/html' href='http://www.iqode.com/Blog/Browse/2008/08/new-iqode-website-goes-live.html' title='New IQODE Website Goes Live'/>
        <link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8097474&amp;postID=2242729009616470352&amp;isPopup=true' title='0 Comments'/>
        <link rel='replies' type='application/atom+xml' href='http://www.iqode.com/Feeds/atom.xml' title='Post Comments'/>
        <link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8097474/posts/default/2242729009616470352'/>
        <link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8097474/posts/default/2242729009616470352'/>
        <author>
            <name>Steve</name>
            <uri>http://www.blogger.com/profile/15887521996067704936</uri>
            <email>noreply@blogger.com</email>
        </author>
    </entry>
</feed>

So to model a blog post I'm going to need the following Model Object so right click your Model folder and add a new class and the following class definition.

public class BlogPost
{
    public string Title { get; set; }
    public string LinkTitle { get; set; }
    public DateTime Published { get; set; }
    public string Url { get; set; }
    public List<string> Tags { get; set; }
    public string Content { get; set; }
    public string Author { get; set; }
}

Read the Feed Using Linq to XML

Next, Create a method to read in the feed xml, transform it into model objects and return a generic list of BlogPost objects. Linq is an excellent tool to achieve this in a very few lines of code...

protected IEnumerable<BlogPost> GetPosts()
{
    var posts = HttpRuntime.Cache["IQODEFeed"] as IEnumerable<BlogPost>;
    if (posts == null)
    {
        string feedPath = Server.MapPath(IQODEWebLib.Config.Current.BlogFeedURL);
        XDocument rssFeed = XDocument.Load(feedPath);
        XNamespace atomNS = "http://www.w3.org/2005/Atom";

        posts = from item in rssFeed.Root.Descendants(atomNS + "entry")
                    select new BlogPost
                    {
                        Title = item.Element(atomNS + "title").Value,
                        LinkTitle = GetLinkTitle(item.Element(atomNS + "title").Value),
                        Published = DateTime.Parse(item.Element(atomNS + "published").Value),
                        Content = item.Element(atomNS + "content").Value,
                        Author = item.Element(atomNS + "author").Element(atomNS + "name").Value,
                        Tags = (from category in item.Elements(atomNS + "category")
                                orderby category.Value
                                select category.Attribute("term").Value).ToList()

                    };

        // cache and set dependency on the local feed file
        HttpRuntime.Cache.Insert("IQODEFeed", posts, new CacheDependency(feedPath),DateTime.MaxValue, System.Web.Caching.Cache.NoSlidingExpiration);

    }
    return posts;
}

Two things to note here:

  • To read the atom feed I need to combine the atom namespace with the local name when constructing the Linq statement.
  • I'm caching this list and setting a cache dependency on the actual feed xml file. This way if the feed file changes the cache item is automatically removed and my code reloads the feed xml.

Next create a new controller for your blog index page and drop in the code blow.

public ActionResult Index()
{

    var posts = GetPosts();

    ViewData.Model = posts.ToList();

    return View();
}

Make sure your View page is strongly typed, for example...

public partial class Posts : System.Web.Mvc.ViewPage<List<BlogPost>>
{

}

...and then you can MVC magically reference the Posts Model in your code

<% foreach (var BlogPost in ViewData.Model) {%>
    <h3><strong><%=Html.RouteLink(BlogPost.Title, "Blog", new { id = BlogPost.Published.Year, month = BlogPost.Published.Month, title = BlogPost.LinkTitle }, null)%></strong></h3>
    
    <div class="postBody">
        <%=BlogPost.Content %>
    </div>
    <p class="author"><strong>Published:</strong> <%=BlogPost.Published.ToString("dddd, dd MMMM yyyy hh:mm tt")%> by <%= BlogPost.Author %></p>
    <p class="tags"><strong>Tags:</strong>
    <% foreach (var tag in BlogPost.Tags)
     {
         Response.Write(string.Format("<span title='{0}'>{1}
             </span>", "See all posts tagged " + tag, Html.ActionLink(tag, tag, "Blog/Tags")));   

    } %>
    </p>
    <p class="comments"><strong>Comments:</strong> <a href="<%=BlogPost.CommentsInfo.Link%>" class="external-blog"><%=BlogPost.CommentsInfo.Title %></a></p>

<% } %>

You'll notice some extra cruft in the code above which I will be covering in later posts but I'm sure you get the idea. Stay tuned for Part Two where I'll be covering in more details the controller actions, routing and rendering within the application.

Published: Wednesday, 01 October 2008 09:13 AM by Steve

Tags: MVC Blogger ASP.Net Linq

Comments: 0 Comments