The McDev Blog

Kevin McKelvin's perspective of the Ruby world.

Windows Phone - a Few Months Later

(Update: I’ve switched to Android now with a Samsung Galaxy S.  Read more here - Android vs WP7)

At the end of February I took a leap of faith and bought an HTC Trophy - Windows Phone 7 device.  This was largely thanks to the likes of Rudi Grobler, Rob MacLean and Darrel Schreyer going on and on about how great WP7 is :)

So it’s been just short of three months, let’s take a look at real usability of the device.  I’m going to keep this one short and try avoid going into too much detail about the OS.

As I said before I got the HTC Trophy (GSMArena link).

First up it’s a delight to hold, it feels solid but it’s not bulky.  I’ve found phones with larger screens like the Desire HD with its 4.3” screen to be uncomfortable to use.  The 3.8” face of the Trophy is a perfect fit.  I suppose one gets used to the feel of the larger phones though.

Software wise the OS is solid.  It’s extremely smooth and responsive - Microsoft has spent a lot of time putting little transitions and animation effects in that just make the phone a joy to use on a day-to-day basis.  Internet Explorer and Outlook are both deeply entrenched in the device and are both a joy to use.  IE uses the same Trident rendering engine found in IE7 on PC and with the next update we’ll get the latest IE9 rendering engine (which means HTML5 support on the device - including audio and video).

Outlook is superb.  Mails viewed on the device look roughly the same as when viewed on a PC - which is a great plus.  Fonts and images are rendered accurately and with the high resolution 480x800 screen it’s crisp and clear.

The last update (NoDo) fixed the problems I was having with bing maps not loading on mobile data connections - though the included bing maps application is very weak compared to Google Maps (gMaps application in the Marketplace).

There are a few things still missing on the platform.  The lack of socket support (which is coming with the Mango update) means that we haven’t got a decent instant messenger yet.  Skype has been promised now that socket support is on its way.  I have hopes that WhatsApp will make its way to WP7 soon as well.

Overall I’m happy with the choice to go with WP7.  The phone is just as capable as any iPhone or Android device and here in South Africa cost me less than half as much as an iPhone 4.  The platform is still growing but with due patience I believe Microsoft has a viable competitor to the iPhone and Android markets.

Mac Blogging

There’s only one thing I’ve left behind in my move from PC to Mac. Windows Live Writer is widely accepted as the single best blogging app - and I’d tend to agree I really love using WLW on Windows.

I’ve started using Scribefire now - as an extension to Google Chrome on the Mac which seems pretty decent too. It’s platform agnostic being a Chrome extension so check it out regardless of which platform you’re on. http://www.scribefire.com/

Lazy Column Loading With NHibernate

Lets take a look at the following simple schema, for brevity’s sake I’ll just use three entities.

Product
+ Id int
+ Description string
+ BasePrice decimal
+ Image byte[]

Order
+ Id int
+ Lines ICollection
+ ShippingAddress string

OrderLine
+ Id int
+ Customer Customer
+ Product Product
+ LinePrice decimal
+ Qty int

From a conceptual point of view it’s great, we have a nice easy object graph to traverse and it’s pretty discoverable as well.

However lets look at it from a SQL point of view. If I were to run this code:

1
2
3
4
5
order = session.Get<Order>(orderId);
foreach (var line in order.Lines)
{
    Console.WriteLine(line.Product.Description);
}

NHibernate has to retrieve the whole Product object for every line in the order. Even with eager-fetching we would bring back the image for every iteration of the loop. If we’re going across to SQL every time, this becomes painfully slow.

In comes lazy column loading to save the day!

By adding the code lazy=”true” to the column’s XML mapping we can lazily load a single column – as below:

1
<property name="Image" lazy="true" />

This will cause NHibernate to ignore the Image column when initially building the object.

It will then issue a separate SELECT query to retrieve the image later if we access the Product.Image property.

Very cool and very easy performance tweak :)

NHibernate 3.1GA Released

Fabio Maulo’s put up the NHibernate 3.1GA release on www.nhforge.org. So far I’ve upgraded my code with minimal issues. Here are the release notes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
Build 3.1.0.GA (rev5425)
=============================

