Scott Hanselman

Exploring FAKE, an F# Build System for all of .NET

March 14, '14 Comments [24] Posted in Open Source
Sponsored By

I've been orbiting around F# - but not really jumping in - for a while now. In fact, I looked at F# in 2008 almost 6 years ago and more recently talked to Richard Minerich and Phillip Trelford on Hanselminutes Podcast #311 "Why F#?"

Last week I looked at using ScriptCS, literally C# as a Scripting language, to drive browser automation. Today I'm exploring a make system called FAKE. It's F# Make, a build automation system similar to Make (which is 38 years old next month!) or Rake (which uses Ruby).

Fake is a Domain Specific Language that you can use without knowing F#, but if and when you outgrow it you can keep heading down the F# road. In all cases you've got all of .NET at your command.

Here's their Hello World example, a basic deploy script:

#r "tools/FAKE/tools/FakeLib.dll" // include Fake lib
open Fake


Target "Test" (fun _ ->
trace "Testing stuff..."
)

Target "Deploy" (fun _ ->
trace "Deploy stuff..."
)

"Test" // define the dependencies
==> "Deploy"

Run "Deploy"

Note that Deploy depends on Test.

FAKE uses F# but you can use it go build whatever. These are some C# OSS projects that use FAKE to build themselves:

FAKE isn't new, it's actually been around for 4 years with 55 contributors so far! It works not only on .NET but also on Linux and Mac under Mono - it works everywhere. Because it's all .NET you can use it on your Continuous Integration Servers like TeamCity or Travis CI.

image

Getting Started with FAKE

Check out their Calculator sample, a .NET project you'll extend to build itself with FAKE. Just download the zip, unblock it, and unzip. Then run build.bat from a developer command prompt.

The build.net is a bootstrapper. It could be powershell or a shell script if you like, of course.

@echo off
cls
"tools\nuget\nuget.exe" "install" "FAKE" "-OutputDirectory" "tools" "-ExcludeVersion"
"tools\FAKE\tools\Fake.exe" build.fsx
pause

This batch file uses NuGet to get FAKE, just as npm install restores node_modules, or gem gets ruby libraries. Then it calls Fake on the build.fsx file. Follow their Getting Started instructions to slowly expand the responsibilities of the build.fsx file.

FAKE has a lot of Helpers in their API documentation. Hundreds, and there's a whole community making others that you can call upon. For example, the built in FileHelper has things like CleanDir to remove files and subdirs.

Here we Clean before we build by making Clean a dependency of Default. BuildDir here is a property that's shared.

// include Fake lib
#r "tools/FAKE/tools/FakeLib.dll"
open Fake

// Properties
let buildDir = "./build/"

// Targets
Target "Clean" (fun _ ->
CleanDir buildDir
)

Target "Default" (fun _ ->
trace "Hello World from FAKE"
)

// Dependencies
"Clean"
==> "Default"

// start build
RunTargetOrDefault "Default"

Jumping to the end of the tutorial, the syntax gets a little more tricky, butu once you get the |> format, it makes sense.

Some cool things to note and file away as interesting at in the script below.

  • the use of !! to include files
  • the use of -- to exclude a file spec after a !! operator
  • Zip is built-in as a helper and zips up the results of the build.
  • the options passed into NUnit
  • The dependency chain
  • #r for referencing .NET DLLs
// include Fake lib
#r "tools/FAKE/tools/FakeLib.dll"
open Fake

RestorePackages()

// Properties
let buildDir = "./build/"
let testDir = "./test/"
let deployDir = "./deploy/"

// version info
let version = "0.2" // or retrieve from CI server

// Targets
Target "Clean" (fun _ ->
CleanDirs [buildDir; testDir; deployDir]
)

Target "BuildApp" (fun _ ->
!! "src/app/**/*.csproj"
|> MSBuildRelease buildDir "Build"
|> Log "AppBuild-Output: "
)

Target "BuildTest" (fun _ ->
!! "src/test/**/*.csproj"
|> MSBuildDebug testDir "Build"
|> Log "TestBuild-Output: "
)

