Tuesday, January 4, 2011

Integrating StructureMap, Unit of Work and WCF

When I initially set off on using StructureMap as my IoC of choice on a recent ASP.NET MVC/WCF project, I didn’t realize how deep the rabbit hole actually was.  Oh, the MVC part was easy.  StructureMap integration in an MVC application has been demonstrated a thousand times over and is made even easier with the latest release of ASP.NET MVC 3 and NuGet.  Getting StructureMap to serve up objects in a WCF application, now THAT’S a horse of a different color.

Standing on Someone Else’s Shoulders

Two blog posts helped me tremendously when attempting this:

I highly recommend reading both posts first, in that order.

Service Request/Response

Before going any further, I need to take a moment to define the pattern I was using on the project for calling the WCF service layer.  For each service operation I defined two data contracts: [OperationName]Request; and [OperationName]Response.  Both of these classes are decorated with the DataContract attribute and encapsulated the parameters in both the request and response to the service.  This allows the values that are passed into and returned from the service to change without breaking the service contract.  Much more flexible, IMHO.  Here’s an example.

IInventoryService
  1. [ServiceContract]
  2. public interface IInventoryService
  3. {
  4.     [OperationContract]
  5.     public CreateProductResponse CreateProduct(CreateProductRequest request);
  6. }
CreateProductRequest
  1. [DataContract]
  2. public class CreateProductRequest
  3. {
  4.     [DataMember]
  5.     public string Name { get; set; }
  6.  
  7.     [DataMember]
  8.     public decimal Price { get; set; }
  9. }
CreateProductResponse
  1. [DataContract]
  2. public class CreateProductResponse
  3. {
  4.     [DataMember]
  5.     public int NewProductId { get; set; }
  6. }


Taking it a Step Further

Andreas’ example would rollback the transaction if an unhandled exception was thrown in the WCF application.  This is all well and good but I didn’t like the idea of throwing exceptions from my service layer.  I thought a much cleaner approach would be to return one or more OperationResult objects in the service response object if a problem occurred.  An OperationResult would basically just consist of a Key/Message pair that identifies exactly what or where something went wrong (in the Key) as well as details of the error (in the Message).  In most cases, this could represent validation errors bubbled up from the domain model.  The Key would be the property name that was invalid and the Message would contain, well, the error message of the validation error (e.g., “Email address is in the wrong format”).  But this could also include other, more generic, errors as well.

OperationResult
  1. [Serializable]
  2. public class OperationResult
  3. {
  4.     public string Key { get; set; }
  5.     public string Message { get; set; }
  6. }

I decided to create a base class that contained a collection of OperationResult objects so that I wouldn’t have to reinvent the wheel for every Response object I created.  It looks like this:

OperationResultResponse
  1. public interface IOperationResultResponse
  2. {
  3.     public void AddResult(string key, string message);
  4.     public IList<OperationResult> Results { get; set; }
  5. }
  6.  
  7. [DataContract]
  8. public class OperationResultResponse : IOperationResultResponse
  9. {
  10.     private void initResults()
  11.     {
  12.         this.Results = new List<OperationResult>();
  13.     }
  14.  
  15.     public IList<OperationResult> Results { get; set; }
  16.  
  17.     public void AddResult(string key, string message)
  18.     {
  19.         if (this.Results == null)
  20.             initResults();
  21.  
  22.         this.Results.Add(new OperationResult { Key = key, Message = message });
  23.     }
  24.  
  25.     public OperationResultResponse()
  26.     {
  27.         initResults();
  28.     }
  29. }

And then, for all (or some, if not applicable) of your response simply inherit from the OperationResultResponse base class.

Inherit from response base
  1. [DataContract]
  2. public class CreateProductResponse : OperationResultResponse
  3. {
  4.     [DataMember]
  5.     public int NewProductId { get; set; }
  6. }


Where the Magic Happens