** Known BREAKING CHANGES from NH3.0.0.GA to NH3.1.0.GA

  ##### Design time #####
      * [NH-2481] - Deprecated: ISession.SaveOrUpdateCopy methods - use ISession.Merge methods instead

  ##### Run time #####
      * [NH-2481] - An exception will now be thrown when an entity references a transient entity and cascade="merge|all" is not configured on the association

  ##### Possible Breaking Changes #####
      * [NH-2461] - Signature change for IQuery.SetParameterList
      * [NH-2556] - NH is too tolerante to incorrect naming when access="field.XXX" is used

** Sub-task
    * [NH-2525] - Wrong parameter used for limit claues in MySQL

** Bug
    * [NH-1985] - NHibernate is allowing deletion of immutable objects
    * [NH-2037] - Reattaching an entity with many-to-one inside a natural-id
    * [NH-2130] - Reporting query containing sum crashes when there are no rows
    * [NH-2179] - String constants are not useable in Linq query projection
    * [NH-2203] - problem with orderby in linq query
    * [NH-2280] - LINQ Query on Composite key creates invalid SQL
    * [NH-2311] - .Any() extension method does not work in most cases
    * [NH-2362] - GroupBy with multiple fields fails with exception
    * [NH-2375] - OfType with a where clause fails with a NotSupportedException
    * [NH-2381] - Fetch clause fails with a NotSupportedException
    * [NH-2386] - Unecessary update / invalid SQL generated when collection updated with a versioned (generated) parent entity
    * [NH-2400] - Linq query fail when using contains from an empty Collection
    * [NH-2407] - Linq provider doesn't support enums in VB.NET
    * [NH-2412] - OrderBy generates an inner join instead a left join
    * [NH-2433] - When using extensions methods with generic parameters the provider uses the first use even if the generic parameter is different.
    * [NH-2441] - Logical bool values are not mapped properly (query execution returns incorrect result with SQLite)
    * [NH-2443] - Error compiling NH with ShowBuildMenu.bat -> Cannot run Tests
    * [NH-2450] - Multi Query in MySQL no longer working in 3.0 (was in 2.1)
    * [NH-2459] - LINQ provider query plan cache issue with use of type check expression .Where(o=>o is SomeType)
    * [NH-2460] - version generator is not working with DateTime2 data type.
    * [NH-2464] - NHibernate DLLs not built with optimization in 'release' mode.
    * [NH-2467] - Futures in 3.0.0.GA go bananas when using PostgreSQL
    * [NH-2470] - PersistentIdentifierBag not creating snapshot correctly for new collections.
    * [NH-2482] - SerializationException when writing object to viewstate
    * [NH-2484] - Regression - Binary Blob SerializationException - MSSQL 2k8 / varbinary(max)
    * [NH-2499] - Case statement does not handle multiple when clauses
    * [NH-2501] - Case statement does not allow a parameter in the first then clause
    * [NH-2503] - HQL subselect with addition fails
    * [NH-2507] - LINQ queries tha compare enumeration values with /checked+ compiler option throw NotSupportedException
    * [NH-2512] - QueryOver with Where clause and Take crashes
    * [NH-2524] - Linq converts enums to integers prematurely
    * [NH-2529] - Linq on Informix using take gives an exception
    * [NH-2536] - Second call to OfType don't change the query
    * [NH-2543] - IQueryOver support is not implemented for IStatelessSession
    * [NH-2549] - Disposing an Stateless Session that has already been closed causes a SessionException
    * [NH-2555] - Linq with Contains doesn't work with read only collections
    * [NH-2556] - NH is too tolerante to incorrect naming when access="field.XXX" is used