Target "Test" (fun _ ->
!! (testDir + "/NUnit.Test.*.dll")
|> NUnit (fun p ->
{p with
DisableShadowCopy = true;
OutputFile = testDir + "TestResults.xml" })
)

Target "Zip" (fun _ ->
!! (buildDir + "/**/*.*")
-- "*.zip"
|> Zip buildDir (deployDir + "Calculator." + version + ".zip")
)

Target "Default" (fun _ ->
trace "Hello World from FAKE"
)

// Dependencies
"Clean"
==> "BuildApp"
==> "BuildTest"
==> "Test"
==> "Zip"
==> "Default"

// start build
RunTargetOrDefault "Default"

You can do virtually anything and there's a great community out there to help.

Here's a more complex dependency chain with an optional parameter:

// Build order
"Clean"
==> "BuildApp"
==> "BuildTest"
==> "FxCop"
==> "NUnitTest"
=?> ("xUnitTest",hasBuildParam "xUnitTest") // runs the target only if FAKE was called with parameter xUnitTest
==> "Deploy"

There's a rich FAKE world out there with support for Octopus Deploy, all Unit Test systems, Xamarin's xpkg format and much more. Thanks to Steffan Forkmann for helping me explore this. ;)

Related Links


Sponsor: Big thanks to Red Gate for sponsoring the blog feed this week. Check out the Free Starter Edition of their release management tool! Deploy your SQL Server databases, .NET apps and services in a single, repeatable process with Red Gate’s Deployment Manager. Get started now with the free Starter Edition.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Back to Basics: Assert your assumptions and diff your source code

March 13, '14 Comments [20] Posted in ASP.NET | Back to Basics
Sponsored By

I've done a whole series of "Back to Basics" posts that I encourage you to check out. Sometimes I'll do a post as a result of a reader emailing me for help and this is one such post.

A person emailed me with an ASP.NET app was behaving differently on his computer vs. another developer's computer.

On his machine when he hit a protected page foo.aspx?returnurl=http://mymachine.domain.com

he would get a FORM element like this:

<form action="foo.aspx?returnurl=http://mymachine.domain.com">

but on everyone else's machines their HTML was:

<form action="foo.aspx">

They debugging and were frustrated and eventually reached out. They said:

1. there's nothing going on in the aspx of login.aspx that would append the querystring.

2. there's nothing going on in the code-behind of the aspx that manipulates Form.Action or messes with the Page.Render in any way.

So, I'm stumped, because the querystring is included on my machine, but not on others. I've tried comparing IIS components, web.config differences, application pool runtime type, machine.config differences, possible differences in Modules for IIS (IISrewrite), but nothing is giving me love. 

I suggested that they assert assumptions and start diffing everything. You can see in the last paragraph that they're comparing stuff but I think you really have to diff everything.

When something "works here but not there" my answer is always, what has changed? What's different? If the answer is "nothing is different" I'm just gonna say it again:

"What's different?"

What are some things we can check?

  • Code
    • Do you know what's on disk?
    • Do you know what ended up in memory? These are different things.
  • Configuration
    • There's local and machine-wide config to check
  • Network Traffic
    • This is often overlooked. The Internet is not a black box, but you'd be surprised how few people hook up a packet sniffer or even just Fiddler to look at HTTP traffic.
    • I've talked to developers who have said "well, that's under SSL so I can't see it." Oh, my friend, if you only knew.

I had them do a sniff and see if there was a difference in HTTP traffic. My assumption was that the HTTP_REFERER HTTP header was different and there was some code that was causing the page to render differently.

We went back and forth over a few days and my reader became frustrated and just added this line in their app's Page_Load:

this.Form.Action = Request.Url.ToString();

Here they are basically reasserting the Form action by pulling it from the URL. It's gross and it's a hack. It's a Band-Aid on Cancer.

They then started looking at the source for ASP.NET proper and then decided to disassemble the code that was running on the other person's machine. They then started to assert their assumptions.

Is the code running what's on disk? For a compiled language, do the binaries reflect the source?

They looked in Temporary ASP.NET files at the compiled ASPX markup pages and found this.

//ExternalSource("D:\WebApplications\Foo\login.aspx",27)
__ctrl.Method = "post";

