Friday, 4 July 2008

Heroes Happened Here

I just dropped a zip file on SkyDrive which contains the solution from my session last night at the Perth .NET Community of Practice Heroes Happen Launch.

What's New in C# 3.0

There were no slides and I think the code is pretty self-explanatory. The samples are all X-Men related following the Heroes Happen theme and cover in order:
  • AutoImplemented Properties
  • Local Variable Type Inference
  • Object Initializers
  • Collection Initializers
  • Lambda Expressions
  • Extension Methods
  • LINQ (only lightly as Alistair was presenting on LINQ to SQL)
  • Anonymous Types
  • Expression Trees
  • Partial methods

Phew. I'm tired all over again just looking at that list. It's a tall order to try and demonstrate 10 features in 15 minutes (that's one every 90 seconds!). Of course I took about 25 minutes so you can blame me if you pizza was cold :(

One thing that I did forget to mention (and I'm excited about it) is that with Visual Studio 2008 Service Pack 1, C# gets a background compiler! My VB.NET friends all laugh at my enthusiasm about Background Compilation and try to tempt me to the dark side. For now I love C# (and Ruby and Boo when no-one is looking).

Thanks to the other presenters Alistair Waddell, Dave Gardner and Mitch Denny. I think my brain melted at some point during the proceedings but I also think I learned some valuable stuff. Unfortunately due to technical difficulties Piers Williams was unable to show Continuous Integration with Team Build but hopefully Mitch can get him back to show us another time.

There were only a few questions last night but if you didn't make it or you've thought of some in the meantime then please leave a comment.

Saturday, 21 June 2008

MvcSupportFacility

Buying a house and dealing with various horrible sicknesses in a wide variety of family members is a great way to use up all of your spare time. That why I build my MVC applications with MvcSupportFacility.

OK in all seriousness, life has been pretty busy and I have been working on building a zero-friction environment like the one Ayende uses in his Rhino.Commons stuff. I'm sure that his is far better than mine (to be fair his has been in use longer and has been polished by more projects) but I wanted to use a different toolset and so I had to bake my own. Where Ayende uses NHibernate and Monorail, I wanted to try and use LinqToSql and ASP.NET MVC.

I've heard many people complain that LinqToSql isn't ready for the big-time but I'm trying to use it in a way that is completely abstracted away so that I can swap it out if necessary. You'll know you're using Linq but not LinqToSql. More on that next time though.

The Wolfbyte.Core.Mvc library contains an MVC ControllerFactory which uses Castle MicroKernel to create Controllers. The advantage of this is that we get IoC on our controllers. The library also contains a simple interface for registering MVC routes (IRouteRegistrar) and a simple implementation that creates the default MVC route.

In order to make it all easier to use there is a simple Castle Windsor Facility which can be initialised with a list of Controller Assemblies and RouteRegistrars from a configuration file. The facility hooks up the Controller Factory, searches through all of the Controller Assemblies for Controllers (concrete classes which implement IController) and registers them in the container and finally asks each RouteRegistrar to apply itself to the default RouteTable. Here's an example configuration file from a sample I've been working on:

<?

xml version="1.0" encoding="utf-8" ?>
<
configuration>
  <
facilities>
    <
facility id="mvc.support" type="Wolfbyte.Core.Mvc.Facilities.MvcSupportFacility, Wolfbyte.Core.Mvc" include-default-route="false">
      <
controller-assemblies>
        <
assembly>WaterCooler</assembly>
      </
controller-assemblies>
      <
route-registrars>
        <
registrar>WaterCooler.ChatRoutes, WaterCooler</registrar>
      </
route-registrars>
    </
facility>
  </
facilities>
</
configuration>

The option to include-default-route defaults to true and so it only really needs to be specified when it's false. If included, it's always the last route registrar called. All you need to do to use it is to create a Windsor Container and initialise it with the config file:

        private void InitializeIoC()
{
var Container = new WindsorContainer("windsor.config");
}



This method is being called in my global.asax.cs file. Note that we don't even need to keep a copy of the container (although you might like to) because the ControllerFactory system keeps a reference to it and uses it. Now that we have Inversion of Control going we can do things like this



    public class ChatController : Controller
{
IRepository<Conversation> conversationRepository;

public ChatController(IRepository<Conversation> conversationRepository)
{
this.conversationRepository = conversationRepository;
}

public ActionResult Index()
{
ViewData["Conversations"] = conversationRepository
.Items
.OrderBy(c => c.Name)
.ToList();
return RenderView("ConversationList");
}
}


But where does IRepository<T> come from? We'll see that one next time with the LinqToSql stuff.



For now everything is in a Google code repository here: http://code.google.com/p/wolfbyte/ so you'll need to crank out Subversion to get at it. It's all licensed under the new BSD license so do with it as you need to. As soon as my sample is finished I'll post the whole lot as a Zip-file.



P.S. As soon as the house has settled and we move in there will be pictures. Well, as soon as I can get rid of the pink carpet anyway :)


Wednesday, 14 May 2008

Masticate and Cogitate

If you're anything like me, you're passionate about programming. So passionate that you try and make other people around you as interested in programming as you are. Some days it can be frustrating to know that when your co-workers go home, they have better things to do then messing with LINQ or trying to piece together a complex design pattern.

So how do you get the people around you excited? You can buy books for them (a la Garry Shutlers library) but as a commenter on that post rightly points out, "The other developers on the team [...] need to be willing to spend time outside of work to going through the books and learn new things".

The outside of work issue is the key. What you need to do is make people interested in learning stuff inside work hours. Lunch-breaks are perfect for this. People tend to waste a good percentage of an hour slowly eating whilst WILFing* around the Internet.

For the last two weeks I've been running Lunch'n'Learn sessions every Monday. A Lunch'n'Learn is just a fancy term for sitting around on your lunch-break and watching something educational on a projector while you eat. In our case the videos have been dnrTV episodes (66 and 35). So far I've had a fairly positive response from both attendees and management and it looks like the initiative will continue with some of our other offices wanting to get in on the action.

So anyway, the next evil plan on the agenda for the Lunch'n'Learn sessions is to see if I can open them up to non-Fujitsu employees. Before I broach the subject with the boss though I wanted to gauge the community interest. So how about it? Would you you give up an hour every Monday to learn something?

*WILFing in this context means What I was Looking For and is a term used to describe the kind of browsing that starts at the search engine and ends when you realize that an hour (and probably about 30 topics) have passed by. There are other meanings.

Monday, 14 April 2008

From David Hayden - Free Training on ASP.NET MVC

 Free Training on ASP.NET MVC - ASP.NET Dynamic Data - Entity Framework - ADO.NET Data Services

Free training rules. I've just installed it and I am having a look at the MVC stuff. It seems that the only content available at the moment are the Hands on Labs but they do go into Test Driven Development along with ObjectBuilder Integration!

Sunday, 13 April 2008

I Started A Webcomic

Well I looked around and there just wasn't enough. Seriously though, in January this year I joined the Church of LOTU (A "church" dedicated to rational thinking) and during a discussion with LOTUs founder, Cameron Reilly, he said this:

Do you ever stop to think about how many people lived and died, scratching out the most meager of living from the dirt, poor, huingry, all so you could be here today, living in the land of opportunity and freedom, in middle class luxury, doing white collar "work"? I often image I am having a conversation with 1000 of my direct ancestors, having to justify to them what I'm doing with this gift they have given me.

How do you find Inspiration and Meaning? Church of LOTU Tangler Forum

It left a profound impression on me and I started to imagine how I would justify myself if my great-great-great grandfather showed up while I was watching YouTube Videos. Out of this Me and the Old Man was born. I have pre-written quite a few so hopefully I can keep going for a while. I plan to put one up every weekend so look out for next weekend (I started with two this weekend). Here's the first:

The second is on the Me and the Old Man website.

P.S. Obviously I can't actually draw and so I'm using a tool to generate these comics. For the record the tool is Toonlet. To see comics with artistic talent, I recommend you check out Little Voices. Stephen's work is being featured on the Australian ReMix 08 site :)

Friday, 11 April 2008

MVC Storefront

If you haven't seen it Rob Conery has been putting together a screen-cast series called MVC Storefront. The project has a few purposes which includes

  • Building a real MVC application (an e-commerce site)
  • Using some other current technologies (it looks like Rob plans to use LINQ to SQL and Unity but maybe not, we'll see)
  • Teaching Rob, and anyone else watching TDD practices
  • And generally inflaming the ALT.NET community into showing us all how it's done.

The videos, blogposts and commentary are at MVC Storefront and the code is available from the MVC Samples Codeplex site.

Remember if you don't like it, tell him to his face. Rob has been working very hard to make this a pretty open project and I personally am loving his approach. Even if he does have to cry himself to sleep at night.

Pipe and Filters, Fluent APIs and LINQ to SQL

Ayende issued a challenge the other day which is becoming the new Enterprise FizzBuzz. The challenges runs roughly like this: build a command-line interface for retrieving a list (the first 10) of products from some data-storage mechanism (up to the implementer) and potentially filter the list using any number of restrictions imposed in any order by passing in parameters to the application.

As the challenge fits nicely with some of the points that I have been trying to make with LINQ to SQL I thought I'd give it a go. I've chosen to implement a simple solution with LINQ to SQL and the Pipes and Filters pattern. Let's have a look at the solution. Note that you can get the complete source from the Code Gallery page.

Firstly the data storage is up to the implementer so I've gone with SQL Server so that I can LINQ to SQL things up later on. If I were a little more disciplined (or less lazy) I might have written some unit tests against a generic IQueryable<T> repository and driven my domain model out of that. Looking at what I've ended up with I might give it a go. Here is the CAL for the data-model:

Products 
<- { Name } NVARCHAR(100)
<- { Price } MONEY
<> Tags
<- { _Text } NVARCHAR(50)



Running that through Crank will spin up my database. Mental note: Write a generator for Crank to produce LINQ to SQL data context and supporting classes.



Next I had to create the LINQ to SQL DataContext. I did this by creating a new LINQ to SQL Designer Surface and dragging my three tables onto it from the server explorer. Again, I'd consider doing this another way because the object model is a bit clumsy. Here is a snapshot:



Products Challenge Data Model



I spent some time messing with the DataContext to get it to create the database and populate it with Ayende's dummy data but it's all fairly boring stuff so I won't go into it here. We now have enough to start meeting requirements. Here's is all we'd need to print ALL of the products:



foreach (var product in new ProductsDataContext().Products)
Console.WriteLine("{0} ${1:0.00}", product.Name, product.Price);


Pretty cool huh? Rob Conery has some cool Pagination stuff but I thought it was overkill for what I wanted to do so I then dropped in a some simple Pagination stuff of my own which gives us:



var productsForDisplay = new ProductsDataContext()
.Products
.InPagesOf(10)
.Page(1);

foreach (var product in productsForDisplay)
Console.WriteLine("{0} ${1:0.00}", product.Name, product.Price);




The code for the InPagesOf extension method (and the PaginatedQueryable that supports it) is in the source code. The best apart about this is that when the database query gets executed, only 10 rows (max) are returned. I started to have some problems with unordered data though I moved some things around to give a default order to all Products when retrieved. In reality I still feel that Pagination and Ordering belong in the User Interface Layer and I could have done that here.



Next up I added the IFilter interface which has a single method:



public interface IFilter<T>
{
IQueryable<T> Filter(IQueryable<T> source);
}


This should allow us to write a filter that knows nothing about the source of the data it just knows how to allow some T's through and stop other T's (it's a T-filter). Because it accepts and returns the same type we can chain filters together. This is the purpose of the generic Pipeline class. Here's it's Filter method:



public IQueryable<T> Filter(IQueryable<T> source)
{
var filteredValues = source;
foreach (var filter in Filters)
filteredValues = filter.Filter(filteredValues);
return filteredValues;
}

Notice that the Pipeline<T> is itself an IFilter<T>. This is great because it means we can take a Pipeline and another Filter and add them to a second Pipeline. This is a good example of the Composite Design Pattern.

So given that we receive a list of Tags from the command line to filter our list by we need a TagFilter that will allow Products through if they are tagged with a specific tag. Here is the Filter method from the TagFilter:



public IQueryable<Product> Filter(IQueryable<Product> source)
{
return from product in source
where product.ProductTags.Count( pt => pt.Tag.TagText == TagName ) > 0
select product;
}



This basically says that we should return any products whose ProductTags collection contains more than zero entries for the specified TagName. Now we need to construct a Pipeline of TagFilters from the tags specified in the command line. Here is a static method to do that:



public static IFilter<Product> TaggedWith(IEnumerable<string> tags)
{
var pipeline = new Pipeline<Product>();
foreach (var tag in tags)
pipeline.Filters.Add(new TagFilter(tag));
return pipeline;
}



It's in a static class called Products. Mix that in with an extension method to apply filters to an existing IQueryable<T> and now our list generation looks like this:



var productsForDisplay = new ProductsRepository()
.AllProducts
.WhichAre(Products.TaggedWith(searchTerms))
.InPagesOf(10)
.Page(1);



This is my first attempt at creating a Fluent Interface and I keep changing it. I decided to quit it and just post it already but it's much harder than I thought it would be. Has anyone seen any good guidance on this?



This is nice and Ayende showed something similar previously using IEnumerables. The best part is when you consider that LINQs deferred execution scheme changes the game. In the original IEnumerable method the pipes and filters method only really works if you can generate everything up front and then push things through the pipe. This would have meant loading all of the products into memory or writing some complicated stuff.



With IQueryable<T> and LINQ the story is different. Even though we have implemented manipulations to our collection of products in a variety of different places (TagFilter, Pipeline<Product>, PaginatedQueryable<Product>) and attached them in different ways (well mainly with extension methods but you could) they all get combined and munged into a single SQL statement which only draws back the Products that we want to display (and in the right order too). Let's turn logging on for a second and have a look at what gets produced for 2 criteria:



SELECT TOP (10) [t0].[Name], [t0].[Price], [t0].[ProductID]
FROM [dbo].[Products] AS [t0]
WHERE (((
SELECT COUNT(*)
FROM [dbo].[ProductTags] AS [t1]
INNER JOIN [dbo].[Tags] AS [t2] ON [t2].[TagID] = [t1].[TagID]
WHERE ([t2].[TagText] = @p0) AND ([t1].[ProductID] = [t0].[ProductID])
)) > @p1) AND (((
SELECT COUNT(*)
FROM [dbo].[ProductTags] AS [t3]
INNER JOIN [dbo].[Tags] AS [t4] ON [t4].[TagID] = [t3].[TagID]
WHERE ([t4].[TagText] = @p2) AND ([t3].[ProductID] = [t0].[ProductID])
)) > @p3)
ORDER BY [t0].[Price]
-- @p0: Input NVarChar (Size = 10; Prec = 0; Scale = 0) [vegetarian]
-- @p1: Input Int (Size = 0; Prec = 0; Scale = 0) [0]
-- @p2: Input NVarChar (Size = 4; Prec = 0; Scale = 0) [pg13]
-- @p3: Input Int (Size = 0; Prec = 0; Scale = 0) [0]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.21022.8



Run that against your database and you'll see only those records we were interested in being returned. I don't think that my method for filtering products by tag will scale particularly well but the idea is sound.



What do you think? Doing this we can build compose-able filtering systems for data in our database. If the client added a requirement that they wanted to filter by price-range or by the first three letters of the product name (or any other crazy thing a client may ask for) then we are well prepared to make the change by adding a PriceRangeFilter, a StartsWithFilter (and a CrazyClientRequest5Filter). Each new filter doesn't need to know where the data is and how it will be retrieved because it could work on any source of products that can be exposed as an IQueryable<Product>.



Remember that you can get the full source from the Pipelines, Filters, Fluent API and LINQ to SQL and any feedback is welcome there, here or at michael.minutillo@gmail.com