OK, that was a lot of lead up to the big reveal.  How do we take this service request/response object model and use it to rollback our Unit of Work (and consequently our NHibernate session transaction)?  I got one, err, two, ish words for you: IParameterInspector.  This is another one of WCF’s many extensibility points and, like IDispatchMessageInspector, it allows you to perform work when a request is received and before a response is sent.  In our case, we want to perform the following steps before each response is sent:

  1. Get a reference to the Unit of Work for the current request
  2. Check the response object type and see if it implements IOperationResultResponse
  3. If the response object DOES implement IOperationResultResponse, interrogate the Results property to see if there are any errors contained therein
  4. If there ARE errors in the Results property, rollback the Unit of Work by calling Rollback()
  5. If none of the above applies, call Commit() and Dispose() on the UoW before sending the response on its way

Here’s what our IParameterInspector implementation looks like:

Custom IParameterInspector
  1. public class OperationResultParameterInspector : IParameterInspector
  2. {
  3.     private IContainer _container;
  4.  
  5.     public OperationResultParameterInspector(IContainer container)
  6.     {
  7.         _container = container;
  8.     }
  9.  
  10.     public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
  11.     {
  12.         // Step 1: Get UoW
  13.         var uow = _container.GetInstance<IUnitOfWork>();
  14.  
  15.         // Step 2: Check for IOperationResult
  16.         var opResultResponse = returnValue as IOperationResultResponse;
  17.         if (opResultResponse != null)
  18.         {
  19.             // Step 3: Interrogate Results for errors
  20.             if (opResultResponse.Results.Count > 0)
  21.             {
  22.                 // Step 4: Rollback our UoW before sending response
  23.                 uow.Rollback();
  24.             }
  25.         }
  26.         else
  27.         {
  28.             // Step 5: If we're still here, commit and dispose
  29.             uow.Commit();
  30.         }
  31.  
  32.         uow.Dispose();
  33.     }
  34.  
  35.     public object BeforeCall(string operationName, object[] inputs)
  36.     {
  37.         // No implementation
  38.         return null;
  39.     }
  40. }


Why We Can’t Use BeforeSendReply

We’re still going to reuse the Andreas’ UnitOfWorkMessageInspector to create and initialize the Unit of Work in the AfterReceiveRequest but instead of committing and disposing of the UoW in the BeforeSendReply method, we’re going to rely on our parameter inspector’s AfterCall method to do the work.  The reason we’re not performing the above logic in the BeforeSendReply method of IDispatchMessageInspector is because BeforeSendReply happens AFTER the response has been serialized into a Message object, making it much more difficult to interrogate the values.  Believe me, I tried.  AfterCall of IParameterInspector is invoked BEFORE the return value has been serialized into a Message object so you can easily just cast the return value to our IOperationResultResponse interface. 

Wiring It All Up

Wiring up our IParameterInspector is just like wiring up the IDispatchMessageInspectors, IErrorHandlers, etc.  Iterate through each EndPoint, get the list of IParameterInspectors  (you may have more than one!) from StructureMap and append them to each DispatchOperation.  Here’s what our updated StructureMapServiceBehavior class looks like.  I’ve omitted the other details for brevity.

StructureMapServiceBehavior
  1. public class StructureMapServiceBehavior : IServiceBehavior
  2. {
  3.     private IContainer _container;
  4.  
  5.     public StructureMapServiceBehavior(IContainer container)
  6.     {
  7.         _container = container;
  8.     }
  9.  
  10.     public void Validate(ServiceDescription serviceDescription,
  11.         ServiceHostBase serviceHostBase) { /* No implementation */ }
  12.     public void AddBindingParameters(ServiceDescription serviceDescription,
  13.         ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints,
  14.         BindingParameterCollection bindingParameters) { /* No implementation */ }
  15.  
  16.     public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
  17.     {
  18.         foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
  19.         {
  20.             var cd = cdb as ChannelDispatcher;
  21.             if (cd != null)
  22.             {
  23.                 foreach (EndpointDispatcher ed in cd.Endpoints)
  24.                 {
  25.                     // Not shown: Add InstanceContextInitializers, IDispatchMessageInspectors, ICallContextInitializers, etc.
  26.  
  27.                     foreach (DispatchOperation op in ed.DispatchRuntime.Operations)
  28.                     {
  29.                         // Add custom IParameterInspectors
  30.                         foreach (var pi in _container.GetAllInstances<IParameterInspector>())
  31.                         {
  32.                             op.ParameterInspectors.Add(pi);
  33.                         }
  34.                     }
  35.                 }
  36.             }
  37.  
  38.             // Not shown: Add IErrorHandlers
  39.         }
  40.     }
  41. }

