Thanks for visiting my blog!
In response to some requests that I have received, I decided to write a several part blog on some of the techniques I used in developing Wildermuth.com. In this first example, I am going to discuss the use of LINQ and data in my site.
In moving from www.adoguy.com to www.wildermuth.com, one of my goals was to use LINQ as much as possible to see the travails of using it on a real project. I have done a lot of small samples with LINQ but did it hold up for real work? Suffice to say I am pretty impressed (though whether a blog is ‘real work’ is up for discussion, but its a better exercise of the technology than my samples had been).
When I say LINQ, I want to be specific. LINQ to me really is “Language Integrated Query”. The data store behind is a secondary discussion and whether the Entity Framework, LLBLGen Pro, or nHibernate, I really wanted to make sure that the way I queried data in the C# code was LINQ. I was hoping to avoid dropping down into SQL as much as possible.
I knew I wanted to be able to use my model from most pages so I added the data context to the Master page so it would handle most of the build-up, tear-down for me:
public WilderEntities Ctx
{
get
{
if (_ctx == null)
{
_ctx = new WilderEntities();
}
return _ctx;
}
}
protected override void OnUnload(EventArgs e)
{
if (_ctx != null)
{
Ctx.Dispose();
}
}
This allowed me to create the Ctx on the master page but use it on any page/control that I neeeded. The disposal of the context would happen during unloading of the the page. The only thing that this required is that most pages needed a typed reference to the master page which you can do with a MasterType page directive:
<%@ Page Language="C#" MasterPageFile="~/StwMaster.Master"
AutoEventWireup="true" CodeBehind="default.aspx.cs"
Inherits="stw._default" Title="Shawn Wildermuth's Blog" %>
<%@ MasterType VirtualPath="~/StwMaster.Master" %>
Much of the LINQ code is fairly pedestrian:
var qry = from b in MasterPage.Ctx.BlogEntry.Include("BlogEntryComments")
where b.Published == true
orderby b.DatePosted descending
select b;
List<BlogEntry> rants = qry.Take(10).ToList();
You can see that I am doing a simple LINQ query to get all the published BlogEntry objects and order them by the date they were posted. Of note, when I execute the query I am adding the Take() method to limit the results. This is akin to TOP in SQL and we’ll be revisiting it in a minute.
Because I need to have access to comments about the blog entries, I use the Include clause in the LINQ query to retrieve not only the BlogEntry objects, but also the related BlogEntryComment objects. This syntax is specific to the EntityFramework. If you are using a different LINQ provider, you may find that lazy loading is automatic (e.g. nHibernate) or not available. I know this is a chief complaint about the Entity Framework, but I like it as it makes the developer have to think about loading sub-types and the side effects…but that’s a whole other discussion.
Once we have a result its a simple as assigning our collection and forcing data binding to happen:
theRants.Blogs = rants;
titles.Blogs = rants;
DataBind();
Of particular interest here is that I am using the same list to bind to two collections (the list of blogs and the “On This Page” titles). Because we are getting simple CLR object collection back, there isn’t any magic here in how we’re doing the binding. This is in stark contrast to older data access (e.g. DataRows, DataReaders).
One thing I really like here is that I can do the paging directly during the execution of the LINQ query as seen in the LINQ query for the paged Rants page:
var qry = from b in Master.Ctx.BlogEntry.Include("BlogEntryComments")
where b.Published == true
orderby b.DatePosted descending
select b;
List<BlogEntry> rants = qry.Skip(PAGESIZE * (currentPage - 1)).Take(PAGESIZE).ToList();
Note that this LINQ query is identical to the earlier home page query but I am doing the paging by calculating both the Skip() and Take() value. Like I mentioned earlier, Take() is like TOP in that it specifies the number of returned elements. Whereas Skip() specifies how many results to ignore before starting the Take() amount. This allows us to manage paging directly using the LINQ code.
There was one place where I just couldn’t get LINQ to bend to my wishes. I am still not convinced that there is *not* a way to do this, but I dove down into Entity SQL to make the request instead. The case was where I am translating the URI pieces of the Rant URI (e.g. http://wildermuth.com/2008/07/07/Wildermuth_com_By_Example_-_Part_1) where I am being handed the year, month, day and title by the routing framework (I’ll talk about that in a future post). I needed a way of finding the right Rant based on that information (since I don’t want to share the Rant ID with anyone). To do this, I used an ObjectQuery (Entity Framework’s query syntax using Entity SQL):
string whereClause = @"SqlServer.DATEPART('yyyy', it.DatePosted) = @year AND
SqlServer.DATEPART('mm', it.DatePosted) = @month AND
SqlServer.DATEPART('dd', it.DatePosted) = @day AND
it.Title LIKE @pattern";
ObjectQuery<BlogEntry> qry = Master.Ctx.BlogEntry
.Include("BlogEntryComments")
.Where(whereClause);
qry.Parameters.Add(new ObjectParameter("year", rantDate.Year));
qry.Parameters.Add(new ObjectParameter("month", rantDate.Month));
qry.Parameters.Add(new ObjectParameter("day", rantDate.Day));
qry.Parameters.Add(new ObjectParameter("pattern", pattern));
_blogEntry = qry.FirstOrDefault();
Note I am using the DATEPART syntax to compare the parts of the date. The real issue with the query was the parameter as I take the title and replace all underscores with % to do a LIKE query. This worked fine and once the query executes, I am dealing with a simple CLR object so it doesn’t matter in the bit picture.
Other than that, the LINQ syntax is pretty straightforward in my example. I ported old old nasty code from my original sites (in fact some routines were originally ported from ASP to ASP.NET back in pre v1.0). Because of that I don’t have a good separation of responsibilities, but that’s for the next conversion…when MVC comes of age.
My last note I wanted to say is that I attempt to create pretty simple HTML. I don’t use many actual controls except the repeater and create mostly CSS-based XHTML to be simple. With that in mind I enchew ASP.NET’s DataSource stuff. Whether its LINQ, Object or Sql DataSources they all are trying to do a lot of magic IMHO and I end up writing code instead of depending on them.
Opinions and observations are welcome!