Paging through lists with LINQ

January 30, 2010

There are a number of fantastic LINQ extension methods that extend IEnumerable(T). I'm going to be using Skip(Tsource) and Take(Tsource) to implement paging of a large list of data in as little as one line of code.

Let's say we have a list of integers from 1 to 9:

IEnumerable<int> list1 = new List<int> {1,2,3,4,5,6,7,8,9};

With skip(n) we can skip the first n items in the list, returning the same ienumerable<int> in our case:

var skipping = list1.skip(3); //4,5,6,7,8,9

With take(n) we can "take" the first n items in the list, omitting the rest:

var taking = list1.take(2); //1,2

And so when chaining both together, we can narrow in on a subset (or page, as we'll see later) of the list:

var both = list1.skip(3).take(2); //4,5

Implementing a paging mechanism in an mvc view page

To implement a paging mechanism on a large set of ienumerable data requires mostly boilerplate code. the only additional pieces of information we need to know is the number of items to display per page and what the current page number is (defaulting to 1, of course). let's use the following 100-item list (of string) as our source data

item 0
item 1
item 2
...
item 99

and construct an action method that accepts a page number parameter, using a fixed 5 items per page limit.

public actionresult index(int page)
{
    int itemsperpage = 5;
    ienumerable<string> list = get100itemlistsomehow();
    list = list.skip((page - 1) * itemsperpage).take(itemsperpage);
    return view(list);
}

Assuming a page value of 1 was requested (a url something like http://mysite/home/index?page=1 in this case) our list would be reduced to:

item 0
item 1
item 2
item 3
item 4

A more robust implementation

The following example is something similar to what i've done on my website home page to display and page through a 10 page list of RSS feed items. I've defined a class called pager that encapsulates most of the paging logic and exposes properties like nextpage, previouspage, etc. it's also used as the model-bound object passed into the Action method and picks up the url parameters page and perPage if present.

This is not necessarily an ideal implementation in every scenario - i've only added the most basic check's and balances in the pager class and i'm leaving it up to the View to determine whether a previous or next button needs to be displayed (in this example, they are always displayed). But it's a pretty solid starting point.

The pager class

public class pager
{
    private int _page;
    public int page
    {
        get { return _page < 1 ? 1 : _page; }
        set { _page = value; }
    }

    private int _perpage;
    public int perpage
    {
        get { return _perpage < 1 ? 10 : _perpage; }
        set { _perpage = value; }
    }

    public int itemcount { get; set; }
    
    public int previouspage
    {
        get { return page - 1; }
    }

    public int nextpage
    {
        get { return (page == lastpage) ? page : page + 1; }
    }

    public int lastpage
    {
        get { return (int)math.ceiling((itemcount / (double)perpage)); }
    }
}

Controller action method

public actionresult index(pager pager)
{
    var list = mylist.get(); //list of 100 strings (eg. "Item 0", and so on)

    pager.itemcount = list.count(); //pager needs to know item count
    
    list = list.skip((pager.page - 1) * pager.perpage).take(pager.perpage); //perform operation

    return view(new homeindexviewmodel{ list = list, pager = pager });
}

The View markup

<%=Html.ActionLink("Previous", "Index", new { page = Model.Pager.PreviousPage }) %> |
<%=Html.ActionLink("Next", "Index", new { page = Model.Pager.NextPage })%>

<br/>

<%foreach (var item in Model.List) { %>
    <%=item %>
<%} %>

And some examples of the output

http://mysite/?page=1

page 1

http://mysite/?page=10

page 2

Optionally specify a different amount per page http://mysite/?page=24&perpage=3

page 24 per page 3

Good to Great… to Failure?

January 21, 2010

Last night I finally sat down and started reading Jim Collins’ 2001 classic, Good to Great: Why Some Companies Make the Leap… and Others Don’t. I may be nearly a decade late, but this has been on my reading list for years and I typically find books of this nature (highly analytical, based on years of research, conclusions made from actual observations rather than preconceived theories) to be timeless.

So I was rather dismayed to find among the 11 “great” companies chronicled in the book, the following names:

  • Circuit City (1982-1997)
  • Fannie Mae (1984-1999)

Both of these companies made the leap from “good results” to “great results” during the 15-year periods listed, respectively. It's important to note that these are good-to-great companies based primarily on attaining extraordinary stock returns compared to other competitors and the market. They were not evaluated on intangibles such as how they impacted society, primarily because there are no objective ways to calculate things of this nature.

It was also interesting to learn that a company I would work for much later, Harris Corporation (observed between the 60’s and 80’s), was identified as a case of “unsustained” greatness – a company that appeared to have hit a breakthrough (to greatness) in 1975, but, well, “crashed to a grinding halt” by 1988.

To be fair, Harris was much different back then than it was in the mid-2000’s when I worked there, but I can’t help but contrast with another 15-year period (1995 – 2010), where the results have been turned upside down. Harris is above S&P 500 whereas Fannie Mae has hit rock bottom.

image

What’s even more interesting is that Harris has sustained these results in the Communications/IT Systems industry, an industry it may never have entered had the company not sold off its printing business decades before. The divesting of the printing business was one of the primary decisions (though not the only) that doomed Harris, and prevented it from attaining greatness, according to Collins.

Indeed, Harris did underperform the market through the 80’s, and to put it in perspective in the same way the book does, if you invested $1 each in Harris and Fannie Mae in 1978, your Harris stock would have only been worth about $1.40 by 1990, whereas Fannie Mae would have netted you $6.00. But if you were to have held on to these stocks to this day, well… I’ll let you do the math.

Is it all a matter of time?

Would Collins have selected the same 11 companies as examples of good-to-great if the time frame had been lengthened? Clearly the list might have been different (bigger) if it were shortened. If Circuit City made a leap to greatness between 1982 and 1997, then what would you describe it as having done between 1982 through 2007? Are these eras entirely separate and distinct - two tales of how the good become great, and then how the great may fall?

Apparently, yes.

Collins released How the Mighty Fall: And Why Some Companies Never Give In this past year, so I look forward to these answers when I read this book around the year 2020 or so.

I hope I’m being unfair

On some level I fundamentally believe in Collins’ research, approach, and therefore the results – even before finishing the book. There may be no perfect way to research and codify what makes anything successful, but I don’t know if there is any better way than making “empirical deductions directly from the [research] data” on over 1,000 companies and building a theory from the ground up, directly from evidence, as Collins puts it.

How the Mighty Fall does address Circuit City and Fannie Mae, I’m told, and I suspect their failures do not debunk Good to Great (surely, they only prove “how the mighty fall”, and that apparently, some companies do go from good to great to failure).

I look forward to the rest of the book and the conclusions Collins has drawn, but certainly we are not starting off on the brightest of notes. I’ll give the book the benefit of the doubt, but I think this goes to show that the parameters within which one researches something (in this case, primarily the time-frame) can have a tremendous effect on the result.

Tags: good to great, jim collins
Categories: Business, Books

Configuring ELMAH on DiscountASP.NET

January 5, 2010

This article will demonstrate the steps required to install and configure ELMAH for ASP.NET applications running on Windows 2008/IIS7 in Integrated mode. I have tested this process and have focused on installing it within the DiscountASP.NET IIS7 shared-hosting platform, but these instructions apply to any default IIS7 server in Integrated mode.

I’m going to roughly follow the ASP.NET MVC instructions to configure an ELMAH installation that will utilize 3 of the most common features, below.

  • Error logging
  • Error emailing
  • Security module

Please note that MVC no longer requires additional setup (routing configuration) as mentioned in the official instructions – they were written back when MVC was in beta and didn’t ignore routes to .axd files. These instructions apply equally to an ASP.NET web forms application.

Installation steps

Step 1: Include latest Elmah.dll in your project

  1. Download and extract ELMAH-1.1-bin-x64.zip from http://code.google.com/p/elmah/
  2. Copy Elmah.dll from the \bin\net-3.5\Release folder to your asp.net website’s bin folder

Step 2: Basic Web.Config setup

The Web.Config will need to have an ELMAH sectionGroup added, ELMAH’s custom config section, and entries in the httpHandlers and httpModules sections.

   1.   Add the following ELMAH section group, including the security, errorLog, and errorMail sub-sections:

<sectionGroup name="elmah"> 
      <section name="security" type="Elmah.SecuritySectionHandler, Elmah" /> 
      <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" /> 
      <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" /> 
</sectionGroup> 

   2.   Add this to the <httpHandlers> section within <system.webServer>:

<add name="Elmah" verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah"/>

   3.   Add this to the <httpModules> section within <system.webServer>:

<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" /> 
<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />

   4.   Add a section to configure ELMAH, somewhere outside of system.web. Here I am configuring the error logger by specifying a log path of ~/App_Data/Errors, the error emailer by defining some email attributes, and the security module by specifying that I want to allow remote access.

<elmah> 
        <errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="~/App_Data/Errors" /> 
        <errorMail subject="elmah error" to="e@mail.com" from="postmaster@mysite.com" /> 
    <security allowRemoteAccess="yes" /> 
</elmah>

Make sure your logPath directory structure exists in your application. ELMAH will throw an exception if it doesn’t exist.

Also, make sure you’ve properly set up your <mailSettings> Web.Config section, as ELMAH will use this to determine your smtp host, delivery method, etc.

Step 3: Testing

Configuration is complete for a basic, non-secure ELMAH installation. I manually caused an exception for testing purposes, and then loaded up /elmah.axd in the browser:

elmah

Step 4: Securing ELMAH from outsiders

The last thing that needs to be done is secure the elmah.axd handler – at this point, any anonymous user on your site could type in the url to it and see all of your errors, potentially gaining access to sensitive information about your application.

ELMAH has it’s own basic security by default. If I hadn’t configured the security module or left allowRemoteAccess=”false” then by default elmah.axd could only be viewed from the local host. This of course is not an option when on shared hosting, so we have to expose ELMAH to remote users and secure it another way: using the built in ASP.NET authorization.

Adding the following entry to the Web.Config is the final step. Here I am denying access to any anonymous user.

<location path="elmah.axd">  
    <system.web>  
        <authorization>  
            <deny users="?" />  
        </authorization>  
    </system.web>  
</location

Done!

Now all you have to do is generate a few exceptions and confirm that the logging, emailing and security is working correctly!

Tags: elmah, discountasp.net, iis7
Categories: ASP.NET, IIS

About Kurt

I'm a senior consultant at Headspring in Austin, TX. My passion is creating web-based applications that are well crafted and solve real problems for real people. Want to know more? Check out my about page.

. @LipGlosserie setting up for Renegade Austin craft fair http://t.co/7X4WBVQb 15 hours ago