And of course, we add the mapping between IParameterInspector and our custom OperationResultParameterInspector in the traditional StructureMap way:

ServicesRegistry
  1. public class ServicesRegistry : StructureMap.Configuration.DSL.Registry
  2. {
  3.     public ServicesRegistry()
  4.     {
  5.         // Not shown: wiring up all other WCF extensions
  6.         For<IParameterInspector>().Add<OperationResultParameterInspector>();
  7.     }
  8. }


Conclusion

This really turned into an exercise in learning the various WCF extension points, what you can and cannot do at each, etc.  We’ve learned though that the best place for inspecting the actual service response objects before they’re sent across the wire is in the AfterCall method of the IParameterInspector interface.

Tuesday, December 14, 2010

Multiple AND/OR Statements with NHibernate

I’m sure you’ve encountered the scenario where you need to add multiple AND or OR statements to an NHibernate query.  The string parsing and manipulation required to do it in HQL would be enough to send you straight to programmer hell.  So it turns out the easiest (in my opinion) option is to use the good ol’ Criteria API.  But it’s not as obvious as it first seems.  In order to accomplish the task, you actually need to use a pair of constructs that may be new to you: Conjunction which represents a grouping of AND statements (A and B and C…); and Disjunction, a grouping of OR statements (A or B or C…).  Here’s an admittedly contrived example of how you could query for Persons by FirstName that are not archived (IsArchived), ordering by FirstName:

Contrived Disjunction Example
  1. string[] namesToCheck = { "Nick", "Dave", "Bryan", "James" };
  2. using (ISession session = SessionManager.CreateSession())
  3. {
  4.     var criteria = session.CreateCriteria<Person>();
  5.     var disjunction = Restrictions.Disjunction();
  6.  
  7.     criteria.Add(Expression.Eq("IsArchived", false));
  8.  
  9.     foreach (var name in namesToCheck)
  10.     {
  11.         disjunction.Add(Restrictions.Eq("FirstName", name));
  12.     }
  13.  
  14.     criteria.Add(disjunction);
  15.     criteria.AddOrder(Order.Asc("FirstName"));
  16.  
  17.     return criteria.List<Person>();
  18. }

This would generate something similar to the following SQL-statement:

Disjunction SQL Results
  1. SELECT * FROM Person
  2. WHERE IsArchived = 0 AND
  3.     (FirstName = "Nick" OR
  4.     FirstName = "Dave" OR
  5.     FirstName = "Bryan" OR
  6.     FirstName = "James")
  7. ORDER BY FirstName

If you needed ANDs instead of ORs you would use Conjunction instead of Disjunction.

A more powerful example would be if you needed multiple sets of statements.  Consider the following scenario:

Select all Persons where IsArchived is false and the FirstName is “Nick”, or all Persons where IsArchived is true and the FirstName is “Bryan”

Here’s how you would do it using Conjunctions and Disjunctions:

Less Contrived Example
  1. using (ISession session = SessionManager.CreateSession())
  2. {
  3.     var criteria = session.CreateCriteria<Person>()
  4.         .AddOrder(Order.Asc("FirstName"));
  5.  
  6.     // IsArchived is FALSE and FirstName = "Nick"
  7.     var conjunction1 = Restrictions.Conjunction();
  8.     conjunction1.Add(Restrictions.Eq("IsArchived", false));
  9.     conjunction1.Add(Restrictions.Eq("FirstName", "Nick"));
  10.  
  11.     // IsArchived is TRUE and FirstName = "Bryan"
  12.     var conjunction2 = Restrictions.Conjunction();
  13.     conjunction2.Add(Restrictions.Eq("IsArchived", true));
  14.     conjunction2.Add(Restrictions.Eq("FirstName", "Bryan"));
  15.  
  16.     // This combines the two statements into an OR
  17.     var disjunction = Restrictions.Disjunction();
  18.     disjunction.Add(conjunction1);
  19.     disjunction.Add(conjunction2);
  20.  
  21.     criteria.Add(disjunction);
  22.  
  23.     return criteria.List<Person>();
  24. }

Which would produce the following:

SQL Results
  1. SELECT * FROM Person
  2. WHERE
  3.     (IsArchived = 0 AND FirstName = "Nick") OR
  4.     (IsArchived = 1 AND FirstName = "Bryan")
  5. ORDER BY FirstName

Wednesday, December 8, 2010

ASP.NET MVC Pager

As promised in my last blog post about creating a reusable NHibernate paging extension method, here is some code that uses the IPagedList interface we created to render a nice looking pager control in an ASP.NET MVC site.

Pager.cs
  1. public class Pager
  2. {
  3.     public enum PagerMode
  4.     {
  5.         NextPrev,
  6.         Numbers,
  7.         All
  8.     }
  9.  
  10.     public string QueryStringParam { get; set; }
  11.     public PagerMode Mode { get; set; }
  12.     public string NextText { get; set; }
  13.     public string PreviousText { get; set; }
  14.     public string FirstText { get; set; }
  15.     public string LastText { get; set; }
  16.     public IPagedList List { get; set; }
  17.     public bool ShowTotalRecords { get; set; }
  18.     public string TotalRecordsText { get; set; }
  19.     public RequestContext Context { get; set; }
  20.     public bool EnableAjax { get; set; }
  21.     public int MaxNumberOfNextPages { get; set; }
  22.     public int MaxNumberOfPreviousPages { get; set; }
  23.  
  24.     private StringBuilder _builder;
  25.     private UrlHelper _urlHelper;
  26.  
  27.     public Pager(IPagedList list, RequestContext context)
  28.     {
  29.         List = list;
  30.         Context = context;
  31.  
  32.         _builder = new StringBuilder();
  33.         _urlHelper = new UrlHelper(Context);
  34.  
  35.         // Defaults
  36.         QueryStringParam = "page";
  37.         Mode = PagerMode.All;
  38.         NextText = "Next »";
  39.         PreviousText = "« Previous";
  40.         FirstText = "« First";
  41.         LastText = "Last »";
  42.         ShowTotalRecords = false;
  43.         TotalRecordsText = "{0} total records";
  44.         EnableAjax = true;
  45.         MaxNumberOfNextPages = 4;
  46.         MaxNumberOfPreviousPages = 5;
  47.     }
  48.  
  49.     public string Render()
  50.     {
  51.         if (List.TotalCount == 0 || List.TotalPages == 1)
  52.             return String.Empty;
  53.  
  54.         _builder.AppendLine("<div class=\"pagination\">");
  55.  
  56.         if (Mode == PagerMode.NextPrev || Mode == PagerMode.All)
  57.         {
  58.             WriteFirstLink();
  59.             WritePreviousLink();
  60.         }
  61.  
  62.         if (Mode == PagerMode.Numbers || Mode == PagerMode.All)
  63.         {
  64.             WritePageNumbers();
  65.         }
  66.  
  67.         if (Mode == PagerMode.NextPrev || Mode == PagerMode.All)
  68.         {
  69.             WriteNextLink();
  70.             WriteLastLink();
  71.         }
  72.  
  73.         if (ShowTotalRecords)
  74.         {
  75.             WriteTotalRecords();
  76.         }
  77.  
  78.         _builder.AppendLine("</div>");
  79.  
  80.         return _builder.ToString();
  81.     }
  82.  
  83.     private void WriteTotalRecords()
  84.     {
  85.         var span = new TagBuilder("span");
  86.         span.AddCssClass("total");
  87.         span.SetInnerText(String.Format(TotalRecordsText, String.Format("{0:n0}", this.List.TotalCount)));
  88.  
  89.         _builder.AppendLine(span.ToString());
  90.     }
  91.  
  92.     private void WriteLastLink()
  93.     {
  94.         if (!List.HasNextPage)
  95.             return;
  96.  
  97.         WriteLink(GenerateUrl(List.TotalPages - 1), LastText);
  98.     }
  99.  
  100.     private void WriteNextLink()
  101.     {
  102.         if (!List.HasNextPage)
  103.             return;
  104.  
  105.         WriteLink(GenerateUrl(List.PageIndex + 1), NextText);
  106.     }
  107.  
  108.     private void WritePageNumbers()
  109.     {
  110.         int start = 0;
  111.         int end = List.TotalPages - 1;
  112.         int maxPages = (MaxNumberOfPreviousPages + MaxNumberOfNextPages) + 1; // the + 1 is for the current page
  113.  
  114.         if (List.TotalPages > maxPages)
  115.         {
  116.             start = List.PageIndex - MaxNumberOfPreviousPages;
  117.             end = List.PageIndex + MaxNumberOfNextPages;
  118.  
  119.             if (start < 0)
  120.                 start = 0;
  121.             if (end > List.TotalPages - 1)
  122.                 end = List.TotalPages - 1;
  123.         }
  124.  
  125.         for (int i = start; i <= end; i++)
  126.         {
  127.             WriteLink(GenerateUrl(i), i + 1);
  128.         }
  129.     }
  130.  
  131.     private void WritePreviousLink()
  132.     {
  133.         if (!List.HasPreviousPage)
  134.             return;
  135.  
  136.         WriteLink(GenerateUrl(List.PageIndex - 1), PreviousText);
  137.     }
  138.  
  139.     private void WriteFirstLink()
  140.     {
  141.         if (!List.HasPreviousPage)
  142.             return;
  143.  
  144.         WriteLink(GenerateUrl(0), FirstText);
  145.     }
  146.  
  147.     private void WriteLink(string url, string text)
  148.     {
  149.         var tag = new TagBuilder("a");
  150.         tag.Attributes["href"] = url;
  151.         tag.Attributes["title"] = text;
  152.         tag.SetInnerText(text);
  153.  
  154.         _builder.Append(tag.ToString());
  155.     }
  156.  
  157.     private void WriteLink(string url, int pageNumber)
  158.     {
  159.         var tag = new TagBuilder("a");
  160.         tag.Attributes["href"] = url;
  161.         tag.Attributes["title"] = pageNumber.ToString();
  162.         tag.AddCssClass("number");
  163.         if ((pageNumber - 1) == List.PageIndex)
  164.             tag.AddCssClass("current");
  165.         tag.SetInnerText(pageNumber.ToString());
  166.  
  167.         _builder.Append(tag.ToString());
  168.     }
  169.  
  170.     private string GenerateUrl(int pageIndex)
  171.     {
  172.         if (this.EnableAjax)
  173.         {
  174.             return "#" + QueryStringParam + "=" + pageIndex;
  175.         }
  176.         else
  177.         {
  178.             Context.RouteData.Values[QueryStringParam] = pageIndex;
  179.  
  180.             return _urlHelper.RouteUrl(Context.RouteData.Values);
  181.         }
  182.     }
  183. }