//ExternalSource("D:\WebApplications\Foo\login.aspx",27)
__ctrl.Action = "login.aspx";

What? Why is someone setting the FORM Action manually? And there's a line number.

They had diff compared all the source code but not the markup/views/html.

Their markup:

<form id="Form1" method="post" runat="server">

Other person's markup:

<form id="Form1" method="post" runat="server" action="Login.aspx">

The other person had hard-coded the action in their source markup. They'd been diffing everything but the markup.

When you are comparing two code-bases, make sure to compare everything or you might just lose a day or two like this person.

Thanks to my reader for sharing this and letting me in on this debugging adventure.

Related Links:


Sponsor: Big thanks to Red Gate for sponsoring the blog feed this week. Check out the Free Starter Edition of their release management tool! Deploy your SQL Server databases, .NET apps and services in a single, repeatable process with Red Gate’s Deployment Manager. Get started now with the free Starter Edition.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Review: A tale of three Lenovo Laptops - X1 Carbon Touch, ThinkPad Yoga, IdeaPad Yoga 2 Pro

March 6, '14 Comments [54] Posted in Reviews
Sponsored By
ThinkPad Yoga, X1 Carbon Touch, and Yoga 2 Pro all together

I'm a big Lenovo fan and have used Thinkpads nearly exclusively since my first T60p. I'm using an first-gen X1 Carbon Touch as my main on the go machine these days. I've also tried using a Lenovo Yoga 2 Pro but had a little trouble with its extremely high-dpi display, although the build quality of the hardware is amazing.

I'm also trying out a loaner of a ThinkPad Yoga. What's the difference between the ThinkPad Yoga and the regular Yoga or Yoga 2 Pro? I think of the ThinkPad line, and this Yoga, as a business laptop. It has a TPM which is essential for Bitlocker encryption and VPN/DirectAccess without a Smartcard. Both very similar specs otherwise aside from the Yoga 2 Pro's super-high-res 3200x1800 screen.

Battery life on all these is reasonable, but not truly all-day long epic. You can get 6 hours on any of them, you just need to be thoughtful about what you are doing. Turn down brightness, use power plans smartly, and you're cool.

Frankly, the battery life Haswell brought us hasn't been as life-changing as has been the "RapidCharge" feature on the X1 Carbon Touch. A 30 min layover at an airport can get me almost 80% of my battery back. THAT is a feature that has changed how I work.

Lenovo ThinkPad Yoga

Yoga means the laptop is also a convertible and bends into a tent or a tablet. This is the consumer Yoga. My Mom and my wife both chose and use this model, coincidentally.

  • The ThinkPad Yoga has your choice of processor from a 4th Gen Intel i3 up to a to an i7-4600U at 3.3GHz.
  • You can get the rather low-res touch-enabled 1366x768 screen or the near-deal touch and pen (with a pen you can store in the device!) 1920x1080 screen. Get the 1080p one, I say.
  • This one uses mini-HDMI for its video out.

Lenovo IdeaPad Yoga 2 Pro

The Yoga 2 Pro has more options to build out but does top out on the processor side earlier.

  • The Yoga 2 Pro can also clock to up to an i7-4500U at 1.8Ghz.
    • Update: the clock speed for the 4500U is 1.8 and it's Max Turbo Frequency is 3.0.
  • It has a fantastic 13.3" QHD+ 32001800 screen.
  • Micro HDMI video output.
    • This was and remains the one totally unacceptable spec for me. As I present a lot, this connector is useless. It's too small, too weak, too unreliable, and too wonky. It only took three presentations before it broke. I don't blame Lenovo, I blame the connector and its spec. If you aren't going to use video out, don't sweat it at all. But if you are presenting daily, NEVER buy a laptop with micro HDMI. It will let you down.

Lenovo ThinkPad X1 Carbon Touch

I own and love the first generation X1. I'd really like to get my hands on the new one and its controversial keyboard and compare the two.