** Improvement
    * [NH-1342] - Very slow inserts for large BLOB
    * [NH-2023] - Batch operations - introduce SetBatchSize for IStatelessSession
    * [NH-2098] - Support for transaction isolation levels in stateless sessions.
    * [NH-2211] - Stateless Session Linq Support
    * [NH-2228] - Cascading StaleStateException doesn't show which Entity caused the problem
    * [NH-2425] - Cache the XmlSerializer for HbmMapping class
    * [NH-2449] - Add IStatelessSession.BeginTransaction(IsolationLevel) Method
    * [NH-2454] - Add auto-quote settings to main documentation
    * [NH-2455] - Centralization of proxy check to IProxyFactoryFactory (better support for static proxy)
    * [NH-2457] - Ability to use DetachedCriteria from stateless session
    * [NH-2461] - Allow parameter list as ienumerable and simplify IQuery
    * [NH-2471] - ShowBuildMenu.bat and Windows XP
    * [NH-2481] - Merge can fail when there is a transient entity reachable by multiple paths and at least one path does not cascade on merge
    * [NH-2502] - Fetch/Cacheable Should be Allowed to be Called Anywhere
    * [NH-2508] - Deprecate the ISession.SaveAndUpdateCopy API
    * [NH-2522] - ILMerge Antlr and ReLinq
    * [NH-2537] - Implement camelcase-m-underscore naming strategy
    * [NH-2557] - Improves log message, of CustomType not serializable, by adding additional data

** New Feature
    * [NH-908] - Implement read-only entities
    * [NH-2410] - Port  from Hibernate

** Patch
    * [NH-2153] - Unused parameter in SetCommandTimeout method in DriverBase
    * [NH-2172] - Unrecognised method call in expression when using QueryOver queries in VB.Net
    * [NH-2445] - Add IStatelessSession.IsOpen and IStatelessSession.IsConnected
    * [NH-2473] - EntityName + inheritance doesn't work
    * [NH-2474] - Xsd for  on subclass missing
    * [NH-2478] - Docs for 
    * [NH-2513] - SetMaxResults issue with DB2400Dialect

** Task
    * [NH-2506] - Fix first example of ternary association in documentation
    * [NH-2541] - Upgrade ReLinq to 1.13.93

The Only Excuse for Skipping TDD

Misko Hevery said in one of his excellent Google Clean Code Talks – the only valid excuse for not doing unit testing is not knowing how to write testable code.

Some time ago I wrote a Windows service that handled some XML imports. The whole thing was built using Reactive Extensions (Rx) and LINQ-to-XML for parsing.

Another developer had since taken over the maintenance of the project for the last few months but I had some more work to do on it today. Lo and behold my shiny unit test suite (36 tests, not something to write home about but it wasn’t all that complex) had less than 10 tests passing. In maintenance there had been a total disregard for updating the existing tests as requirements had changed.

Horrified – particularly since you can’t just hit F5 to load up a Windows service – I started looking through the failing tests. There hadn’t been all that many changes, a couple of validation checks here and there now throwing FormatExceptions and a new XML tag had been added to the XSD and the parse tree. It took a whole of about 20 minutes to get the test suite back to a passing state – meeting the requirements of the code that had changed including Asserting the correct FormatExceptions were being thrown.

Just another case where Misko was right.

HTC Trophy on Its Way

So after being coerced into trying a Windows Phone 7 device out by Rudi Grobler and a few others, I’ve taken the leap of faith and placed an order for my HTC Trophy today Open-mouthed smile

Hopefully it will arrive by the middle of next week – more posts to follow soon.

Powershell Builds With Psake

James Kovacs released Psake (pronounced sake, as in Japanese rice wine) – a Powershell based DSL for automated builds.