The Pager class contains the rendering logic for the pager control and makes use of the TagBuilder class supplied in the System.Web.Mvc namespace.  Makes generating well-formatted HTML code a bit easier, in my opinion.  The class itself is pretty boring, just pass in the IPagedList instance and BAM!  There are some customization options as well like changing the text of the Next/Previous/First/Last links, showing or hiding the total record count, etc.  One of the more interesting options is the EnableAjax property which, when set to true, renders the URL of each pager link as a hash link (/list#page=2) instead of a hard link (/list?page=2).  This allows you to intercept the hash changed event using jQuery and dynamically update the page content using Ajax.

In typical MVC fashion, I created a couple extension methods that make calling the Pager class easy from a View.  You can customize the settings however you like.  Call any of these methods from your view and just pass in the IPagedList instance.

MVC Pager Extension Methods
  1. public static string Pager(this HtmlHelper html, IPagedList list)
  2. {
  3.     return Pager(html, list, "page", false);
  4. }
  5.  
  6. public static string AjaxPager(this HtmlHelper html, IPagedList list)
  7. {
  8.     return Pager(html, list, "page", true);
  9. }
  10.  
  11. public static string Pager(this HtmlHelper html, IPagedList list, string queryStringParam, bool enableAjax)
  12. {
  13.     var pager = new Pager(list, html.ViewContext.RequestContext)
  14.     {
  15.         QueryStringParam = queryStringParam,
  16.         EnableAjax = enableAjax,
  17.         ShowTotalRecords = true
  18.     };
  19.     return pager.Render();
  20. }

Here’s what the rendered pager markup looks like, pretty clean if I do say so myself.

Rendered pager markup
  1. <div class="pagination">
  2.     <a title="First" href="/list?page=0">&lt;&lt; First</a>
  3.     <a title="Previous" href="/list?page=0">&lt;&lt; Previous</a>
  4.     <a class="number" title="1" href="/list?page=0">1</a>
  5.     <a class="current number" title="2" href="/list?page=1">2</a>
  6.     <a class="number" title="3" href="/list?page=2">3</a>
  7.     <a title="Next" href="/list?page=2">Next &gt;&gt;</a>
  8.     <a title="Last" href="/list?page=2">Last &gt;&gt;</a>
  9.     <span class="total">37 total records</span>
  10. </div>

It doesn’t take much to make this look nice with some CSS classes.  Here’s a simple example.

Code Snippet
  1. a
  2. {
  3.     color: #003366;
  4. }
  5. .pagination
  6. {
  7.     margin: 0 0 5px;
  8.     text-align: center;
  9. }
  10. .pagination a, .pagination a:visited
  11. {
  12.     margin: 0 5px 0 0;
  13.     padding: 3px 6px;
  14.     text-decoration: none;
  15. }
  16. .pagination a.number
  17. {
  18.     border: 1px solid #DDDDDD;
  19. }
  20. .pagination a.current
  21. {
  22.     color: #000000;
  23.     border-color: #DDDDDD;
  24.     background-color: #DDDDDD;
  25. }
  26. .pagination .total
  27. {
  28.     display: block;
  29.     margin-top: 4px;
  30.     color: #666666;
  31. }

And, finally, here’s what the pager looks like on the, err, page.

pager

Tuesday, December 7, 2010

A Re-Usable NHibernate Paging Extension Method

Every developer knows that they should always try to avoid returning an unbounded result set from the database, right?  Of course they do.  The obvious solution to this problem is to page through the results in smaller chunks that the database (and most likely the end-user) can handle efficiently.  I didn’t want to have to reinvent the wheel for every query I wrote so I wrapped the necessary NHibernate logic up in a nice, tidy extension method.

Side note: NHibernate Profiler is an indispensible tool for optimizing your NHibernate code.  I couldn’t live without it, nor would I want to.  And no I don’t receive kickbacks from Ayende.

Of course, in order to make this truly useful, I needed a custom collection that could keep track of the various paging settings like the current page index, the total number of records in the database, how many records are returned for each page, etc.  For this, I blatantly stole borrowed the PagedList<T> code from Rob Conery and ScottGu.  I also later found a great project on github by Troy Goode that is basically the same but with some supporting unit tests and optimizations.  Either way, here is teh codez that I use.

PagedList.cs – contains an interface and a default implementation of a generic PagedList<T>.  It also has a helper extension method so you can quickly turn any IEnumberable<T> into a PagedList<T>.

PagedList.cs
  1. public interface IPagedList
  2. {
  3.     long TotalCount { get; set; }
  4.     int TotalPages { get; set; }
  5.     int PageIndex { get; set; }
  6.     int PageSize { get; set; }
  7.     int FirstItem { get; }
  8.     int LastItem { get; }
  9.     bool HasPreviousPage { get; }
  10.     bool HasNextPage { get; }
  11. }
  12.  
  13. [Serializable]
  14. public class PagedList<T> : IPagedList
  15. {
  16.     public List<T> Items { get; private set; }
  17.     public int TotalPages { get; set; }
  18.     public long TotalCount { get; set; }
  19.     public int PageIndex { get; set; }
  20.     public int PageSize { get; set; }
  21.  
  22.     public PagedList()
  23.     {
  24.         Items = new List<T>();
  25.     }
  26.  
  27.     public PagedList(IEnumerable<T> source, long totalCount, int pageIndex, int pageSize)
  28.     {
  29.         PageSize = pageSize;
  30.         PageIndex = pageIndex;
  31.         TotalCount = totalCount;
  32.         TotalPages = (int)(TotalCount / pageSize);
  33.  
  34.         if (TotalCount % pageSize > 0)
  35.             TotalPages++;
  36.  
  37.         Items = new List<T>(source);
  38.     }
  39.  
  40.     public bool HasPreviousPage
  41.     {
  42.         get { return (PageIndex > 0); }
  43.     }
  44.  
  45.     public bool HasNextPage
  46.     {
  47.         get { return PageIndex < TotalPages - 1; }
  48.     }
  49.  
  50.     public int FirstItem
  51.     {
  52.         get
  53.         {
  54.             return (PageIndex * PageSize) + 1;
  55.         }
  56.     }
  57.  
  58.     public int LastItem
  59.     {
  60.         get { return FirstItem + PageSize - 1; }
  61.     }
  62. }
  63.  
  64. public static class Pagination
  65. {
  66.     public static PagedList<T> ToPagedList<T>(this IEnumerable<T> source, int totalCount, int pageIndex, int pageSize)
  67.     {
  68.         return new PagedList<T>(source, totalCount, pageIndex, pageSize);
  69.     }
  70. }

NHibernateExtensions.cs – this is where the NHibernate goodness comes in.  This warrants a little explanation but take a look first and see if you can see what’s going on.

NHibernateExtensions.cs
  1. public static class NHibernateExtensions
  2. {
  3.     public static PagedList<T> PagedList<T>(this ICriteria criteria, ISession session, int pageIndex, int pageSize) where T: class
  4.     {
  5.         if (pageIndex < 0)
  6.             pageIndex = 0;
  7.  
  8.         var countCrit = (ICriteria)criteria.Clone();
  9.         countCrit.ClearOrders(); // so we don't have missing group by exceptions
  10.  
  11.         var results = session.CreateMultiCriteria()
  12.             .Add<long>(countCrit.SetProjection(Projections.RowCountInt64()))
  13.             .Add<T>(criteria.SetFirstResult(pageIndex * pageSize).SetMaxResults(pageSize))
  14.             .List();
  15.  
  16.         var totalCount = ((IList<long>)results[0])[0];
  17.  
  18.         return new PagedList<T>((IList<T>)results[1], totalCount, pageIndex, pageSize);
  19.     }
  20. }

First of all, this method is an extension of NHibernate’s ICriteria class so it obviously requires you to be using NHibernate’s Criteria API.  This type of thing would be much more difficult using the HQL syntax.

Essentially what we want to do is leverage NHibernate’s multi-criteria feature to send two different SQL statements to the server at the same time.  One to retrieve the total record count of the query and two to get the actual results of that query, limited to the page size and page index that we specify.  That’s why we Clone the original criteria that is passed into the method.  This will be what is used to get the total record count for the query.  We call the ClearOrders method on this instance of the ICriteria because: 1) we don’t care about how the query is ordered if we’re just getting the total row count; and 2) the order statements can cause problems if there are groupings in the criteria. Trust me on this.