The X1 isn't a Yoga and while it does bend to flat and it does have a great touch-screen, it is NOT a tablet.

  • The X1 has goes from 4th Gen Intel i3 up to a to an i7-4600U at 2.1GHz.
    • Update: the clock speed for the 4600U is 2.1 and it's Max Turbo Frequency is 3.3.
  • Mine has a 1600x900 screen but you can now get up to QHD 2560x1440. This isn't as high as the Yoga 2 but when setting Windows at 150% it works nicely. If you have decent eyes you can avoid any high-dpi issues and just run at 100%.
  • The Mini DP Connector is great. I've never had an issue connecting to a projector with this laptop.

Things you need to consider if you travel

We can talk specs all day and you can dig into them if you like. Needless to say, they are fast, they have SSDs and you can get up to 8gig of RAM. Here's some things you may not have thought about when looking at an Ultrabook.

All Lenovo laptops are thin and light

These are all small and all around 3lbs. The Yoga 2 Pro is the sleekest and the most outwardly attractive. The X1 is a close second with its tapered nose. The ThinkPad Yoga is boxy and reliable looking.

  • What kind of Video Output does it have?
    • You won't get full-sized VGA on 90% of Ultrabooks. It'll be either HDMI, Micro-HDMI (a nightmare), or MiniDP (Mini Display Port.) When in doubt, go MiniDP all the way.
  • USB3 vs. USB2
    • Most Ultrabooks have one USB3 connector and one that's USB2. The USB3 one is almost always blue, that's how you can tell. Think about what your requirements are what if you'll need a nice USB adapter. I recommend combination USB3 hubs with included Ethernet. I own one and love it.
  • Will you dock your laptop a lot?
    • If so, consider the new OneLink Pro dock from Lenovo. It's in the ThinkPad line of laptops, so that's the ThinkPad Yoga or the 2nd Gen X1 Carbon Touch. That means one connector gets you power, USB along with 6 (!) ports, 4 of which are USB3. You'll also get DisplayPort up to 2560x1600 and a DVI-I connector.
    • I own the original USB3 dock which uses USB3 and DisplayLink technology to run up to two additional monitors. The video is compressed and you have to plug in both a USB3 connector and the power. It works, and I'm happy with it, but OneLink is a clear improvement.

Keyboards

I have always loved ThinkPad keyboards. The W520 workhorse has the best laptop keyboard ever, to this day, IMHO. The first generation X1 Carbon Touch is a close second.

X1 Carbon Touch Keyboard

The ThinkPad Yoga's keyboard is good, but a few changes like the removal of the insert button from the top row as well as the de-emphasis of the function keys did slow me down for a few days.

The Yoga also changes the TrackPad a little by making in larger, clickier, and removes the physical buttons for folks who like "The Nub" for their mousing. Note that the buttons are still there, they are just integrated into the top of the TrackPad so your muscle memory doesn't need to change.

ThinkPad Yoga Keyboard

The Yoga 2 Pro keyboard keys don't have the subtle concave shape that the ThinkPad line is known for. The keyboard is nearly flat. It also seemed to show hand grease a little more, although clearly a cloth solves that problem quickly. As a fast touch-typist I'm a little slower on this keyboard but it's certainly reasonable and only took me a few days to adapt. I do prefer the X1, though.

Yoga 2 Pro Keyboard

Resolutions

I just love 1080p on a 13.3" screen. It's just large enough that it feels roomy but not so big that it's squinty. This collection of three laptops straddles that ideal, though. My wife doesn't see the difference and works on the 768 or 900 machines with no complaint. My wife has a retina one and doesn't appreciate it (or notice its screen). I prefer 1080 or 1800 if I'm doing multiple window website debugging. At 1800p the pixels just disappear.

1366x768 you can see the pixels

1600x900 is a nice compromise

3200x1800 is insane. No pixels to be seen.

My Wish List for the Ultimate Lenovo Ultrabook

This is simple.

  • Micro-HDMI is a failed connector. The industry needs to accept this and stop using it.
    • There is only full-sized HDMI or ideally, MiniDP.  Mini Display Port, in my experience, always works and works well. Adapters are many and plentiful and I always feel comfortable going to a conference with a MiniDP laptop as I know they can handle it.
  • I want more RAM. Always. Give me a 12 gig Ultrabook, please, Lenovo. That said, these machines have happily run VS, Outlook and two Virtual Machines without complaint.
  • Anything over 1080p at 13"+ is the sweet spot resolution for me. Retina is nice but Windows 8.1 isn't quite there yet on the desktop. Soon I hope.
  • A 256 gig SSD is the ideal size for me. 128 is a little cramped for a developer.
  • #MOARYOGA - The whole Yoga hinge is brilliant.

