Monday, 20 December 2010
Time to Move On
I have now worked at the same software company for 10 years. I started as a developer and worked my way up to become the Development Director. Over the past 2 years I have transformed the company's development department with changes that will continue to help it deliver working software long after my departure. I moved us from a classic waterfall model, with manual deployment, to an Agile process (Scrum) incorporating SOLID principles, unit tests, continuous integration & build servers and, nearly, fully automated deployment of a dozen commercial websites. I am very proud of the changes I have made and I leave a company which now scores 9/12 on the Joel Test.
I'm giving all that up.
My own, personal, transformation didn't stop there though. Over the last year I have become more and more excited by the business side of software development. I spent countless evenings and weekends consuming all I could find from such luminaries as Dharmesh Shah, Seth Godin, Geoffrey A. Moore, Eric Ries etc. The possibility that a 'simple developer' could expand his horizons and create something more than just an application was exhilarating. During 2010 I had, and still have, a hat-full of ideas; one of which bubbled to the top of the stack quite effortlessly. After the creation of an MVP, a few public demos & pitches and some incredible user feedback I'm now ready to go for it. I've learnt all I can without taking that final step off the cliff.
In 2011 I'll be looking for more people to come join me in helping change the way people interact with social media - starting with TweetPivot. If you want to know what that involves you'll have to follow me on twitter or even just come talk with me!
Finally, I'd like to thank some local groups (e.g. Pitch and Mix), some global groups (e.g. Microsoft's BizSpark program) and an innumerable number of individuals who have supported and encouraged me thus far. Almost any hour of the day can feel like a lonely 3am when you're an entrepreneur and having people that can help you refocus is priceless.
So, wish me luck. 2011 is going to be scary-as-hell but, wow, what a sweet looking ride!
Thanks,
Chris
Saturday, 16 October 2010
What are you scared of?
I was unable to attend the Business of Software in Boston last month so was excited to see that Eric was doing a whistle-stop talk last night at TechHub, London. After a, not too inconvenient, train and tube journey I found myself in a vibrant room packed with budding entrepreneurs. The 'formal' part of Eric's talk was mostly content that I'd already seen online, so no major surprises there. What always makes these events unique, though, is the interaction with the people that turn up. The Q and A hand-raising session was very busy but I did manage to talk with Eric afterwards. Here's what I asked him:
As is often the case in this field there was no short, definitive answer! What follows is paraphrased.
Imagine you're back at IMVU circa 2004, but this time you know what you know now. You've created a Minimum Viable Product for just one Instant Messaging network that's already getting good traction. Geoffrey Moore would have probably suggested that you capture your beachhead in that first IM. Would you agree with that or would your next move be to create an MVP for each IM?
Without any prepared answer for this one I said "Well, probably someone else copying us". Eric rocked back, head shaking "No." I got the impression that he hears this 'wrong answer' often.
Eric: "What are you scared of?"
Me: "Uh, nothing. I'm an entrepreneur creating a startup - by definition am I not fearless?"
Eric: "No. What's your biggest fear?"
The gist of Eric's response was that I needed to look at my assumptions. I'd assumed that because my product 'works' for one social network that it will automatically be greeted in the same manner on all the other networks. The way you reassess these assumptions is to do more measuring against hard targets e.g. I'm currently at 10, I think I can get to 20 by time t. If the increments are 10, 12, 14... you're probably gonna make it, but if they're 10, 11, 11.5... you might have a problem. Testing your worst fears early is key.
MBAs are seen as a hindrance in the startup world, but the remainder of Eric's lesson to me did seem to cross a chasm into this territory. The basics of the Lean Startup are pretty straight forward: iterate through 'the loop' as quickly as you can and learn what works. Simple. Once you get into the guts of it, by talking with Eric, it starts sounding a whole lot more complicated. If I wanted an MBA I would have studied for one. I like my vision of the startup as having less politics, less overheard and, by extension, less rigor; but, again, this is an assumption.
I am, however, extremely grateful for the way Eric makes you question all of your assumptions - sometimes to the point of your own existence!
So, what is my biggest fear? After some considerable thought I think it's that people won't like what I do. I fully agree with Eric's observation that the best way to utterly deject developers is to have them spend time and energy creating something only for it to never see the light of day. Lean helps us avoid wasting everyone's time and for that, alone, it is worth pursuing.
Tuesday, 12 October 2010
Standardising String Cryptography
Crypto Rule #1 seems to be "Don't roll your own.". OK fine, so give me something pre-rolled. C# / .Net doesn't contain the method I was looking for:
string Encrypt(string plainText, string password, string salt);
Luckily, @blowdart came to my rescue and posted this code. Now, there's a few things worth pointing out about this:
- Barry's book sales suggest he might actually know what he's doing, but who else amongst us has the confidence and knowledge to say that it's safe and reliable?
- Did anyone read the code and think "actually, TripleDES would suit my needs, not Rijndael"?
So, given that a) it's hard to do correctly and b) this standard method would probably suit 99% of developers 99% of the time why, then, do we not find this in the BCL? If I want to open a file I can use System.IO.File.OpenRead(string path). If I need to take more control over the process I can use a StreamReader and dictate encodings etc.
I've included my final code below (you deserve something for reading this far!). Porting it into Silverlight simplifies matters - mainly because there's only one implementation of SymmetricAlgorithm. "But Barry defaulted to Rijndael. Does this mean that Silverlight is less secure than standard .Net applications?" Another crypto conundrum!
I do feel that this functionality needs abstracting behind a simplified facade. I genuinely don't care how long my initialisation vector is or whether I'm using DES, DoubleDES or even TripleDES. Cryptography is simply a tool for hiding stuff and it should be easier, safer and more confidence-imspiring than it currently is. @blowdart's subsequent tweet kinda sums up the complexity of the subject. Signatures, really? You might just as well have asked me if I wanted tweeters with it! #referenceToOldJoke
using System; using System.IO; using System.Security.Cryptography; using System.Text; public static class CryptographicExtensions { public static string Encrypt(this string plainText, string password, string salt) { var algorithm = new AesManaged(); algorithm.SetKeyAndIV(password, salt); return Encrypt(algorithm, plainText); } public static string Encrypt(this string plainText, ref byte[] key, ref byte[] iv) { if (string.IsNullOrEmpty(plainText)) return plainText; var algorithm = new AesManaged(); if (key != null) algorithm.Key = key; else key = algorithm.Key; if (iv != null) algorithm.IV = iv; else iv = algorithm.IV; return Encrypt(algorithm, plainText); } private static string Encrypt(SymmetricAlgorithm algorithm, string plainText) { var plainTextAsBytes = Encoding.UTF8.GetBytes(plainText); using (ICryptoTransform transform = algorithm.CreateEncryptor()) { using (var outputStream = new MemoryStream()) { using (var inputStream = new CryptoStream(outputStream, transform, CryptoStreamMode.Write)) { inputStream.Write(plainTextAsBytes, 0, plainText.Length); inputStream.FlushFinalBlock(); var cipherText = outputStream.ToArray(); return Convert.ToBase64String(cipherText); } } } } public static string Decrypt(this string cypherText, string password, string salt) { var algorithm = new AesManaged(); algorithm.SetKeyAndIV(password, salt); return Decrypt(algorithm, cypherText); } public static string Decrypt(this string cypherText, byte[] key, byte[] iv) { if (string.IsNullOrEmpty(cypherText)) return cypherText; if (key == null) throw new ArgumentNullException("key"); if (iv == null) throw new ArgumentNullException("iv"); var algorithm = new AesManaged() { Key = key, IV = iv }; return Decrypt(algorithm, cypherText); } private static string Decrypt(SymmetricAlgorithm algorithm, string cypherText) { var cipherTextAsBytes = Convert.FromBase64String(cypherText); using (ICryptoTransform transform = algorithm.CreateDecryptor()) { using (var outputStream = new MemoryStream()) { using (var inputStream = new CryptoStream(outputStream, transform, CryptoStreamMode.Write)) { inputStream.Write(cipherTextAsBytes, 0, cipherTextAsBytes.Length); inputStream.FlushFinalBlock(); var plainTextAsBytes = outputStream.ToArray(); return Encoding.UTF8.GetString(plainTextAsBytes, 0, plainTextAsBytes.Length); } } } } public static void SetKeyAndIV(this SymmetricAlgorithm algorithm, string password, string salt) { string paddedSalt = salt; while (paddedSalt.Length < 8) paddedSalt += salt; var rfc2898 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(paddedSalt)); algorithm.Key = rfc2898.GetBytes(algorithm.KeySize / 8); algorithm.IV = rfc2898.GetBytes(algorithm.BlockSize / 8); } }
Tuesday, 21 September 2010
Great, another Pivot!
If that wasn't enough we now have a fifth! The final Windows Phone 7 SDK launched a couple of days ago and contains, wait for it, a Windows Phone 7 Pivot Application template.
I work with two of these (the Lean Startup and Live Labs) and it's already confusing trying to explain to potential customers which pivots our business uses.
Seriously, no more. Please?
Maybe this is a compelling reason for companies to trademark their products?
Wednesday, 18 August 2010
A Rebuke of Twifficiency
Unwittingly, or not, James had violated one of Twitter's codes of practice re: abuse of privileged access to accounts. Specifically, the application automatically tweeted a message from the user's account in the format:
My Twifficiency score is x%. What's yours? http://twifficiency.com
Generally, this annoyed people but, because of its viral nature, the term "Twifficiency" started trending worldwide.
Now, can we please get some perspective on this?
Yes, it trended. Yes, it spread like wildfire. Yes, everyone's talking about it; but so what? This is the Twitter version of "Britain's Got Talented X-Factor for me Nan". It was hugely popular but, ultimately, worthless. Shouldn't we be discouraging this kind of sensationalism and, instead, be encouraging the trending of remarkable products (to quote Seth Godin). The mere fact that it trended says nothing about its worth - because it trended itself automatically, virally. This is the complete opposite of something that trends because users want to tell their friends about it. In fact self-promotion obfuscates this valuable measure.
James is, undoubtedly, a very nice chap who was just experimenting with an idea, but for some people to be considering offering him a job! Seriously, no. Stop.
Tech Crunch hits the nail on the head in their blog post and I would like to add to that by pointing out that Stephen Fry only scored 3%!!!
Now, if James had created the site with an opt-in option for re-tweeting (as opposed to its new opt-out option) and it had still trended; then, wow! That would have been a truly remarkable feat. The fact is, though, that you've removed any meaningful way of measuring your success.
I have no doubt that James will go on to great things but, I'm afraid, I think he should be infamous, not famous, for Twifficiency.
Monday, 16 August 2010
Immutable, Auto-Implemented Properties in C#
Until recently I, like most other developers had been creating my properties like this...
public class Person { public DateTime BirthDate { get; private set; } public string FavouriteSong { get; set;} public Person(DateTime birthDate) { BirthDate = birthDate; } }
Pretty straight forward - the BirthDate is required and must be set during construction of the object whilst the FavouriteSong is optional. The other reason for writing properties this way is simplicity and code succinctness. It also allows more 'interesting' properties to be more easily identified. For instance, this new Age property stands out from the other two and 'asks' to be unit tested.
public class Person { public DateTime BirthDate { get; private set; } public string FavouriteSong { get; set;} public int Age { get { var now = DateTime.Today; var age = now.Year - bday.Year; if (bday > now.AddYears(-age)) age--; return age; } } public Person(DateTime birthDate) { BirthDate = birthDate; } }
I should, however, be making the BirthDate property immutable - it isn't gonna change! This does make the code less neat though...
public class Person { private readonly DateTime _birthDate; public DateTime BirthDate { get { return _birthDate; } } public string FavouriteSong { get; set;} public int Age { get { var now = DateTime.Today; var age = now.Year - bday.Year; if (bday > now.AddYears(-age)) age--; return age; } } public Person(DateTime birthDate) { _birthDate = birthDate; } }
Auto-implemented properties are a .Net construct and are used as a template, at design-time, to create a 'real' pair of _get _set methods. So, what I'd like to be able to do is this ...
public class Person { public DateTime BirthDate { get; private readonly set; } public Person(DateTime birthDate) { _birthDate = birthDate; } }
Anyone from Microsoft like to chime in?
Wednesday, 11 August 2010
I've been scammed! A follow-up to Twitter Auth Issues
Having written the above post over a month ago and considering myself to be quite net-savvy, I'm hugely embarrassed and mortified to admit that I've just been victim to a Twitter-related scam. This is the scam site that duped me: http://www.ipadappstesting.com/. It's safe to browse to it - JUST DON'T LOG IN!
I received a Twitter Direct Message (DM) from a trusted friend that invited me to go to the site so that I could sign up to be an iPad tester. At the end of the test period I would get to keep the hardware. Superb! Yeh, right.
My spidey-senses were working well enough that I didn't complete the in-depth financial survey they put in front of me. What did happen, however, was their servers sent DMs to, presumably ALL, my friends inviting them to do the same. Needless to say that this was without my knowledge - let alone my consent.
Twitter, seriously guys, this needs to change quickly otherwise you're going to go the way of Facebook.
The access granted to my account for an application needs to be segmented and I need to have the ability to REVOKE any aspect I'm not entire happy with at login time. For instance, the shill application in question should have had to request DM read / write access during their registration with Twitter. This should then have appeared as a checkbox on the Twitter OAuth screen. I would then have unchecked it.
Feeling rather violated now but, hey, how was I to know? I currently just have to put my trust in the application developers and I don't think that's either fair or sustainable.
Monday, 12 July 2010
Faking PivotViewer in Blend 4
"Error HRESULT E_FAIL has been returned from a call to a COM component."
This problem does not occur in Visual Studio 2010 (VS2010) - although the page view does look a little suspect. Some people have suggested commenting out the PivotViewer element when opening the page in Blend, but there's a much better approach - fakes.
First, create a new "Silverlight Class Library" project in VS2010 called FakePivot and, once loaded, delete the Class1.cs file. Now add a new Code File called PivotViewer.cs with the following starting code:
using System; namespace FakePivot { public class PivotViewer { } }
Next add a reference to the PivotViewer library (System.Windows.Pivot.dl) and the SharedUI library (System.Windows.Pivot.SharedUI.dll). If you don't find them under the .Net tab you can browse for them at <Program Files>\Microsoft SDKs\Silverlight\v4.0\PivotViewer\Jun10\Bin\. Now update your PivotViewer class to look like this...
using System; namespace FakePivot { public class PivotViewer : System.Windows.Pivot.PivotViewer { } }
Right click on Microsoft's PivotViewer and choose "Go To Definition". You should now see the following metadata file:
#region Assembly System.Windows.Pivot.dll, v2.0.50727 // C:\Program Files\Microsoft SDKs\Silverlight\v4.0\PivotViewer\Jun10\Bin\System.Windows.Pivot.dll #endregion using System; using System.Collections.Generic; using System.ComponentModel; using System.Resources; using System.Windows; using System.Windows.Browser; using System.Windows.Controls; namespace System.Windows.Pivot { [ScriptableType] [TemplatePart(Name = "PART_Container", Type = typeof(Grid))] public class PivotViewer : Control, INotifyPropertyChanged { public PivotViewer(); public PivotViewer(ResourceDictionary colorScheme); public IDictionary<string, IList<string>> AppliedFilters { get; } public int CollectionItemCount { get; } public string CollectionName { get; } public Uri CollectionUri { get; } public string CurrentItemId { get; set; } public ICollection<string> InScopeItemIds { get; } public string SortFacetCategory { get; } public string ViewerState { get; } public event EventHandler CollectionLoadingCompleted; public event EventHandler<CollectionErrorEventArgs> CollectionLoadingFailed; public event EventHandler<ItemActionEventArgs> ItemActionExecuted; public event EventHandler<ItemEventArgs> ItemDoubleClicked; public event EventHandler<LinkEventArgs> LinkClicked; public event PropertyChangedEventHandler PropertyChanged; protected virtual List<CustomAction> GetCustomActionsForItem(string itemId); public PivotItem GetItem(string id); public void LoadCollection(string collectionUri, string viewerState); public override void OnApplyTemplate(); public static void SetResourceManager(ResourceManager resourceManager); } }
What we want to do next is extract the interface for the standard PivotViewer class. If you have a refactoring tool I'd use it, otherwise you just have to do it manually. Either way, we should now have an interface in our project called IPivotViewer.cs ...
using System; using System.Collections.Generic; using System.ComponentModel; using System.Windows.Pivot; namespace FakePivot { public interface IPivotViewer : INotifyPropertyChanged { IDictionary<string, IList<string>> AppliedFilters { get; } int CollectionItemCount { get; } string CollectionName { get; } Uri CollectionUri { get; } string CurrentItemId { get; set; } ICollection<string> InScopeItemIds { get; } string SortFacetCategory { get; } string ViewerState { get; } event EventHandler CollectionLoadingCompleted; event EventHandler<CollectionErrorEventArgs> CollectionLoadingFailed; event EventHandler<ItemActionEventArgs> ItemActionExecuted; event EventHandler<ItemEventArgs> ItemDoubleClicked; event EventHandler<LinkEventArgs> LinkClicked; PivotItem GetItem(string id); void LoadCollection(string collectionUri, string viewerState); } }
Next, we want to go back to our PivotViewer class and make it extend Control and implement our new IPivotViewer interface like this...
using System; using System.Collections.Generic; using System.Windows.Pivot; using System.ComponentModel; using System.Windows.Controls; namespace FakePivot { public class PivotViewer : Control, IPivotViewer { public IDictionary<string, IList<string>> AppliedFilters { get { throw new NotImplementedException(); } } public int CollectionItemCount { get { throw new NotImplementedException(); } } public string CollectionName { get { throw new NotImplementedException(); } } public Uri CollectionUri { get { throw new NotImplementedException(); } } public string CurrentItemId { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public ICollection<string> InScopeItemIds { get { throw new NotImplementedException(); } } public string SortFacetCategory { get { throw new NotImplementedException(); } } public string ViewerState { get { throw new NotImplementedException(); } } public event EventHandler CollectionLoadingCompleted; public event EventHandler<CollectionErrorEventArgs> CollectionLoadingFailed; public event EventHandler<ItemActionEventArgs> ItemActionExecuted; public event EventHandler<ItemEventArgs> ItemDoubleClicked; public event EventHandler<LinkEventArgs> LinkClicked; public event PropertyChangedEventHandler PropertyChanged; public PivotItem GetItem(string id) { throw new NotImplementedException(); } public void LoadCollection(string collectionUri, string viewerState) { throw new NotImplementedException(); } } }
OK, I know, I know - this is pretty convoluted. But, what we now have is a 'real' Silverlight control that exposes the same interface as Microsoft's control. Here's how to use it...
Let's add a new "Silverlight Application" project to our solution - we'll call it PivotViewerApp. For this example we don't need to "Host the Silverlight application in a new Web site". Now add three references to this project (2 of which we already added to our FakePivot project):
- System.Windows.Pivot
- System.Windows.Pivot.SharedUI
- Our FakePivot project
VS2010 should have opened the MainPage.xaml file in "split view" mode. Let's add some references to our 2 namespaces of interest (pivot and fakepivot) and then create our pivot control...
<UserControl x:Class="PivotViewerApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:pivot="clr-namespace:System.Windows.Pivot;assembly=System.Windows.Pivot" xmlns:fakepivot="clr-namespace:FakePivot;assembly=FakePivot" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <Grid x:Name="LayoutRoot" Background="White"> <pivot:PivotViewer x:Name="pivotViewer1" /> </Grid> </UserControl>
If you now right click on MainPage.xaml in the Solution Explorer and choose "Open in Expression Blend" you'll see the exception I mentioned at the start of this post. So let's go back to VS2010 and change the namespace of our pivotViewer1 control from pivot to fakepivot...
<UserControl x:Class="PivotViewerApp.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:pivot="clr-namespace:System.Windows.Pivot;assembly=System.Windows.Pivot" xmlns:fakepivot="clr-namespace:FakePivot;assembly=FakePivot" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <Grid x:Name="LayoutRoot" Background="White"> <fakepivot:PivotViewer x:Name="pivotViewer1" /> </Grid> </UserControl>
Because both namespaces contain a PivotViewer class our xaml is perfectly happy and the solution will still compile. Now go ahead and reopen the same file in Blend - ta dah, no exceptions.
The real beauty of this approach, however, becomes evident when working with the fake control in Blend. Because it exposes exactly the same events, methods and properties as the Microsoft control we can use the Blend UI to hook into these. Selecting pivotViewer1 in our Objects and Timeline window we can then click the Events button in the Properties tab and see all the events that we'd expect our real control to expose.
Once we've done your design work in Blend we, obviously, have to revert our fakepivot namespace to pivot before we can build anything useful. One option is to go back to VS2010 to do this. However, if you hide the Design window in Blend and just have the XAML window visible you can change it there and successfully compile it.
Hopefully, that wasn't too complicated and, once you have your FakePivot library, you can reuse it in any related projects. If anyone want me to do a video of this process please let me know in the comments.
Finally, click here to download the example solution for this post.
Tuesday, 6 July 2010
Social Network Authorisation Needs to Change
Imagine my [surprise | outrage | fury] (you choose!) when I discovered that both apps had posted public comments from my account. WTF!? Both used the familiar template of 'I have just used [appX] to do [functionY]. Go to [urlA] to try it yourself.'.
OK, so nothing malicious in that - but I didn't authorise either of these posts. Facebook does give you the ability to deny an application from posting in your name, but only after you've installed it. If the app posts immediately there's nothing you can do about it.
Now, don't get me wrong, good applications deserve to be blown along on the virtual word-of-mouth jet stream; but, and here's the critical bit:
"It should be my decision to publicise my usage of your site."
At TweetPivot we made a very conscious decision to enable a user to promote our site easily but not to presume that that's what they wanted. If the site's good enough they will, but automatically doing it for them removes any worth from the act.
So, what should happen now?
Well, you have to apply to Twitter if you want your application to be able to use their OAuth process. At that point you are asked whether your application requires read-only or read-write access to users' accounts. When I enter my details into the popup OAuth window I want to be told whether I'm giving write rights to the app and, if that's not acceptable to me, I want to be able to decline that 'write' request. If you want me to try out an application that I know has no reason to write to my account then I need confirmation that you can't.
I would hate to see the Twitter authentication process get as complicated as Facebook's became; but it does need improving. The API that all 3rd-parties hook into has very specific, well defined methods. Developers should have to declare, individually, which ones they need to invoke. For instance, if I gave you read-only access to my account how can I be sure that you haven't just farmed off all my private Direct Messages?
Ultimately, this is going to be bad news for application developers that require integration to social networks. The next time I'm asked to try something like this I might hesitate. The time after that I might decline. Good developers are going to be punished and their great apps ignored by the unacceptable actions of the few.
Friday, 18 June 2010
Adding Twitter Search to Chrome
First, right click on the Chrome Omnibox and choose 'Edit search engines...'. Alternatively, go to Chrome Options and click on the 'Manage' button in 'Default search:' area. Either way you should now be looking at the 'Search Engines' dialog...
Click the Add button to create the following search engine...
Name: Twitter User
Keyword: @
URL: http://twitter.com/@%s
Click OK.
Next, click the Add button again to create our second search engine like this...
Name: Twitter Hashtag
Keyword: #
URL: http://twitter.com/search?q=%23%s
Click OK and then close any remaining open dialog windows.
So now, whenever you start typing the @ symbol Chrome will activate the Twitter User search
and whenever you want to search for a hashtag just start typing with the #symbol...
Tuesday, 8 June 2010
If Klingons Wrote Software
I've written a lot of applications targeting all manner of media and platforms - but they all had the same sentiment: "If we only get this right, everyone will buy it". I now think this is wrong. What we should be thinking is "This is a pile of crap and no-one's gonna buy it".
"What?! You mean we should actual aim to fail?"
No.
What I mean is this: if you assume that your software is probably worthless then this changes the way you write it. Your job now becomes "I'm probably gonna fail, so how do I fail quickly?". Eric Ries talks about failing products not equating to failing companies.
So, your business plan becomes "I have a lot of ideas for products. One of them might succeed. How do I quickly discover which one that is without wasting time and money on those that no one is interested in?"
What does this mean in practice? Here's a small sample list:
- Minimum Viable Product. Get it out there! It'll be crap, but do it and do it quickly.
- Measure everything. How do you know which parts of your application people are interested in if you don't capture that information?
- Smoke Tests. See how many people click on the fake 'Buy It Now' button. That tells you more than any theoretical pricing model or traditional market research.
- Pivot. You might have the basis of a great idea. Don't be fixated on what you think it should do - listen to your users.
The Lean Startup movement has radically changed how I develop software and it's not just for startups. Any project, even in the enterprise, that has a level of uncertainty will fit this model.
For more information check out Eric Ries' blog.
Introduction to Code Contracts
Now, let me start by saying "I love Code Contracts". They will save you time, code and headaches if you implement them correctly. Rather than just talk theory I thought it would be more informative to go through a worked example together...
The Bank Demo
We've been asked to add a simple feature to an existing banking application that allows transfers to be made from one account to another. Here are the, entirely sensible, starting classes we've created...
interface IAccount { string Name { get; } double Balance { get; } } class BankAccount : IAccount { string Name { get; set; } double Balance { get; set; } } static class TransferService { public static void TransferMoney(IAccount from, IAccount to, double amount) { from.Balance -= amount; to.Balance += amount; } }
Well, that would work - mostly; but we have no exception handling or validation. So, here's how we would normally amend that...
Ugly Validation
interface IAccount { string Name { get; } double Balance { get; set; } } class BankAccount : IAccount { // Name is required, so add it to the constructor... public BankAccount(string name) { if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name"); Name = name; } public string Name { get; private set; } public double Balance { get; set; } } static class TransferService { public static void TransferMoney(IAccount from, IAccount to, double amount) { // None of the accounts can be null and the amount must be positive... if (from == null) throw new ArgumentNullException("from"); if (to == null) throw new ArgumentNullException("to"); if (from.Balance < amount) throw new ArgumentOutOfRangeException("from account does not have enough money to transfer"); from.Balance -= amount; to.Balance += amount; } }
Ok, so that's better; but we're starting to really muddy our previously clean classes with all the new validation code. The other problem is that the interface (IAccount) doesn't define the full contract. What we actually want to enforce is 'has a Name property', 'has a Balance property' AND 'the Name property cannot be empty'. Clearly there's no mechanism in the language to specify our final requirement - but it's important. To fix this we find ourselves leaking contract information into classes - in this case our BankAccount class. Now, we could solve this by creating an abstract Account class but then the TransferService would have to change as well and, to be honest, you may as well just get rid of the interface because you cannot use it without the abstract class.
Whilst we've been thinking about this the 'boss' reminds us that the Balance on any account can never be negative. OK, well that destroys our nice, clean auto-implemented property! The BankAccount class now needs to be amended to this...
class BankAccount : IAccount { // Name is required, so add it to the constructor... public BankAccount(string name) { if (string.IsNullOrEmpty(name)) throw new ArgumentNullException("name"); Name = name; } public string Name { get; private set; } private double _balance; public double Balance { get { return _balance; } set { if (value < 0) throw new ArgumentOutOfRangeException("Your Bank does not allow your account to be negative."); _balance = value; } } }
I think I prefer the cleanness of our original BankAccount class - how do I get that back?
Well, wouldn't it be the best thing you'd heard this month if you could define the whole of your contract just via the interface? 3rd party developers could then create their own account classes to use in your Transfer Service without you having to tell them about the extra Name or Balance validation. This is where Code Contracts come in.
Contract Requirements
The Contract class in the System.Diagnostics.Contracts namespace has a number of static methods that can be used in a similar way to assertions in unit tests. With them we can tidy up our validation code...
interface IAccount { string Name { get; } double Balance { get; set; } } class BankAccount : IAccount { // Name is required, so add it to the constructor... public BankAccount(string name) { Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(name)); Name = name; } public string Name { get; private set; } private double _balance; public double Balance { get { return _balance; } set { Contract.Requires<ArgumentNullException>(value >= 0, "Your Bank does not allow your account to be negative."); _balance = value; } } } static class TransferService { public static void TransferMoney(IAccount from, IAccount to, double amount) { Contract.Requires<ArgumentNullException>(from != null, "from cannot be null"); Contract.Requires<ArgumentNullException>(to != null, "to cannot be null"); Contract.Requires<ArgumentNullException>(from.Balance >= amount, "'from' account does not have enough money to transfer"); from.Balance -= amount; to.Balance += amount; } }
So now you're thinking "Well, that's nice. My code is a little tidier, but I've effectively just changed syntax.". And you'd be right - almost. The benefit to using the Contract methods is that they can be evaluated at compile time and during static analysis. However, in order to reach the tipping point where you might start thinking "OK, I'm going to use this!" we have to start using some of the Contract Attributes.
Contract Attributes
Contract Attributes allow you to move your validation to a separate class.
[ContractClass(typeof(AccountContract))] interface IAccount { string Name { get; } double Balance { get; set; } } [ContractClassFor(typeof(IAccount))] class AccountContract : IAccount { public string Name { get { return string.Empty; } } public double Balance { get { return 0; } set { Contract.Requires<ArgumentNullException>(value >= 0, "Your Bank does not allow your account to be negative."); } } } class BankAccount : IAccount { public string Name { get; private set; } public double Balance { get; set; } public BankAccount(string name) { Name = name; } }
Now that's much nicer. Now all my validation logic is in a separate class and, with the use of the ContractClass and ContractClassFor attributes, my interface now defines my entire contract.
Contract Invariance
Another very useful attribute is ContractInvariantMethod. This allows you to decorate one, and only one, method in your class that contains contract logic that will be validated every time a property changes on your class. You don't even have to worry about calling it yourself! We could use that to tidy up our AccountContract class...
[ContractClassFor(typeof(IAccount))] class AccountContract : IAccount { public string Name { get; private set; } public double Balance { get; set; } [ContractInvariantMethod] private void ObjectInvariant() { Contract.Invariant(!string.IsNullOrEmpty(Name), "Name cannot be empty."); Contract.Invariant(Balance >= 0, "Balance cannot be negative."); } }
In order to introduce another feature of the Contracts namespace I will change the IAccount interface so that Balance is only gettable. This, then, requires the addition of 2 new methods to withdraw and deposit money to the account...
[ContractClass(typeof(AccountContract))] interface IAccount { string Name { get; } double Balance { get; } void WithdrawMoney(double amount); void DepositMoney(double amount); }
Change Awareness (OldValue)
So, what contract should we define for these new methods? In each case 'amount' needs to be non-negative but also the Balance needs to change by the correct amount. If I deposit £10 I expect my Balance to increase by the same amount. We can make good use of Contract.OldValue
[ContractClassFor(typeof(IAccount))] class AccountContract : IAccount { public string Name { get; private set; } public double Balance { get; set; } [ContractInvariantMethod] private void ObjectInvariant() { Contract.Invariant(!string.IsNullOrEmpty(Name), "Name cannot be empty."); Contract.Invariant(Balance >= 0, "Balance cannot be negative."); } public void WithdrawMoney(double amount) { Contract.Requires<ArgumentNullException>(amount >= 0); Contract.Ensures(Balance == Contract.OldValue(Balance) - amount); } public void DepositMoney(double amount) { Contract.Requires<ArgumentNullException>(amount >= 0); Contract.Ensures(Balance == Contract.OldValue(Balance) + amount); } }
Putting it all together
Finally, you're going to need to download a Microsoft DevLabs addon to Visual Studio to make all this work. DevLabs: Code Contracts automatically changes your code during compilation to bind you contract classes referenced in your interfaces to you concrete classes.
Conclusions
Code Contracts simplify validation, complete your interface definitions and keep you code clean. It enhances Design Patterns, for instance the Adapter Pattern, by allowing your interface to express your entire intent. I haven't covered anywhere near what this namespace can do for you, so go check out the rest yourselves!
Sunday, 23 May 2010
Pivot API for .Net
Once you've read through all this there are links to download the source code and binaries at the end of this post.
If you don't already know Microsoft Live Labs is in the process of releasing a pretty cool piece of data "analysis" called Pivot. At the time of writing this you need to download their browser to experience the 'goodness' but by summer 2010 we've been assured a Silverlight plugin.
Let's get another thing straight: you could just go write your own xml files for this; after all, that is the product we ultimately need to create. However, why not let someone else (me) do the hard work so that all you need to do is just new-up some classes?
Finally, I need to assume that you have a reasonable understanding of the architecture and components involved in the Pivot application. You can get all you need from here.
The School Class Collection
The Pivot API contains very little documentation or comments. This is because, I hope, the classes and methods document themselves with their names. So, the best way I could think to demonstrate the behaviour and usage was through a worked example.
For this example let's assume that I have a pre-populated collection of Pupil objects (IList<Pupil>) that represents the raw data that I wish to create a Pivot Collection from.
class Pupil { public long ID { get; set; } public string Name { get; set; } public DateTime DateOfBirth { get; set; } public string Class { get; set; } public string ReportCard { get; set; } public string Description { get; set; } public Pupil BestFriend { get; set; } }
Our starting object will be an instance of the PivotCollection class. You'll need to supply this with a name, a version and a reference to an already created Deep Zoom Collection. I will not cover how to perform the latter but there's plenty of information out there to help you with this. You can also, optionally, add some copyright information to the collection.
var collection = new PivotCollection("School Collection", "schoolCollection.dzc", "1.0") { Copyright = new CollectionCopyright { Name = "Chris Arnold", Href = "http://goodcoffeegoodcode.blogspot.com/" } };
Next we have to define what information (Facets) the items in our collection will expose. The API defines a number of different Facet Category classes that encapsulate some inherent functionality.
collection.FacetCategories.Add(new StringFacetCategory("Class")); collection.FacetCategories.Add(new DateTimeFacetCategory("Date of Birth")); collection.FacetCategories.Add(new LongStringFacetCategory("Report Card")); collection.FacetCategories.Add(new LinkFacetCategory("Best Friend"));
You could also encapsulate all of this into your own class to make your code more readable:
class SchoolPivotCollection : PivotCollection { public SchoolPivotCollection() : base("School Collection", "schoolCollection.dzc", "1.0") { Copyright = new CollectionCopyright { Name = "Chris Arnold", Href = "http://goodcoffeegoodcode.blogspot.com/" }; FacetCategories.Add(new DateTimeFacetCategory("Date of Birth")); FacetCategories.Add(new LongStringFacetCategory("Report Card")); FacetCategories.Add(new LinkFacetCategory("Best Friend")); } }
We've now set up all of the pre-requisites for our collection. Time to start populating it. To do this we just need to iterate over our collection of Pupils, create a Pivot Item for each one and add it to the Pivot collection...
var pupils = GetPupils(); foreach (var pupil in pupils) AddPupilToCollection(pupil, collection);
void AddPupilToCollection(Pupil pupil, PivotCollection collection) { string PUPIL_URL = "http://www.myschool.com/pupils?id={0}"; string PUPIL_COLLECTION_URL = "http://www.myschool.com/collection/pupil_{0}.cxml"; var item = new Item() { Name = pupil.Name, Href = string.Format(PUPIL_URL, pupil.ID), Description = pupil.Description, Img = "#" + imageCounter, /* This assumes that the order of the pupils is the same as their images in the deep zoom collection */ Id = pupil.ID }; item.Facets.Add(FacetFactory.Create(collection.FacetCategories["Class"], pupil.Class)); item.Facets.Add(FacetFactory.Create(collection.FacetCategories["Date of Birth"], pupil.DateOfBirth.ToShortDateString())); item.Facets.Add(FacetFactory.Create(collection.FacetCategories["Report Card"], pupil.ReportCard)); var facet = FacetFactory.Create(collection.FacetCategories["Best Friend"], string.Format(PUPIL_COLLECTION_URL, pupil.BestFriend.ID)) as LinkFacet; facet.Name = pupil.BestFriend.Name; collection.Items.Add(item); imageCounter++; }
The final step in the process is to save the collection to an xml document. This is handled using the classes in the PivotAPI.XmlWriter namespace...
using (var writer = new XmlTextWriter("pupils.cxml", System.Text.Encoding.Default) { Formatting = Formatting.Indented }) { var collectionWriter = new PivotAPI.XmlWriters.PivotCollectionXmlWriter(writer, collection); collectionWriter.Write(); }
And that's the only client code you'll need to create a collection document (.cxml).
Here's the complete code example. Create a new console application, reference the PivotAPI library and replace the Program.cs with the following...
using System; using System.Collections.Generic; using System.Xml; using PivotAPI; namespace BlogPivotExample { class Program { static int imageCounter; static void Main() { var collection = new SchoolPivotCollection(); var pupils = GetPupils(); foreach (var pupil in pupils) AddPupil(pupil, collection); using (var writer = new XmlTextWriter("pupils.cxml", System.Text.Encoding.Default) { Formatting = Formatting.Indented }) { var collectionWriter = new PivotAPI.XmlWriters.PivotCollectionXmlWriter(writer, collection); collectionWriter.Write(); } } static void AddPupil(Pupil pupil, PivotCollection collection) { string PUPIL_URL = "http://www.myschool.com/pupils?id={0}"; string PUPIL_COLLECTION_URL = "http://www.myschool.com/collection/pupil_{0}.cxml"; var item = new Item() { Name = pupil.Name, Href = string.Format(PUPIL_URL, pupil.ID), Description = pupil.Description, Img = "#" + imageCounter, /* This assumes that the order of the pupils is the same as their images in the deep zoom collection */ Id = pupil.ID }; item.Facets.Add(FacetFactory.Create(collection.FacetCategories["Class"], pupil.Class)); item.Facets.Add(FacetFactory.Create(collection.FacetCategories["Date of Birth"], pupil.DateOfBirth.ToShortDateString())); item.Facets.Add(FacetFactory.Create(collection.FacetCategories["Report Card"], pupil.ReportCard)); var facet = FacetFactory.Create(collection.FacetCategories["Best Friend"], string.Format(PUPIL_COLLECTION_URL, pupil.BestFriend.ID)) as LinkFacet; facet.Name = pupil.BestFriend.Name; collection.Items.Add(item); imageCounter++; } static IListGetPupils() { var pupils = new List (); //TODO: this is just a test pupil. You'll have to write your own routine if you want more data than this! var testPupil = new Pupil() { Name = "Chris Arnold", ID = 34873, Class = "Upper 5A", Description = "Lovely child", DateOfBirth = DateTime.Parse("1/1/1973"), ReportCard = "Chris has worked very hard this year. Well done!" }; pupils.Add(testPupil); return pupils; } } }
Now create 2 new classes in you application...
using System; using PivotAPI; namespace BlogPivotExample { class SchoolPivotCollection : PivotCollection { public SchoolPivotCollection() : base("School Collection", "schoolCollection.dzc", "1.0") { Copyright = new CollectionCopyright { Name = "Chris Arnold", Href = "http://goodcoffeegoodcode.blogspot.com/" }; FacetCategories.Add(new DateTimeFacetCategory("Date of Birth")); FacetCategories.Add(new LongStringFacetCategory("Report Card")); FacetCategories.Add(new LinkFacetCategory("Best Friend")); } } }
using System; namespace BlogPivotExample { internal class Pupil { public long ID { get; set; } public string Name { get; set; } public DateTime DateOfBirth { get; set; } public string Class { get; set; } public string ReportCard { get; set; } public string Description { get; set; } public Pupil BestFriend { get; set; } } }
Caveats
- The API uses LINQ and so you'll need version 3.5 or 4.0 of the .Net framework.
- The API is v1.0.0 and is not complete. Missing functions include the Supplement file, BrandImage, AdditionalSearchText, Icon. Also, some of the extensions have not been implemented yet e.g. DateRange, SortOrder and SortValue. These will all follow very quickly.
Tweet Pivot
Just to demonstrate that this isn't just all smoke and mirrors I wrote a website called Tweet Pivot. This utilises the Pivot API to get Twitter data and create dynamic collections. This is also where I've hosted the API for convenience.
So, if you like Tweet Pivot or the Pivot API please re-tweet them! Thanks.
Download Pivot API Source Code and Binaries
Wednesday, 28 April 2010
Silverlight for Business
The Silverlight Tour comes to the UK – and it’s called the Masterclass!
This 3 day hands-on training with both designer and developer tracks looks awesome and (uniquely) has two expert trainers per course.
Currently scheduled in London, Manchester, and the Midlands for June, all courses also come with the chance to win an xbox 360, and Silverlight Spy licences!
Early bird discount of £100 if you book in May, and if you are a member of #SLUGUK or #nxtgenug there are additional discounts to be had.
Full Details are here: http://silverlightmasterclass.net
In addition bbits are holding a raffle for a free ticket for the masterclass. To be eligible to win the ticket (worth £1095!) you MUST paste this text, including all links, into your blog and email Ian@bbits.co.uk with the url to the blog entry. The draw will be made on June 1st and the winner informed by email and on http://silverlightmasterclass.net
Tuesday, 20 April 2010
Enhancing the Aggregate Pattern in Domain Driven Design with Weak References
I'm working my way through Eric Evan's wonderful book Domain Driven Design and, already, it has a place on my "books-every-developer-should-read" shelf. One of the 3 main design patterns covered in chapter 6 is Aggregate. One of the tenets of this pattern is that external objects can request a reference to an internal object of the Aggregate on the assumption that they use it fleetingly and do not hold a permanent reference to it. The reason for this is that the Aggregate needs to be disposable as a whole and this will not happen if some internal objects are still being referenced.
Now, the problem with this is that, certainly in .Net, there is nothing in the language to enforce this requirement. As a developer you have to write your Aggregates and hope that the next developer to consume them understands the pattern requirements. A simple recipe for disaster!
Enter the genreric WeakReference class.
I'll extend Eric's example using his idea of a Car class (omitting any Factory or Repository complications)...
public enum WheelLocation { FrontLeft, FrontRight, RearLeft, RearRight } public class Wheel : IDisposable { public WheelLocation Location { get; private set; } public Wheel(WheelLocation location) { Location = location; } public void Dispose() { } } public class Car : IDisposable { public Guid ID { get; private set; } private IList<Wheel> _wheels; public Car(Guid id) { ID = id; _wheels = new List<Wheel>(4); _wheels.Add(new Wheel(WheelLocation.FrontLeft)); _wheels.Add(new Wheel(WheelLocation.FrontRight)); _wheels.Add(new Wheel(WheelLocation.RearLeft)); _wheels.Add(new Wheel(WheelLocation.RearRight)); } public void Dispose() { foreach (IDisposable obj in _wheels) obj.Dispose(); } public Wheel GetWheel(WheelLocation location) { return _wheels.Where(w => w.Location == location).Single(); } }
So, we now have a nicely encapsulated Car Aggregate class that can also, indirectly, supply any consumers with references to its internal objects (Wheels). Here's a (kinda dumb!) usage example...
public class WheelEventArgs : EventArgs { public Wheel Wheel { get; private set; } public CarEventArgs(Wheel wheel) : base() { Wheel = wheel; } } public class CarConsumer { public void CreateCar(Guid id) { using (var car = new Car(id)) { OnCarCreate(car); } } protected virtual void OnCarCreated(Car car) { if (CarCreated != null) { CarCreated(this, new EventArgs(car.GetWheel(WheelLocation.FrontLeft)); CarCreated(this, new EventArgs(car.GetWheel(WheelLocation.FrontRight)); CarCreated(this, new EventArgs(car.GetWheel(WheelLocation.RearLeft)); CarCreated(this, new EventArgs(car.GetWheel(WheelLocation.RearRight)); } } public event CarCreated; }
The problem here is that we have no way of knowing whether the classes that registered to the CarCreated event still have references to the Wheel objects when we come to dispose our Car class.
So, what we can do is change the GetWheel method to return weak references to the internal wheels...
public WeakReference<Wheel> GetWheel(WheelLocation location) { return new WeakReference<Wheel>(_wheels.Where(w => w.Location == location).Single()); }
Now, this doesn't prevent the external objects from securing a strong reference to the wheels and it doesn't stop dumb developers doing dumb things; but it does indicate our intention with the pattern. "Hey, look, I'm returning a WeakReference to this object - there's a reason for it!"
Generic WeakReference Class
The .Net BCL currently only contains a non-generic class for this functionality, so I wrote a simple generic version...
namespace System { public class WeakReference<T> : WeakReference { public new T Object { get { return (T)base.Target; } set { base.Target = value; } } public WeakReference(T target) : base(target) { } public WeakReference(T target, bool trackResurrection) : base(target, trackResurrection) { } protected WeakReference(SerializationInfo info, StreamingContext context) : base(info, context) { } } }
And here's how you can use it...
public class Dog : IDisposable { public string Name { get; private set; } public Dog(string name) { Name = name; } // IDisposable routines left out for brevity } public class Consumer { public void DoSomething() { using (var myDog = new Dog("Dioji")) { var weakReference = new WeakReference<Dog>(myDog); Console.WriteLine("My dog is called '{0}'.", weakReference.Object.Name); } } }
I'll demonstrate why this can be really useful in my next post on the "Enhancing the Aggregate Pattern in Domain Driven Design with Weak References".
Thursday, 15 April 2010
Problems sending Email through IIS7 (SmtpFailedRecipientException)
First, just because you have the SMTP Email feature enabled in IIS7 doesn't mean you can actually send mail. You still have to have an Smtp Virtual Server set up. You do this via the "IIS 6.0 Manager" link. Thankfully, that's not the confusing bit!
I'm rambling and, anyway, all the information above is easily discoverable online.
So, every time my ASP.Net application attempted to send an email to an external address (e.g. someone@gmail.com) the system would throw a System.Net.Mail.SmtpFailedRecipientException "Mailbox unavailable. The server response was: 5.7.1. Unable to relay for someone@gmail.com".
Here's how I fixed it...
1. In IIS 6.0 Manager right click on the SMTP virtual server and choose properties.
2. In the Access tab click the Relay button.
3. "Only the list below" and "Allow all computers which successfully..." should both be selected.
4. I thought this would be enough, but it wasn't.
5. You also have to add the IP address(es) of your server to the list (e.g. 127.0.0.1).
Et voila!
Now, as I said before, I'm not a network specialist so this is almost certainly not the 'suggested' solution; but, hey, it worked for us.
Wednesday, 14 April 2010
Goodbye x64, Hello x86
Whilst it's certainly quite 'cool' to say that I run a 64 bit development machine do I really need it? For servers it's a definite 'yes' - mainly for the > 4GB memory space; but for the rest of us, probably 'no'.
I don't use more than 4GB RAM and, whilst the x64 architecture is slightly better / newer / maturer than the equivalent in x86 it doesn't make a bit of difference. You might even notice a reduction in performance due to the larger memory space pointers!
Visual Studio 2010 shipped yesterday - 32 bit only. A lot of software vendors are still wedded to unmanaged code - normally 32 bit only. Even IE8 64bit seems to have issues on my machine.
My biggest driver, though, is our company VPN. The client software will not work on Windows 7 x64. We can get an upgrade, but it's gonna cost us #fail.
So, until x64 becomes mainstream and not something that I either have to wait for or pay extra for I'm back down to 32 little bits.
Tuesday, 6 April 2010
Adding a Lazy Loading Pattern to T4MVC
Some of our controllers are currently redundant so I changed the base class' constructor to make sure no-one called them accidentally...
public abstract class MyRedundantBaseClass { public MyRedundantBaseClass() { throw new NotImplementedException(); } }
Now, none of my tests go anywhere near this class (or any inherited ones) so why was it throwing the exceptions?
The problem is in one of the auto-generated classes in the T4MVC.cs file. The template generates a static field for each non-abstract controllers in your application...
public static class MVC { public static MyApp.Controllers.Account.AccountController Account = new MyApp.Controllers.Account.T4MVC_AccountController(); // Repeated for each controller... }
This list of fields also included a number that extended my redundant class. So, as soon as I did anything with the T4MVC framework all these classes got automatically instantiated and now I'm dead in the water.
My solution is to edit the template so that it generates a Lazy Loading version of the T4MVC.cs file. This also makes it a little more efficient as it will only new-up classes when it needs them.
Here's the old template section (starting from line 73)...
<#foreach (var controller in DefaultArea.GetControllers()) { #> public static <#=controller.FullClassName #> <#=controller.Name #> = new <#=controller.FullDerivedClassName #>(); <#} #>
... and here's what I changed it to...
<#foreach (var controller in DefaultArea.GetControllers()) { #> private static <#=controller.FullClassName #> _<#=controller.Name #>; <#} #> <#foreach (var controller in DefaultArea.GetControllers()) { #> public static <#=controller.FullClassName #> <#=controller.Name #> { get { if (_<#=controller.Name #> == null) _<#=controller.Name #> = new <#=controller.FullDerivedClassName #>(); return _<#=controller.Name #>; } } <#} #>
This now generates the following code in T4MVC.cs...
public static class MVC { private static MyApp.Controllers.Account.AccountController _Account; public static MyApp.Controllers.Account.AccountController Account { get { if (_Account == null) _Account = new MyApp.Controllers.Account.T4MVC_AccountController(); return _Account; } } // Repeated for each controller... }
I still need to add a double locking mechanism for thread safety, but I think this makes a valuable improvement to an already great framework.
Friday, 19 March 2010
RoboCopy Terminates XCopy!
I was building a little batch file to deploy the extracted files once cited on the server. OK, so let's re-acquaint myself with XCopy's parameters.
"Note: Xcopy is now deprecated, please use Robocopy."
WTF. What's Robocopy!?
Seems as though I've missed a few of the Microsoft emails about this 'new' feature of Windows. It used to be available as a Resource download but is a standard component from Windows XP onwards.
So, what can it do? Well, after a cursory glance, pretty much anything. There's a glut of parameters covering the following sections: Copy, File Selection, Retry, Logging, Job. Here's a couple that stood out as extremely useful...
- /E :: copy subdirectories, including Empty ones.
- /LEV:n :: only copy the top n LEVels of the source directory tree.
- /Z :: copy files in restartable mode (if the connection is interrupted it will continue when next available!).
- /PURGE :: delete dest files/dirs that no longer exist in the source.
- /MIR :: MIRror a directory tree (equivalent to /E plus /PURGE
I'm well aware that I may very well be the only guy on this sphere who isn't aware of this command line tool; but I think this was worth noting anyway.
Saturday, 27 February 2010
ASP.NET MVC Html.RadioButton Generates Invalid XHTML
public enum Colours { Red, Blue, Green } public class ColoursViewModel { public Colours ChosenColour { get; set; } }
Then I could create a simple action like this ...
public class TestController { public ActionResult SelectColour() { return View(new ColoursViewModel()); } }
... that rendered the following view ...
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ColoursViewModel>" %> <%= Html.RadioButton("ChosenColour", "Red"); %> <%= Html.RadioButton("ChosenColour", "Green"); %> <%= Html.RadioButton("ChosenColour", "Blue"); %>
The MVC framework will automatically bind my view model's "ChosenColour" property to the radio button collection so that I can post the results back like this...
public class TestController { [AcceptVerbs(HttpVerbs.Post)] public string SelectColour(ColoursViewModel viewModel) { return "You chose " + viewModel.ChosenColour.ToString(); } }
The problem here is that the generated html looks like this (notice the duplicate id attributes)...
This means that we cannot use the "for" attribute on any labels associated with the individual radio buttons nor can we use javascript to select the radio button when that associated label is clicked.
This is how you fix the problem...
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ColoursViewModel>" %> <%= Html.RadioButton("ChosenColour", "Red", new { id = "chooseRed" }); %> <%= Html.RadioButton("ChosenColour", "Green", new { id = "chooseGreen" }); %> <%= Html.RadioButton("ChosenColour", "Blue", new { id = "chooseBlue" }); %>
... which will generate this html fragment...
The point I'm trying to make here is that the id attribute should be a first class parameter of the RadioButton extension method. I shouldn't have to override it using the htmlAttributes parameter. This is what the official extension methods look like...
public static string RadioButton(this HtmlHelper htmlHelper, string name, object value); public static string RadioButton(this HtmlHelper htmlHelper, string name, object value, object htmlAttributes); public static string RadioButton(this HtmlHelper htmlHelper, string name, object value, IDictionary<string, object> htmlAttributes); public static string RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked); public static string RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked, object htmlAttributes); public static string RadioButton(this HtmlHelper htmlHelper, string name, object value, bool isChecked, IDictionary<string, object> htmlAttributes);
You could consider making a change to the MVC framework libraries to include the id, thus decoupling it from the name. For example..
public static string RadioButton(this HtmlHelper htmlHelper, string name, string id, object value);
So anyway, the take-away from this blog post is "Html.RadioButton(...) will produce invalid XHTML unless you pay attention."
Wednesday, 24 February 2010
Question: When is a non-static class static?
OK, quite an obtuse question there so let me explain...
In our ASP.NET MVC application we have a custom Membership Provider and a custom Role Provider. Here's a (vastly simplified) example of the membership provider...
public class CustomMembershipProvider : MembershipProvider { private SqlConnection _connection; private string _userName; private string _password; public CustomMembershipProvider() { } public override bool ValidateUser(string username, string password) { _userName = username; _password = password; bool success = false; try { // Sets the _cn member... OpenConnection(); success = ValidateUser(); } finally { CloseConnection(); } return success; } }
So, pretty straight forward. Nothing wrong with that, is there?
Well, actually, yes. The problem is that you may write this class with the expectation that it will be treated as a POIO (Plain Old Instance Object) but ASP.NET has other ideas. One, and only one, of these objects is instantiated (during the first request to you site). The big issue here is that we now have private fields on a singleton class - not good.
We ran into problems with SqlDataReaders because the runtime complained about multiple readers on the same connection. We could have implemented MARS, but that still didn't deal with the underlying problem. Worse still is the possibility of an unauthorised user actually being authorised! You only need user1's time-slice to end and user2's to begin midway through that ValidateUser method and you've got serious problems.
This is a summarised suggestion of how we solved this problem...
public class CustomMembershipProvider : MembershipProvider { public CustomMembershipProvider() { } public override bool ValidateUser(string username, string password) { bool success = false; using (var cn = OpenConnection()) { var validater = new UserValidater(cn, username, password); success = validater.IsValid(); } return success; } private class UserValidater { private SqlConnection _connection; private string _userName; private string _password; public UserValidater(SqlConnection cn, string userName, string password) { _connection = connection; _userName = username; _password = password; } public bool IsValid() { // Logic removed for brevity... } } }
Monday, 15 February 2010
Automatically Redirect Http Requests to Https on IIS 7
Monday, 18 January 2010
ASP.Net MVC's Hidden Web.Config File
So, both versions were absolutely identical except for their web.config files that targeted different databases.
Both sites would allow me to hit the landing and login; but v2 failed to render any view that was strongly typed against a ViewModel. For instance, a view that inherited this would be fine...
System.Web.Mvc.ViewPage
... however, a view that inherited this, would fail...
System.Web.Mvc.ViewPage<MyApp.MyViewModel>
The exception was a System.Web.HttpParseException: "Could not load type 'MyApp.MyViewModel'".
Turns out that it was because I wasn't deploying that innocuous little web.config file that resides in the root View directory. One very important part of this file is to deny direct browsing to the aspx & ascx files - you want any requests to go via your routing model and controllers. The second purpose is to define the base types for your views and how to parse them.
Bottom line: without it your app won't work.
Friday, 15 January 2010
Google Analytics: Such a Shame.
It captures hits, countries, browsers, referers and will show you a page overlay indicating where most of you visitors click to next. A web developer's dream "Look what I did, boss...".
However, here's the problem...
It's a script and they want me to put it on my site. That's fine for this blog, but I simply cannot use it on any commercial site that requires a user to login. Now, I'm not saying that GA would ever do anything untoward with that script but, technically, they could; and that's enough. If it felt so inclined, that script could access any DOM element on that page and even redirect POSTs to another endpoint. Login details, passwords and, in my case, financial data could all be collected and associated with the clients' IP address.
A crying shame - guess I'll just have to write my own server-side solution.
Saturday, 9 January 2010
Supercede Instance Pattern
The first solution to this is to add a constructor overload to every class. You will also need to extract an interface for the Singleton. For instance...
public class MyClass { public MyClass() { } public void DoWork() { MySingleton.Instance.DoWork(); } }
... would become ...
public class MyClass { private IMySingleton _worker; public MyClass() : this (MySingleton.Instance) { } public MyClass(IMySingleton worker) { _worker = worker; } public void DoWork() { _worker.DoWork(); } }
You won't have to change any other code once the above it implemented. What this achieves is that your Unit Tests have the opportunity to use a Dependency Inversion pattern by using the non-default constructor. Simple, and nothing new.
However, this can be an awful lot of work - directly proportional to the number of classes that consume your singleton. This is where the Supercede Instance Pattern comes in.
This is probably a fair representation of the Singleton Pattern...
public class MySingleton : IMySingleton { private MySingleton() { } private static IMySingleton _instance; public static IMySingleton Instance { get { // Double locking mechanism omitted for brevity... if (_instance == null) _instance = new MySingleton(); } } }
All we need to do is add a new method to the above class...
public class MySingleton : IMySingleton { // All previous members omitted for brevity public static void SupercedeInstance(IMySingleton newInstance) { _instance = newInstance; } }
Et voila. Your unit tests can call this new method and inject their own, chosen object into the Singleton. You've now broken the dependency of masses of classes in one hit. Nice.
Final point, if your language allows you could conditionally remove this method. That would keep your singleton 'safe' in the wild. For example...
public class MySingleton : IMySingleton { #if RUNNING_UNIT_TESTS public static void SupercedeInstance(IMySingleton newInstance) { _instance = instance; } #endif }
Thursday, 7 January 2010
ASP.Net MVC Groups
Here's a fictional example of a problem I've been having in ASP.Net MVC...
In an MVC application I have 1000 controllers. 2 of these controllers share a partial view so I put it in the ~/Views/Shared/ folder. I then find that I have 499 more pairs of controllers that also share different partial views so I do the same. The problem is that I now have a shared folder cluttered with 500 partial views - each of which is only relevant to 0.2% of my entire application! I also have to give each of these views unique names which becomes unwieldy.So what's the solution?
What I'd like to do is group multiple controllers together and give them their own shared area for views. Any controller can also belong to more than one group.
Firstly, we need to extend the ViewEngine that our application is using. This is, primarily, responsible for finding the correct view in our filing system. Out of the box the framework uses WebFormViewEngine. Our new view engine will inject extra, temporary locations in which to search for views. The information about which locations will be supplied by the current controller. Here's the code for the new engine...
using System; using System.Collections.Generic; using System.Web.Mvc; namespace MyMvcApp.Web.Mvc { /// /// GroupWebFormViewEngine extends WebFormViewEngine and allows Controllers to be /// decorated with the MvcGroupAttribute to indicate extra locations Views can be found. /// public class GroupWebFormViewEngine : WebFormViewEngine { private string[] _defaultMasterLocationFormats; private string[] _defaultPartialViewLocationFormats; private string[] _defaultViewLocationFormats; public GroupWebFormViewEngine() : base() { // We want to make a note of the base formats to that we can reset them prior to each Find operation _defaultMasterLocationFormats = MasterLocationFormats; _defaultPartialViewLocationFormats = PartialViewLocationFormats; _defaultViewLocationFormats = ViewLocationFormats; } public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) { AddGroupLocations(controllerContext); return base.FindView(controllerContext, viewName, masterName, useCache); } public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) { AddGroupLocations(controllerContext); return base.FindPartialView(controllerContext, partialViewName, useCache); } private void AddGroupLocation(ControllerContext controllerContext) { ResetAllLocations(); var type = controllerContext.Controller.GetType(); var attrs = type.GetCustomAttributes(typeof(MvcGroupAttribute), true); Array.ForEach(attrs, attr => { AddGroupLocations((attr as MvcGroupAttribute).GroupName); }); } private void ResetAllLocations() { MasterLocationFormats = _defaultMasterLocationFormats; PartialViewLocationFormats = _defaultPartialViewLocationsFormats; ViewLocationFormats = _defaultViewLocationFormats; } private void AddGroupLocations(string folderName) { AddMasterLocationFormats(folderName); AddPartialViewLocationFormats(folderName); AddViewLocationFormats(); } private void AddMasterLocationFormats(string folderName) { List<string> currentLocations = new List<string>(MasterLocationFormats); currentLocations.Add("~/Views/Shared/" + folderName + "/{0}.master"); MasterLocationFormats = currentLocations.ToArray(); } private void AddPartialViewLocationFormats(string folderName) { List<string> currentLocations = new List<string>(PartialViewLocationFormats); currentLocations.Add("~/Views/Shared/" + folderName + "/{0}.aspx"); currentLocations.Add("~/Views/Shared/" + folderName + "/{0}.ascx"); PartialViewLocationFormats = currentLocations.ToArray(); } private void AddViewLocationFormats() { ViewLocationFormats = PartialViewLocationFormats; } } }
Now we just need our MvcGroupAttribute. This will be used to decorate our controller classes and should apply also to any inherited controllers. It is also possible to have more than one attribute on each controller as it might belong to multiple groups. Here's the code...
using System; namespace MyMvcApp { [AttributeUsage(AttributeTarget.Class, AllowMultiple = true, Inherited = true)] public class MvcGroupAttribute : Attribute { /// /// Gets or sets the name of a shared group that this controller belongs to. /// public string GroupName { get; set; } } }
Now that we have all our building blocks we can finally use the functionality. Here's a trivial example...
using System; using System.Web.Mvc; namespace MyMvcApp { [MvcGroup(GroupName = "Group1")] public class MyFirstController : Controller { public ActionResult Index() { return View(); } } [MvcGroup(GroupName = "Group1")] public class MySecondController : Controller { public ActionResult Index() { return View(); } } public class MyThirdController : Controller { public ActionResult Index() { return View(); } } }
In the example, above, all 3 controllers have access to views in the ~/Views/Shared/Group1/ directory.
Conclusion
This simple addition to our Mvc applications has been an enormous difference. We now have so much more flexibility to construct our views in a meaningful, clean way. Hopefully you'll find it useful too?