We then create the multi-criteria object on line 11 and add the total row count criteria that we cloned and the original criteria passed in but we use the SetFirstResult and SetMaxResults methods to limit the number of records returned.

Finally, we retrieve the values from the two result sets.  The first being the total row count.  The second, the actual results of the query and return the results using our fancy new PagedList<T> class.

The usage of this extension method couldn’t be simpler.

Code Snippet
  1. public PagedList<Order> GetCompletedOrders(int pageIndex, int pageSize)
  2. {
  3.     // _currentSession is an instance of ISession, instantiation not shown
  4.     return _currentSession.CreateCriteria<Order>()
  5.         .Add(Expression.Eq("Status", (int)OrderStatus.Complete))
  6.         .AddOrder(Order.Desc("OrderCompletedDate"))
  7.         .PagedList<Order>(_currentSession, pageIndex, pageSize);
  8. }

As a bonus, I’m going to post some code that I use in my ASP.NET MVC projects that allows you generate a Pager control complete with customizable, CSS classes, next/previous text customizations, AJAX support, etc. from our PagedList<T> class in ONE LINE OF CODE.  Stay tuned…

Update: here’s the post on those ASP.NET MVC Pager helper classes I mentioned.

Monday, December 6, 2010

Finally Blogging

Hello WorldWell, it’s official.  I’ve finally started blogging.  Now seems like a good time since my entire development environment (vSphere with multiple development and test virtual machines) here at a client’s site is DOWN.  It’s not my fault this time, I swear!  So I’m starting on my blog backlog while they work on getting it restored.  Here’s a list of a few topics I’m going to be covering:

If any of these topics interest you, I encourage you to check back soon!

I’m also going to be posting a lot of information to Twitter @nickpatterson so check me out there as well.