Give me an X1 Yoga with the fastest i7, 256G SSD, a Mini DP connector, and a screen that is anything over 1080p and we're cool. You can...

Shut up and take my money

Today, I'm happy using the X1 Carbon Touch until I see the new X1. But I really recommend any of these devices if the tech specs and connectors meet your requirements.

Related Links


Sponsor: Big thanks to ComponentOne, a division of GrapeCity, for sponsoring the blog this week. Their widely popular .NET control suite, Studio Enterprise contains hundreds of data and UI controls such as grids, charts and reports that offer the functionality, features and support you need for current and future application development. Download your trial today!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

NuGet Package of the Week: FluentAutomation for automated testing of Web Applications

March 4, '14 Comments [25] Posted in NuGet | NuGetPOW | Open Source
Sponsored By

FluentAutomation starting a testLast week I was exploring today's varied choices we have for Automated Browser Testing. There's headless WebKit "browsers" like PhantomJS and cloud powered multi-browser testing tools like BrowserStack and SauceLabs.

Selenium is kind of the gold standard and offers not only a lot of "drivers" but also a lot of language bindings with which drive a browser. Sometimes browsers update so fast there can be some version incompatibilities with Selenium, but for the most part it works great once you've settled in.

One option I've been looking at is FluentAutomation. It's a fluent automation API that supports Selenium as well as WatiN along with all their flavors and drivers. Since Fluient supports Selenium, that means you can use the Selenium ChromeDriver, IEDriver, Remote Web Driver or even the headless PhantomJS. FluentAutomation is on GitHub, of course, as well as on NuGet.

FluentAutomation has great (and growing) documentation and has adopted and interesting fluent style for it's API.

Now, not everyone likes a "fluent" API so it may take a while to get used to. Often you'll be doing things over many lines when it's really just one line, for example, this is one line:

I.Open("http://automation.apphb.com/forms")
.Select("Motorcycles").From(".liveExample tr select:eq(0)")
.Select(2).From(".liveExample tr select:eq(1)")
.Enter(6).In(".liveExample td.quantity input:eq(0)")
.Expect
.Text("$197.72").In(".liveExample tr span:eq(1)")
.Value(6).In(".liveExample td.quantity input:eq(0)");

Notice the method chaining as well as the use of CSS selectors.

FluentAutomation also has the cool concept of a PageObject to take your potentially brittle scripts and give them more structure. PageObjects group your actions, expectations, and assertions and let you reuse code when a page appears in multiple tests.

For example you could have a high level test (this is XUnit, but you can use whatever you want):

public class SampleTest : FluentTest {
public SampleTest() {
SeleniumWebDriver.Bootstrap(SeleniumWebDriver.Browser.Chrome);
}

[Fact]
public void SearchForFluentAutomation() {
new BingSearchPage(this)
.Go()
.Search("FluentAutomation")
.FindResultUrl("http://fluent.stirno.com/blog/FluentAutomation-scriptcs/");
}
}

Then you can have separate PageObjects that have your own public methods specific to that page, as well as assertions you can reuse.

public class BingSearchPage : PageObject<BingSearchPage> {
public BingSearchPage(FluentTest test) : base(test) {
Url = "http://bing.com/";
At = () => I.Expect.Exists(SearchInput);
}

public BingSearchResultsPage Search(string searchText) {
I.Enter(searchText).In(SearchInput);
I.Press("{ENTER}");
return this.Switch<BingSearchResultsPage>();
}

private const string SearchInput = "input[title='Enter your search term']";
}

public class BingSearchResultsPage : PageObject<BingSearchResultsPage> {
public BingSearchResultsPage(FluentTest test) : base(test) {
At = () => I.Expect.Exists(SearchResultsContainer);
}

public BingSearchResultsPage FindResultUrl(string url) {
I.Expect.Exists(string.Format(ResultUrlLink, url));
return this;
}

private const string SearchResultsContainer = "#b_results";
private const string ResultUrlLink = "a[href='{0}']";
}

