mostlylucid

April 2008 Entries

Grrr...poor use of singletons and a very cool Generic Singleton pattern!

I posted earlier that I'm switching to blogengine.net, as part of this I've been fiddling around with the code (as is my way..I'll contribute back to the source when I've finished). One of my major pet hates is poor use of the singleton pattern, especially as there's a definitive article on the pattern in .NET and how to do it well. It's actually likely that this pattern is overkill in this case and a ReaderWriterLockSlim could be better (though it has it's own problems) . Anyway, on the assumption that a Singleton is the best choice here, let's look at the current code:

 

public static BlogSettings Instance

        {

            get

            { 

                if (blogSettingsSingleton == null)

                {

                    blogSettingsSingleton = new BlogSettings();

                }

                return blogSettingsSingleton;

            }

        }

 

If you look at the article I mentioned above you'll see that this is the version which is specifically called out as follows:

"the above is not thread-safe. Two different threads could both have evaluated the test if (instance==null) and found it to be true, then both create instances, which violates the singleton pattern. Note that in fact the instance may already have been created before the expression is evaluated, but the memory model doesn't guarantee that the new value of instance will be seen by other threads unless suitable memory barriers have been passed.'

The common 'best' singleton pattern (well, it's debatable...but generally the best...) is a lot more wordy (see version 5 in that article) so I was please to find this post on a Generic Singleton (actually this was posted a while ago on Codeproject)...really nice. In the Utils class I added this:

 

    /// <summary>

        /// Provides a Singleton implementation using Generics.

        /// </summary>

        /// <typeparam name="T">Type of singleton instance</typeparam>

        public sealed class Singleton<T> where T : new()

        {

            Singleton() { }

 

            public static T Instance

            {

                get

                {

                    return Nested.instance;

                }

            }

 

            class Nested

            {

                // Explicit static constructor to tell C# compiler

                // not to mark type as beforefieldinit

                static Nested() { }

 

                internal static readonly T instance = new T();

            }

        }

 

The code for returning the instance then becomes:

        public static BlogSettings Instance

        {

            get

            {

                return Utils.Singleton<BlogSettings>.Instance;

            }

        }

You have to also change the constructor for BlogSettings to public to allow this this to work which does let devs shoot themselves in the foot (by ignoring the singleton)  and you of course have to balance the benefit agains that risk...

Changes afoot...change to BlogEngine.Net

This site currently runs on SubText but with my recent coding rebirth I'm looking to start playing with this site's platform a lot more; with this in mind I've decided to switch to blogengine.net; it's a .NET 2.0 based engine which has a lot of nice toys to play with as well as a really nice extensions model. So, there you go...SubText has been great (and I share an office with Phil the guy who maintains the project) but it's time to move to something which is easier to fiddle with. The only constraint I have is preserving the current link structure...I'm investigating various methods of doing this including using Phil's WebFormRouteHandler. I messed up to move to www.mostlylucid.net from www.mostlylucid.co.uk and I'd really like to prevent that happening again.

The state of the art...spelunking in the .NET Source

One of the nice things about being on the ASP.NET team is that I finally get to scratch the various itches (on that topic, Poison Oak...very itchy!) that have been bugging me for a few years. I finally get to play around with the source and see what effect changing various bits and bobs has on how stuff works.  I really do recommend spending some time spelunking in the .NET Source code. You can actually get this source (in a very non-official and non-supported way) using .NET Mass Downloader. This is just such an awesome resource...want to see how a server control like the Repeater works...look at the source! Trying to figure out why Viewstate behaves in a weird way...well, you get the idea. Right now you can get the source for these .NET 3.5 assemblies...that is a LOT of source!

Mscorlib.DLL
System.DLL
System.Data.DLL
System.Drawing.DLL
System.Web.DLL
System.Web.Extensions.DLL
System.Windows.Forms.DLL
System.XML.DLL
WPF (UIAutomation.DLL, System.Windows.DLL, System.Printing.DLL, System.Speech.DLL, WindowsBase.DLL, WindowsFormsIntegration.DLL, Presentation.DLL, some others)
Microsoft.VisualBasic.DLL

I don't know the official word on this yet but I'd be surprised if we don't continue adding to this list in future. From my team you can of course get the ASP.NET MVC source  already in Codeplex and we'll be adding even more projects to this site in the very near future...in fact we now operate a 'why can't we release the source policy'...

New and shiny...old and busted?

I had a comment on this blog the other day which I really got to thinking about following a discussion on a private list about how we market technologies. This is one area I'm really not qualified to talk about in any kind of 'semi-official' way but it's one of the things which bubbles under the surface of my day to day job.

It's a constant irritation working at MS...frankly some people hate us, with a passion. A fair number of the people I've talked to at work are confused by this...day to day we really do work hard to improve the working lives of the people who buy our software. But that's the thing, at it's heart MS is a business, it makes money by selling software...in the end it doesn't matter how user focused we are, if it doesn't shift boxes of Product X then it doesn't make it. The comment I was referring to earlier is an example of that dichotomy...yes, people still use VBScript based 'classic ASP', just as a huge number of people still use VB6 based windows applications and more recently WinForms apps. You wouldn't really think it though by looking at our marketing output...why? Simple, it doesn't sell new Windows licenses or Visual Studio 2008 SKUs...Microsoft is a business remember!
I'm not being down on Microsoft here (I choose to work there and I truly love the company!), it's a reality for every business and for software businesses especially. Book authors, consultants, trainers, almost everyone feeds off of the leading edge of new releases. The 'new and shiny' is where all the momentum exists, it's interesting to talk about and fun to learn. The issue (if there is one) is that just because a technology has become 'legacy' doesn't mean it's irrelevant. Some of the greatest programming books I have are for technologies I haven't written a line of code in for years. Nevertheless I spend time every year or so reading through those old books; as the saying goes 'Everything old is new again'

 

On the topic of books, just noticed this on the CodingHorror blog:Programmers Don't Read Books -- But You Should. I constantly find the lack of reading by developers a source of disdain...Jeff mentions four five (innumerate fool) books which are the absolute bare minimum you should read:

 

Code Complete 2 Don't Make Me Think Peopleware Pragmatic Programmer Facts and Fallacies

Hopefully I don't have to add this any more for anyone who has ever read more than a couple of posts on this blog...but it's called 'mostlylucid' for a reason! Nothing here unless I explicitly say otherwise reflects any opinion except my own (and even then, lack of sleep and extreme moodiness has an impact). You can think what you like about me but none of these comments is in any way attributable back to my employer.

I'm an old fuddy duddy and I like it...how to be a great coder by a mediocre one...

I was reading the excellent interview with Donald Knuth on InformIt and it led me to reflect on my own thoughts on programming. I learnt development under the gun...I wrote websites for a pure and simple reason, to make money for my employers. The best techniques to use when creating this software were those which led to the best result for my clients. Oh, sure I experimented with Patterns, read just about every book on coding and general techniques over the past 10 or so years and I've worked with some of the most inspiring designers, coders, scientists and business leaders I could find. With each project I worked on I tried to learn from my mistakes...I HATE making mistakes and it's led me to push myself...and frankly led my career far more than any conscious planning has.  
I'm not saying that I'm some cutting edge development mind...I'm not, if anything I'm an extreme pragmatist, in my personal and professional life practical is far more important than trendy. I moved between a few different programming languages over the years, following the technology to where it made sense for what I was trying to achieve. When I started out I was writing small security testing utilities which mainly ran on NT 3.5 Servers (and Windows 95 boxes). I didn't need a powerful OO based language and the quickest way to write this stuff was using Vi or EMACS with C or Perl...so that's what I did. The work I produced was utilitarian, pared down and quick...and frankly it paid well for the couple of years I wrote that stuff. I then moved to the web...at the time the 'best' technology for writing the type of site that I worked on was VBScript based 'classic' ASP...it was quick, had a nice development tool (for the time) and I developed techniques for making this stuff maintainable, decent in terms of performance (at the time we had about 20 fairly popular sites running on a Pentium 233MHz server with 128Mb of RAM). I wrote caching engines, 'made up' design patters and learnt how to get SQL Server 6.5 to work with my code to get decent performance.
Frankly I got lucky, I had no family commitments to worry about and was able to take risks with my career...for a while I 'job hopped' to companies which I thought were doing interesting stuff...my next stop was the hottest design company in Scotland at the time...which imploded about a year after I left. At BlackId, I worked with a great boss and got a chance to find out how to work with external customers....in the process boosting my confidence in programming. I was doing stuff that I thought was pretty mundane but it worked, was quick and I was still interested in what I was doing (a HUGE thing for me, I have a terrible habit of 'drifting' if I lose interest in the task at hand). Soon after that I got the opportunity to head to a startup...Globalfarmers. This company was led by a real entrepreneur who luckily for me saw the passion I had for the web...so in one day I doubled my salary and got to lead a multi-million pound development project. At Globalfarmers much of the development was contracted out to another company and frankly the company was led by a bunch of farmers...they needed someone who *got* the web and could keep up with the development cycle...apparently they thought that was me!
At this new company the system being developed was huge...at the time we were expecting to pay about $2 million just for the software and the hardware to run it on. The system was J2EE based, used some third party forum software and an Object Oriented DB backend. A big problem was that the development process was truly 'waterfall' based, like a train we just couldn't divert the main process to be responsive to the world the company was in...wanted a weather information system...tough, wanted a news publishing platform...tough. Long story (kind of) short, I had to learn a technology which would run on our new Sun machines, I'd gone from MS based websites using SQL Server to well, nothing. Into this world came PHP and Allaire Homesite...the right technology for the right problem. I wrote a bunch of software (this is also why i still think PHP is awesome...running little bits of software on HUGE servers makes it seem well, pretty fast!). At this time I was also learning a great lesson in recruiting from a very smart HR person Niamh Donnellan, hire passion. I had a junior role going, my first minion! We interviewed some very experienced people with great qualifications...but none of them had that spark...seemed like they would take the job and make it something special. Until I met Jarlath, a young Irish guy straight out of college...relatively little experience but smart and full of passion for the technology. I can't emphasize enough how much this changed my mind on recruiting...qualifications and experience are great for senior roles but when you really don't know what a job will become, hire passion.
Anyway, this post became something more than I originally intended...hopefully it's not TOO boring. The point which I'm aiming at was I think what Knuth was talking about...the 'latest thing' is fantastic, it builds passion and inflames hearts and minds...learn it, know it, but when it comes down to it you have to actually build something. This, for me is pragmatism all the great people I've met over the years have known this...learn everything you can but retain focus...always ask, 'can I use this', will it make what you're trying to achieve easier or more pleasurable or are you blinded by the 'new and shiny'.

Here's my parting words...being a great coder is not a job, it's a lifestyle. You have chosen to be an artist, you integrate a million different colors to build a picture...but you should decide what those colors are...do they help make the picture or are you just painting by numbers. All of these techniques, OOP, Extreme Programming, Patterns based development, TDD, BDD, DDD they're colors in your palate...in the end it's your picture, you decide how you paint it  because really no-one else can. (This strained metaphor brought to you by way of Paul Graham's excellent essay (and not so great book) Hackers and Painters)

My god it's full of stars!

I posted this stuff before but this is a new one which is just stunning...I wish I was this talented!


Weird Fishes: Arpeggi from flight404 on Vimeo. .

How I lost 30lbs...and plan to lose 60 more. The Geek Diet

Before I write this I should add a disclaimer...this works 'for me', and for my particular lifestyle / psychology...

After Christmas and being ill way too often just because I was chronically unfit and weighed almost 300lbs I made a decision...that I would lose 100lbs.

Now, I didn't set a limit for *when* I'd do it by but the trajectory I was on should point in that direction. I started by looking at why I'd gotten to the point I was at...why had I put on so much weight and let it get as bad as it was. Well, I wasn't happy with my life in general, really wasn't sleeping more than a couple of hours a night, was getting stressed at work to a degree which wasn't healthy and did very little physical activity. Oh, and my diet sucked...I was living on fast food and Pizza.
So, what to do...first I needed to get my sleep sorted out..so off to the doctor. Got some Prozac and Ambien...again not for everyone but this vastly improved my life...I was sleeping and dealt with stress far better. This led to clearer focus during the day and just generally feeling better about myself. Should add that the sleeping pills were only temporary, got me back in a sleeping rythm and I use them very occasionally now to jerk me back into the pattern if I start drifting off again...you need sleep, it impacts everything else if you don't get it. The extra sleep and antidepressant also began to impact the rest of my day...danger of getting a bit psychological here....but it certainly improved my confidence and just led me to a more healthy mindset when dealing with problems.

Now, on to the diet...

My major problem is plain and simple impulse eating...get hungry, by a triple bacon cheeseburger meal...not good. So, plan was: prevent getting hungry.
Starts with: Breakfast

I did a lot of research with different options but what works for me is:

300mls Crystal Geyser water
1 Banana
1 handful of unsweetened Berry Medley (red berries, no strawberries)
3 tbsp Soy Milk Powder
2 Scoops Metabolic Reset Shake
1 tbsp Green Tea Powder

So, that's all blended into a shake...the Green Tea adds some caffeine to get me going in the morning as well as having some effect on appetite suppression. The Metabolic Reset shake is pretty cool stuff, high protein (again, suppresses hunger), contains viscofiber, good for digestions as well as adding bulk...fills you up. The whole shake is also low-GI (important as I have diabetes and don't want a high glucose spike, this is absorbed slowly).

Adding to that I also take a multivitamin pack, fish oil and soy lecithin. The Soy Lecithin is due to a slightly odd component of my diet:

Piracetam - I use a bulk powder and add 1tsp of it to 500ml of water with breakfast (tastes bitter but you get used to it). The main reason I use this nootropic is that it keeps my energy up during the day (without caffeine). I have researched this pretty intensively, and I can say that the side effects seem very limited...I do feel it improves my concentration when fatigued...but I include it here for reasons of disclosure rather than a recommendation! Oh, and my experience is that without Lecithin, I get a headache at the end of the day from this stuff.

Lastly I take a Breakfast Blend juice ('cos I like it)...

Excercise...walking...simple, easy. I try to do 10000 steps a day (buy a pedometer). It works for me, I snack during the day on these, and have a normal (i.e., cooked at home) evening meal. Well, it works for me...I sleep better and generally feel better on this regimen...

Where should the ASP.NET team release stuff?

What about Code Gallery? I'm changing this post slightly. The consensus right now is that Codeplex releases must have source, the ASP.NET site is the right place for release (mainstream, public stuff). What about the non-mainstream, early preview,  binary only releases? Would you prefer these to stay on Code Gallery (e.g., like Dynamic Data) and just link to the site from somewhere on ASP.NET site or should we create a 'special area' on the ASP.NET site (like 'thelab' or somesuch) and provide details and link to specific downloads...or do you prefer these stay on Connect?

So, dear customer I have a question for you (as apparently I've lost all perspective)...as I've written before I'm working on the releases for the ASP.NET team and one of the challenges I've been facing is *where* we should release stuff. Scott Hanselman wrote about this topic at the bottom of this post and it's been something which has been vexing me for a few weeks. Right now we have 4 main places where I can stick content:

1. Connect;  old and faithful (and a bit clunky to use) we use this site for giving specific group access to really early previews / documents etc...this stuff has to be covered by confidentiality and legal agreements so we have to know who gets it.

2. ASP.NET main site; the 'main' site...I think of this as the place where you can expect to find stuff 'you can just use', more like the network tv channels, mainstream stuff.

3. Codeplex; fairly new to our team, I hae fairly strong views on this and want to keep it for mainly source code focused releases, i.e., you should expect to always be able to browse the source, download, compile and mess around with what you find there. The stuff here will be around for a while and will be updated regularly.

4. Code Gallery; the wild-card and partly the reason for ScottHa's post. In essence this site is fairly similar to CodePlex (it shares the same code-base for the site) but it's reserved for MS internal use...in other words only Microsoft people post on there. I chose to use this site to host the Dynamic Data Preview over the Connect site...mainly because it's easier to use and just seems better. In addition the Dynamic Data bits are really mostly binary...and will go away once we wrap the bits into a release at some point.

Here's a question for you...have I made things worse? Would you prefer that we:

1. Put all mainstream stuff on the ASP.NET site and everything else on the one Codeplex site (http://www.codeplex.com/aspnet)? Bearing in mind the Codeplex site has limited navigation and we could be talking about a lot of stuff!

2. Put everything on ASP.NET with some sort of subdivision within the site for 'mainstream' and 'edgier, less stable stuff'. Possibly 'randomizing' the purpose of the ASP.NET site...

3. How we have it right now (only maybe linked from the main ASP.NET site to Codeplex and Code Gallery). Lots of sites maybe not obvious where you find stuff.

4. Something else I haven't thought of?

This is a topic where you can make a real difference, these releases are for you (as I can no longer think like a customer...pah!), where do you want them?

Should add if you don't want to comment openly you can mail me at scott.galloway_at_microsoft.com or through the contact form on this site.

UPDATE: Thanks to ScottHa for 'Twittering' this post...great responses and I WILL take the feedback seriously...watch this space!

UPDATE: And so the floodgates open...BradA has just linked to this post...looking forward to a lot more feedback!

Work in progress, Response.RelativeRedirect

Inspired by this post, only covers simple cases but it's a start. Essentially this is an extended version of Response which only allows redirection to pages within the same site...so allows /default.aspx, does not allow http://www.evildomain.com/default.aspx. I've also ripped off a member of my team's excellent work on Response.Redirecting to a new window. This method uses extension methods, to use it just drop the file in App_Code and Response gets two new members. Oh and it's incomplete because I didn't account for encoded / obfuscated URLs...I'll update when I do...

 

using System;

using System.Web;

using System.Web.UI;

 

public static class EnhancedRedirect

{

    public static void RelativeRedirect(this HttpResponse response, string path)

    {

        RelativeRedirect(response, path, "_self");

    }

 

    public static void RelativeRedirect(this HttpResponse response, string path, string target)

    {

        RelativeRedirect(response, path, target, string.Empty);

 

    }

 

    public static void RelativeRedirect(this HttpResponse response, string path, string target, string windowFeatures)

    {

        if (!string.IsNullOrEmpty(path) && !path.StartsWith("http:") && !path.Contains("//"))

            Redirect(response, path, target, windowFeatures);

        else

        {

            throw new InvalidOperationException("Cannot redirect outside of the current site.");

        }

    }

 

    public static void Redirect(this HttpResponse response, string url, string target, string windowFeatures)

    {

        Redirect(response, path, target, string.Empty);

    }

 

    public static void Redirect(this HttpResponse response, string url, string target, string windowFeatures)

    {

        if ((String.IsNullOrEmpty(target) || target.Equals("_self", StringComparison.OrdinalIgnoreCase)) && String.IsNullOrEmpty(windowFeatures))

        {

            response.Redirect(url);

        }

        else

        {

            Page page = (Page)HttpContext.Current.Handler;

            if (page == null)

            {

                throw new InvalidOperationException("Cannot redirect to new window outside Page context.");

            }

            url = page.ResolveClientUrl(url);

            string script;

            if (!String.IsNullOrEmpty(windowFeatures))

            {

                script = @"window.open(""{0}"", ""{1}"", ""{2}"");";

            }

            else

            {

                script = @"window.open(""{0}"", ""{1}"");";

            }

            script = String.Format(script, url, target, windowFeatures);

            ScriptManager.RegisterStartupScript(page, typeof(Page), "Redirect", script, true);

        }

    }

}

The boring bit at the end...

I've posted about this before but one of my main functions on my new team is managing all of our releases. We have a large number of these both public and private (to closed groups like ASPInsiders). Of course I really just do the bit at the end, people like Phil, Eilon, Scott and David (to name but a few) do 99.9% of the actual work.
Anyway, I mention this by way of an opening to our latest release of the MVC source code complete with Unit Tests, Visual Studio Templates and just some great changes which my boss3 Scott Guthrie just announced. I spent most the day and made a grand total of 3553 changesets before getting the source code uploaded correctly. The observant will have noticed that the post yesterday was a little diffing tool...which I built a new source tree updater system on for our Codeplex releases; I'll release it once I clean the source up a bit...I probably wound up rewriting 3/4 of it today!
Anyway, nice to do some coding again, but stress, Guarana, coffee and little sleep make me a bit on the frantic side...and feeling very spaced all day. Just as well that most of my colleagues were OOF at the MVP Summit. Anyway, I should now be in bed...lots to do again tomorrow (last couple of releases took longer than I'd planned so I have a backlog...hmmphh...). Oh, and of course I have to do some cleaning before my cleaner comes...yup, I know weird!

Why do single chances make me nervous...life should come with an undo function...

Spent most of the day not doing what I should've been doing. I have a number of balls in the air at the moment and it feels like I've just added a spinning plate act at the same time...noisy disaster may ensue. Right now I'm working on another Codeplex release, working on a private build of some beta bits for the Insiders, getting our bugs migrated to the right place so we can get cracking on ASP.NET v.Next, getting the notes together for the last meeting (before the next one happens!) as well as getting the Hands-On-Labs ready for this thing...All of which are pretty  much due right now...oh and I just got out of a multi-month relationship with my now ex-girlfriend...so let's say my time is now exactly my own right now. (Ideally I'd be sitting in a little boat in the Caribbean for a couple of weeks but alas...). Anyway in the heart of this perfect storm I managed to do a bit of fiddling with code...Essentially I'm putting a method together to help us make quicker, less onerous Codeplex releases (we want to get as much stuff there as often as possible). I've been writing (and I've written about it before) a little directory comparer tool which I'm about to expand into adding changed items into TFS (for Codeplex pushes)...for various reasons the current way TFS makes us do this is a bit problematic for us...which meant we were using a python based tool which was 1. a bit flaky (poor errors) and 2. unmaintainable since of all the languages used in my team, Python ain't one...

Well, here's some code for the most recent incarnation (solution for follow)...it's getting a few add-ons like the ability to ignore certain directories / file extensions, multi-part configuration system and just a few general performance fixes.

Oh, and next week I plan to take a deep dive into the ASP.NET Page framework, giving an overly detailed guided tour of how it does it's stuff...

using System;

using System.Collections.Generic;

using System.Collections;

using System.Collections.Specialized;

using System.Text;

using System.IO;

 

namespace MergeDirs

{

    class Program

    {

        #region Sample Config

        /* Sample code for default config creation       

        * {           ConfigurationGroup cg = new ConfigurationGroup();

            cg.ConfigurationItems.Add( new Config() { SourceDirectory = "source1", DestinationDirectory = "dest1", ClearDestination = true, ExtensionsToExclude = new List<string>(new string[] { "vssscc", "dll", "pdb", "vspscc" }), DirectoriesToExclude = new List<string>(new string[] { "bin" }) });

            cg.ConfigurationItems.Add(new Config() { SourceDirectory = "source2", DestinationDirectory = "dest2", ClearDestination = true, ExtensionsToExclude = new List<string>(new string[] { "vssscc", "dll", "pdb", "vspscc" }), DirectoriesToExclude = new List<string>(new string[] { "bin" }) });

            cg.Serialize(@"C:\TestConfig\ConfigGroup.xml");

            return;*/

 

 

        #endregion

 

        private ConfigurationGroup Cfg { get; set; }

        private static string ConfigFile = string.Empty;

        static void Main(string[] args)

        {

            Program pgm = new Program();

            pgm.Run(args);

        }

 

 

        public void Run(string[] args)

        {

            Cfg = new ConfigurationGroup();

            if (args.Length > 0)

            {

                foreach (string argument in args)

                {

                    string tokenName = argument.Substring(1, 1);

                    switch (tokenName)

                    {

                        case ("f"):

                            ConfigFile = args[0].Substring(3).Trim();

                            break;

                    }

                }

 

            }

            Cfg.Deserialize(ConfigFile);

            if (Cfg == null)

            {

                Console.WriteLine("No config file at " + ConfigFile + " and no arguments...I can't guess!");

                Console.ReadLine();

                return;

            }

            foreach (Config cfg in Cfg.ConfigurationItems)

            {

                if (!string.IsNullOrEmpty(cfg.SourceDirectory) && !string.IsNullOrEmpty(cfg.DestinationDirectory))

                {

                    if (Directory.Exists(cfg.SourceDirectory))

                    {

 

                        if (!Directory.Exists(cfg.DestinationDirectory))

                            Directory.CreateDirectory(cfg.DestinationDirectory);

                        if (!Directory.Exists(cfg.DestinationDirectory))

                            throw new IOException("Failed to create directory:" + cfg.DestinationDirectory);

 

                        CompareDirs(cfg.SourceDirectory, cfg.DestinationDirectory, cfg.ClearDestination, cfg.ExtensionsToExclude, cfg.DirectoriesToExclude, cfg.ExtensionsToLeave);

 

                    }

                    else

                    {

                        Console.WriteLine("Both source and destination directories must already exist!!");

                    }

                }

                else

                {

                    Console.WriteLine("You must specify both source and destination directories!");

                }

            }

            Console.WriteLine("Finished!");

#if DEBUG

            Console.ReadLine();

#endif

        }

 

 

        private bool CompareDirs(string sourceDir, string destDir, bool clearRight, List<string> extensionsToExclude, List<string> directoriesToExclude, List<string> extensionsToLeave)

        {

            List<string> srcFileNames = new List<string>(GetFSItems(new DirectoryInfo(sourceDir).GetFiles()));

            List<string> dstFileNames = new List<string>(GetFSItems(new DirectoryInfo(destDir).GetFiles()));

            foreach (string fileName in srcFileNames)

            {

                //Console.WriteLine(string.Format("Comparing:{0} and {1}", Path.Combine(sourceDir, fileName), Path.Combine(destDir, fileName)));

                bool excludeFile = false;

                foreach (string excludeExt in extensionsToExclude)

                {

                    if (fileName.EndsWith("." + excludeExt))

                    {

                        excludeFile = true;

                        break;

                    }

                }

                if (excludeFile)

                    continue;

                if (!dstFileNames.Contains(fileName))

                {

                    File.Copy(Path.Combine(sourceDir, fileName), Path.Combine(destDir, fileName));

                    Console.WriteLine("Adding:" + Path.Combine(destDir, fileName));

                }

                else

                {

                    DateTime srcInf = new FileInfo(Path.Combine(sourceDir, fileName)).LastWriteTime;

                    DateTime dstInf = new FileInfo(Path.Combine(destDir, fileName)).LastWriteTime;

                    if (dstInf != srcInf)

                    {

                        File.Copy(Path.Combine(sourceDir, fileName), Path.Combine(destDir, fileName), true);

                        Console.WriteLine("Overwriting:" + Path.Combine(destDir, fileName));

                    }

                }

            }

            foreach (string fileName in dstFileNames)

            {

                string extension = fileName.Substring(fileName.LastIndexOf('.') + 1);

                if (!srcFileNames.Contains(fileName) || extensionsToExclude.Contains(extension))

                {

                    if (extensionsToLeave.Contains(extension))

                    {

                        Console.WriteLine("Leaving:" + fileName);

                        continue;

                    }

                    else

                    {

                        File.Delete(Path.Combine(destDir, fileName));

                        Console.WriteLine("Deleteing:" + fileName);

                    }

                }

            }

            List<string> sourceNames = new List<string>(GetFSItems(new DirectoryInfo(sourceDir).GetDirectories()));

            List<string> destNames = new List<string>(GetFSItems(new DirectoryInfo(destDir).GetDirectories()));

            foreach (string dirName in sourceNames)

            {

                if (directoriesToExclude.Contains(dirName))

                {

                    Console.WriteLine("Skipping: " + dirName);

                    continue;

                }

                //Console.WriteLine(string.Format("Comparing:{0} and {1}", Path.Combine(sourceDir, dirName), Path.Combine(destDir, dirName)));

                if (!destNames.Contains(dirName))

                {

                    string newDestDir = Path.Combine(destDir, dirName);

                    Directory.CreateDirectory(newDestDir);

                    Console.WriteLine("Adding:" + Path.Combine(destDir, dirName));

                    CompareDirs(Path.Combine(sourceDir, dirName), newDestDir, clearRight, extensionsToExclude, directoriesToExclude, extensionsToLeave);

                }

                else

                {

                    CompareDirs(Path.Combine(sourceDir, dirName), Path.Combine(destDir, dirName), clearRight, extensionsToExclude, directoriesToExclude, extensionsToLeave);

                }

            }

 

            foreach (string dirName in destNames)

            {

                if (!sourceNames.Contains(dirName) || directoriesToExclude.Contains(dirName))

                {

                    Directory.Delete(Path.Combine(destDir, dirName), true);

                    Console.WriteLine("Deleteing:" + Path.Combine(destDir, dirName));

                }

            }

            return false;

        }

 

        private IEnumerable<string> GetFSItems(FileSystemInfo[] dirInfos)

        {

            foreach (FileSystemInfo dinf in dirInfos)

            {

                yield return dinf.Name;

            }

        }

 

    }

}

Meh, no more boring posts promise...

Well, through (partly) my own choice I now have a whole heap of time back. Time to rejuvenate the blog...first stop updating the increasingly boring look of the thing. Second, a series of posts on some of the newer ASP.NET technologies. Third, some ideas for ways forward for the framework...Realizing I have a whole server in my house, a 3mbit upstream speed and am doing nothing with it! Hmm...

Somewhere along the way I lost my self confidence.

To A...it was great while it lasted.
I envy people with self confidence...there was a time when I had it but it slipped away over the years. Now I spend more and more time faking it; I'm sorry I couldn't keep up the pretense...

Welcome to the world of tomorrow!

Found this through Notcot, absolutely stunning graphical accompaniment to a brilliant song...


Solar, with lyrics. from flight404 on Vimeo..

A reason I dislike working for Microsoft

Ok, deliberately provocative title but I was reading Rob Conery's blog and stumbled across this comment to a recent screencast he put out:

@robconery: you owe the .net community an apology

To be honest, my first reaction was 'what a xxxx'...but that's unprofessional and I couldn't possibly say something like that. So, what was Rob's crime that this individual thought he should apologize for? He hasn't mastered TDD...now Rob dealt with the criticism very politely and elegantly (far better than this Scottish hothead would have). As I commented on his blog I had planned to post some screencasts here which showed my process of learning some of our technologies...this has really put me off the idea.
Here's a scoop, no-one (with the exception of Scott Guthrie and Bill Gates) at Microsoft knows everything...we actually have to learn stuff just like everyone else.
I've been dealing with this a little myself recently working with some of our recent releases, everyone learns stuff and unless we say 'this is definitive' or it's a 'final release' (in my case), expect mistakes! Constructive feedback is the reason for our two most recent releases , it's also the reason they're on non-traditional release platforms (Codeplex and Code Gallery)...they aren't final releases...we know there's mistakes in there (and frankly we can't absolutely guarantee they won't destroy your machine) and we want you to help us find them so we can improve things.
One big thing I've learnt on this team is that we don't think we're perfect...we live and die by our community...it's why we spend so much time talking to you and putting out these releases (believe me, it's much easier just to sit back and hope everything is OK when we release it!). We fret over everything we do to make things better for you and we take feedback seriously, but at least be civil!

One month in...well, and a bit

I thought I'd post a kind of update mail...I'm just over a month into my new role in the ASP.NET team. I've said before it's a really small team in Microsoft terms; there's 7 Program Managers in my current team compared with more than twice that number in my previous team. I've written before on how impressed I've been by everyone (and I mean everyone) in the team, PM, Dev and QA and well, suck-upy (not a word!) though it sounds it's true.
What have I been doing, well I've mostly been helping out with releases from the team (ooh, and watch out this week!), getting ramped up on a couple of features I'm going to deliver for VNext (which I'm pretty excited about) and learning the ins-and outs of the team. One big thing you learn at Microsoft is that no-one will tell you what to do...you have to find out...this can be a huge challenge for new hires but in reality it's one of the biggest plusses...you make your own path and career.

One of the really surprising things I've discovered is  how much time the 'names' in the team actually spend 'down in the trenches'. Now this isn't reflecting badly on previous managers I've had, MS is a huge company and it comes with a lot of swarf which consumes a huge amount of time (I spend most of every day working this stuff out to make our releases quicker and smoother...). One example was today...I share an office with another PM (Phil Haack) who's 'owns' (in a very real sense, another MS thing...) the new ASP.NET MVC framework; it's still being worked on...it's very new and we spend a huge amount of effort getting this stuff 'just right'. Now, I'd always read the Scott Guthrie posts and though 'great PR...someone writes this stuff up and sticks it on his blog'...WRONG! He actually 'gets it'...to a very deep level...I've never met a Corporate Vice President of a multi-billion dollar company who has Visual Studio open on his laptop most of the time. He does...(oh and these are his settings). I was 'in the room' when he, Phil and Eilon were chatting about MVC...well put it this way...I'm now convinced he writes the blog posts...
Anyway, I continue to be impressed by the place I moved to and more and more convinced I made the right choice. I'm slightly getting over my fanboyism (another non-word!) but I'm still not into the whole 'super-excited' American thing (which I'm still not convinced is sincere) .

Oh, and this is my last syncophantic post...it's really not me!

Dear Comcast, I am not an idiot.

Update: Well, I guess Comcast monitor the search engines! I got a comment to this post as well as email and telephone follow-up on the issue. The post below was written out of frustration; and in no small part, concern for what this type of advice could have on inexperienced users. I know it's a small chip against a huge edifice of a company but hey, they took notice, it cost real time and money to follow up on this...which I thank them for doing. I know what it's like dealing with pissed off customers, it's not pleasant but most of the biggest lessons I've learnt over the years have been as the result of trying to solve customer's problems (whether I caused them or not).

Grr...I've never had as many problems with a company as I do with Comcast. I've written before about the forgetful DVR box...latest issue left me without Internet access for 3 days. *Something* messed up and I was getting slower and slower speeds then finally I got no connection at all. I did all the usual stuff, reset everything, uninstall any new software etc...but nothing helped. Finally I resigned myself to having to call customer service. First thing they always do is reset your modem...this didn't work at first...then suddenly it just started working after they told me to connect the modem direct to my laptop (I usually connect through a router) and they did a reset again.  The instruction of the woman on the line was that my router must be broken. After much fiddling I realize what really happened; they reset the MAC address my cable modem will accept. Comcast have a delightful habit of locking down your cable modem to the MAC address the installer finds on your PC when they first install your service...looks like they somehow lost my registration and 'helpfully' fixed it by resetting the service to the MAC address of my laptop...which of course broke my router. So long story short, cloned my laptop MAC on to my router and everything works again.
I have no idea what users who don't get this would do...probably end up buying a new router which still wouldn't work...grr...

Sorry, boring post but I hate when companies do this shit!

WAY OFF TOPIC: Is Virgin Coconut Oil really healthy...or is is just bad science?

I was lucky enough to work with some world class medical researchers in a previous career (which I sucked at but that's beside the point), one of the lessons I learnt whilst there was how to read research with a critical eye.

This popped up today when after accidentally buying a 54oz Jar of Extra Virgin Coconut oil (yes, alcohol was involved in this purchase) I decided to find out if the claimed benefits were actually validated anywhere. The manufacturer's claim that this stuff is incredibly good for you, lowers cholesterol and a bunch of other stuff.
My first stop for medical claims is Medline  (actually pubmed). Medline is the major search tool for medical researchers; studies there are published in 'peer-reviewed' journals (essentially journals where other medical researchers get a chance to pick apart the study...no guarantee of quality but better than nothing). Searching for Coconut Oil  gives a bunch of overwhelmingly negative studies; aha! say the proponents of 'Virgin Coconut Oil' the studies are all anti-tropical oils...non-hydrogenated Coconut Oil (which the stuff I bought supposedly is) is different...problem, there seems to be no proof (apart from a 4 year old study in a not very well recognized journal from an Indian researcher; who I have no idea of the background of...a major supplier of Coconut Oil is...India). Anyway, I'm still undecided, the research seems pretty limited; two names keep popping up, Raymond Peat, PhD and Dr. Mary Enig, both of whom wrote books about how good coconut oil is for you...always a warning sign, I always want to see truly 'independent' studies...if you're trying to sell me the stuff (or related books etc...) then I'm inclined to believe you might be a tad biased. My other worry is that there's very little peer reviewed research on what would seem like a HUGE discovery...too good to be true?

Anyway, just giving you a peek into my super-critical thinking process...and hey it tastes good (and I have a TON of it...I'm Scottish we don't waste food!)