After source control, the next most important step of infrastructure is a one step build process. You should ideally have a scheduled build that begins with a clean checkout of your code from your version control server and does a build of every configuration of your software that will be delivered to customers. This includes localizations, trial versions and alternate build configurations (using #if/endif). Your unit tests should also be run as part of the build and the results must be made available.

We had been using the Ruby Rake build system for a year now, but the dependency on Ruby has always been a downfall because of Ruby’s boot up time on Windows. Removing my dependency on Ruby was the main motivation that I’ve swapped all of my builds over to using Powershell and Psake.

What I love the most is the clarity of the script, here’s a sample of the compile task in a build script.

1
2
3
4
5
6
7
8
task Default -depends Compile
task Init { ... }
task Compile -depends Init {
  & msbuild "$proj_file" "/p:OutDir=$build_dir\\" /p:Configuration=Release   
  if ($lastExitCode -ne 0) {
     throw "Error: Failed to execute msbuild"
  }
}

Much nicer than the XML used for NAnt builds!

In Rake you have access to the Ruby backend, with Psake you have the whole of Powershell to work with. Best of all it integrates pretty seamlessly with build servers such as TeamCity and Hudson.

TechEd Africa 2010

It’s been a week now and I’ve been able to reflect on Tech Ed Africa 2010

This year again Microsoft brought out some of the big local and international names to speak to us about the newest Microsoft innovations & technologies in a variety of breakout sessions. Being my second year at Tech Ed and having attended so many ‘lap around LINQ’ or ‘new features in C# 4.0’ sessions already over the last year, this year’s Tech Ed allowed me to formalise my opinion on development, rather than come and just learn about all the new bells and whistles.

First up I’m still convinced my choice to stick to the Microsoft .NET platform has been a solid one for the next 5 years. There’s lots still happening, lots still to come and a great demand for developers who have a thorough understanding of how the framework works.

In my day to day job I do a lot of work connecting to databases. Tech Ed showed me that while Entity Framework has come a distance since its inception with .NET 3.5SP1 and now with .NET 4, it still has a lot of catching up to do before it can truly compete with guys like NHibernate.

My favourite speaker – Bart de Smet had some great and informative talks. I had been dying to learn more advanced LINQ concepts, such as building providers for LINQ without falling back to IQueryable/IEnumerable.

I had a great time networking with peers. It wasn’t all about sitting in the breakout sessions and sponging. It was cool to meet up with some of the presenters from Devs4Devs as well.

To close I must just say thanks to MTN for their coffee stand at the back corner of the expo… You guys kept us alive for 3 days!

An Enterprise Language?

I heard an interesting comment at Devs4Devs on Saturday. We were talking about Ruby when a point was raised - that an enterprise application handling sensitive information (such as credit card details) should not be written in a scripting language. The argument was that a compiled language such as C# is far more appropriate.

While I love C# and the .NET Framework as a whole, I disagree with this notion.

Ruby is a powerful language and its been getting a lot of attention lately - particularly with the popularity of Rails in the last few years. Several of the more common websites have been written using Rails (TekPub, Yellow Pages, Shopify, GitHub) - it isn’t just the socialites like Twitter anymore. These guys have proven that Ruby is a stable platform worth considering.

The natural argument is that C# compiles to IL code which is more secure than a script. Granted it’s easier to open up a .rb file and edit a statement or two than open up a .dll or *.exe file, but with Mono.Cecil and the Reflexil plugin to Reflector, it’s also possible to open up an IL assembly and modify it. You can even save a patched version with the same strong name key!

It comes down to using the right tool for the job. C# is a great language and the .NET Framework supports it well, but sometimes the scripting languages like Ruby or Python are more concise and can save time. Be pragmatic, analyze the situation and choose the right tool for the job. C# is not a silver bullet, nor is Ruby.

Introducing NHibernate 3 (Devs4Devs)

Thanks to all who attended my 20 minute introduction to NHibernate 3 this morning. Here are my slides from the presentation

Introducing_NHibernate3.pptx

And the code samples:

Loquacious SessionFactory configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var config = new Configuration();

config.DataBaseIntegration(db =>
                         {
                             db.ConnectionStringName = "ConnectionString";
                             db.Dialect<MsSql2008Dialect>();
                         })
  .Proxy(p => p.ProxyFactoryFactory<ProxyFactoryFactory>())
  .SetProperty(NHibernate.Cfg.Environment.CurrentSessionContextClass, "web")
  .AddAssembly(typeof (Artist).Assembly);

HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize();

return config.BuildSessionFactory();

Querying the Music Store

1
2
3
4
5
6
7
8
9
10
11
12
13
IMusicStoreContext context = MvcApplication.GetCurrentRequestSession();

using (var tx = context.Session.BeginTransaction(IsolationLevel.ReadUncommitted))
{
    var artists = (from artist in context.Artists
                  where artist.Name.StartsWith("a")
                  orderby artist.Name descending
                  select artist).Fetch(x => x.Albums).ToList();

    tx.Commit();

    return View(artists);
}

One change I’ve made here is the call to .ToList() when selecting the artists. By enumerating the collection, we’re forcing NHibernate to get the objects from the database - to ensure that the select happens inside the transaction boundary.

The View code

1
2
3
4
5
6
7
8
9
10
<% foreach (var artist in Model)
   { %>

   <tr>
   <td><%: artist.Name %></td>
   <td><%: string.Join(", ", artist.Albums.Select(a => a.Title)) %></td>
   </tr>

<%
   } %>