You don't have to be all structure and OO if you don't want. You can just as easily write scripts with FluentAutomation and head in a different direction.

FluentAutomation along with ScriptCS = Automating your Browser with C# Script

I've usually used Python with my Selenium scripts. I like being able to just make a text file and start scripting, then run, debug, continue, all from the command line. It feels simple and lightweight. Creating a DLL and running Unit Tests in C# usually comes later, as I can move faster with a "scripting language."

You can do that with ScriptsCS as it gives you project-less C# that effectively is C# as scripting language. Combine this with FluentAutomation and you've potentially got the best of both worlds.

To install, first you need the Windows apt-get open source equivalent, the oddly-named and -spelled Chocolatey. Then you get ScriptCS and the packages for FluentAutomation.

  • Install Chocolatey - one line installation here
  • Run "cinst ScriptCS" from your command line to use Chocolatey to install ScriptCS
  • Now, get the ScriptCS script packages for FluentAutomation like this:
    • scriptcs -install FluentAutomation.SeleniumWebDriver
    • scriptcs -install ScriptCs.FluentAutomation

Now, as a quick test, create a folder and put a text file called start.csx in it with just these contents:

var Test = Require<F14N>()
.Init<FluentAutomation.SeleniumWebDriver>()
.Bootstrap("Chrome")
.Config(settings => {
// Easy access to FluentAutomation.Settings values
settings.DefaultWaitUntilTimeout = TimeSpan.FromSeconds(1);
});

Test.Run("Hello Google", I => {
I.Open(http://google.com);
});

Notice how there's no namespace, no classes, no main. It's just a script, except it's using C#. You can change the "Chrome" to "IE" or "Firefox" as well, to play around.

Random: I love this Selenium feature, exposed by FluentAutomation...take screenshot!

// Take Screenshot
I.TakeScreenshot("LoginScreen");

If you don't want ScriptCS, while it can act as a REPL itself, there is also the start of a dedicated FluentAutomation REPL (read–eval–print loop). This is basically a command prompt that lets you explore you app interactively and facilitates building your scripts. You can get the Repl as a Chocolatey package as well and just "cinst FluentAutomation.Repl"

You've got LOTS of choices in the world of automated testing. There's so many choices that there's just no good excuse. Pick a library, pick a language, and start automating your web app today.

Related Links


Sponsor: Big thanks to ComponentOne, a division of GrapeCity, for sponsoring the blog this week. Their widely popular .NET control suite, Studio Enterprise contains hundreds of data and UI controls such as grids, charts and reports that offer the functionality, features and support you need for current and future application development. Download your trial today!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Hanselman's Newsletter of Wonderful Things: January 21st, 2014

March 4, '14 Comments [1] Posted in Newsletter
Sponsored By

I have a "whenever I get around to doing it" Newsletter of Wonderful Things. Why a newsletter? I dunno. It seems more personal somehow. Fight me.

You can view all the previous newsletters here. You can sign up here to the Newsletter of Wonderful Things or just wait and get them some weeks later on the blog, which hopefully you have subscribed to. If you're signed up via email, you'll get all the goodness FIRST. I also encourage you to subscribe to my blog. You can also have all my blog posts delivered via email if you like.

Here's the Newsletter from January. Subscribers get the new one first. ;) 


Thanks again for signing up for this experiment. Here's some interesting things I've come upon this week. If you forwarded this (or if it was forwarded to you) a reminder: You can sign up at http://hanselman.com/newsletter and the archive of all previous Newsletters is here.

Remember, you get the newsletter here first. This one will be posted to the blog as an archive in a few weeks.

Scott Hanselman

(BTW, since you *love* email you can subscribe to my blog via email here: http://feeds.hanselman.com/ScottHanselman DO IT!)

P.P.S. You know you can forward this to your friends, right?


Sponsor: Big thanks to ComponentOne, a division of GrapeCity, for sponsoring the blog this week. Their widely popular .NET control suite, Studio Enterprise contains hundreds of data and UI controls such as grids, charts and reports that offer the functionality, features and support you need for current and future application development. Download your trial today!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.