Scott Hanselman

10 Guerilla Airline Travel Tips for the Geek-Minded Person

March 31, '08 Comments [26] Posted in Musings
Sponsored By

Photo by Joshua DavisThere's a million sites with a million travel tips, so I won't even try to imply that I know what the Rick Steves of the world have worked so hard to figure out.

However, I do fly around a lot and I have for many years. I'm a geek, I like tools and I solve problems in my own niche way. It's not the way others might do it, but it seems to be working pretty well.

I just narrowly avoided a REALLY bad situation in Chicago (ORD) a few minutes ago where six flights to New York's LGA airport had cascading delays. My 3pm flight was now at 6pm which would have had me arriving into New York at 9pm! Considering I have a 9am talk, that could be disastrous. Here's how I saved my butt.

#1 Take Action at the first sign of trouble

Always have the # of the airline (or their preferred line). Nothing wastes time like digging for stuff. Use a service like tripit.com to keep all your travel details in one place. For example, today I called United from my phone the second I saw the delay appear on the board.  I also go in line at Customer Service, but I had a person on the phone well before the line moved.

#2 Know the schedule

Don't just know the schedule for your airline, or for your flight. Know and print out ALL the flights going to your destination the day you're traveling. This provides you power as you'll know what parallel flights are leaving before the other travelers, even before the flight personnel. I use http://mobile.flightstats.com/go/Mobile from my phone to stay on top of flights while on the go.

Knowing other airlines' schedule is useful because when mechanical difficulty cancels a flight you can insist that Airline #1 move you to Airline #2 if it's totally clear that there's no other way to get you to your destination on #1. Last flight I was on United had a mechanical difficulty and completely canceled my flight. The whole plane got in line to get on the next flights out, but this was the last flight of the day out of that city for that airline. I knew there was a Delta flight in an hour, so I took off for the Delta desk while calling United at the same time. I told them what flight I was on and that I wanted to be moved to Delta. I was kind, but firm, and only 1 hour late coming home. As I was boarding the Delta flight, I saw that United was passing out hotel vouchers for the folks on the first flight.

#3 Make their job easy

Speak their language and tell them what they can do to get you out of their hair. Refer to flights by number when calling reservations, it saves huge amounts of time. For example, today I called United and I said:

"Hi, I'm on delayed United 686 to LGA from Chicago. Can you get me on standby on United 680?"

Simple and sweet. I noted that UA680 was the FIRST of the 6 flights delayed and the next one to leave. I made a simple, clear request that was easy to grant. I told them where I was, what happened, and what I needed all in one breath. You want to ask questions where the easiest answer is "sure!"

#4 Never give up a guaranteed seat for a chance at a seat.

That said, always get on cascading standby. Make sure to ask them if your reservation will move from flight to flight if you don't get on standby. You'd be surprised how many reservations go missing or float around in the system. Always make sure you have your ACTUAL guaranteed ticketed seat for some flight later in the day in case the earlier standby's don't work out.

#5 Never check luggage

I did two weeks in Malaysia once with only carry on. Seriously, checking your bags not only slows you down physically but it also limits your options. When you talk to Customer Services, the FIRST thing they'll ask is "did you check bags." Your bags can't move as fast as you can.

#6 Be absolutely pleasant at every point

I can't stress this enough. Never raise your voice or demand anything. Be nice to people. Nothing you need to go (unless it's a child's health) to is important enough have a complete freak-out in public at the Airport. I've seen personally a half dozen different incidents where Airport Police have taken people away and charged them for disorderly conduct. More importantly, very rarely will you be talking to the person who screwed up your travel. They are just doing their job.

Try to be inclusive, using terms like "we," like "what can we do to fix this?" If the person seems to have a power trip, try using "you" sentences that inflate their sense of power. "Can you help me make this right?"

Today I said to an agent, "If you get me on this flight, you'll only need 2 more miracles for sainthood!" This got me an immediate smile and a pleasant transaction.

#7 Keep ahead of the wave

When disaster strikes, you have 15 minutes before the masses figure it out. Folks will queue at the drop of a hat, but savvy travelers will leap into action and start a multi-pronged approach. Call your assistant, spouse,  boss, travel agent, get on the phone, go to the departure boards. Always have at least two options. Even try going to the next gate, preferably a near-empty one with an agent behind it. Anyone at any terminal can usually fix your issue while the rest wait in a queue.

#8 Setup SMS Alerts on your airline

The best way to know what's happening before the public is via SMS Alerts. Corporate will often send gate changes before they are announced on the flight. This can save you time while trying to find the monitors upon leaving the plane. You can also setup notifications for delays. More information is better.

#9 Always wear a jacket or sport coat.

Don't look like a schlub. Have a nice pair of shoes. Shave. Well-dressed, kind, professional people get upgrades. I started wearing nice Cole Haan shoes and a sport coat when I travel and I've been consistently upgraded about 1/5th of the time. I'm convinced being fresh and clean and pleasant is the reason.

#10 Use your miles for upgrades

I don't have status on any airline, just a bunch of miles all over. Never enough for a free flight, so I never even try to use my miles for free flights . I always use them for upgrades and I offer to use them at the point where I'm talking to a customer service representative. Often, if you are pleasant beforehand, they'll just upgrade you without deducting the miles. Worst case, you get upgraded and use your miles. Best case, you're just upgraded.

Eek, they've called my boarding group, so I'm off! Bye!

Technorati Tags: ,,

Photo by Joshua Davis

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

Spontaneous New York Ethiopian Nerd Dinner

March 31, '08 Comments [17] Posted in Internationalization | Musings
Sponsored By

sheba_11 I'm going to be in NYC for 3 short days (travel, talk, travel) and Dmitry Lyalin and I were thinking to do a dinner thing on Tuesday evening.

I've got an early flight out at 9am on Weds, so we'll be at Queen of Sheba NYC on Tuesday around 6:30pm. Hopefully we won't get kicked out for not having a reservation.

Every time I go to ANY town, anywhere in the world for the last 10 years, I always go to the nearest Ethiopian Restaurant. Consequently, if your town has a habesha me'gub beyt I've probably eaten there.

Ethiopian food is my grub. I could eat it all day long. I'm also into the Amharic Language, and recently Aleme from Beteseb.com read about my interest on my blog and was kind enough to send me a Fidel (The Ethiopian Alphabet is often hung as art), bringing my collection of Fidel to three. Time to find a better place to hang them. (Note, my wife is Ndebele, not Habesha, but I learned the language before I met her).

So, if you've never had Ethiopian Food, here's a good enough opportunity to come hang out and try it. (Of course, we're all going Dutch.)

RSVP in the Comments!

Related Links

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

Hanselminutes Podcast 106 - Inside Outsourcing

March 31, '08 Comments [12] Posted in Podcast
Sponsored By

windia08 My one-hundred-and-sixth podcast is up. This was an unusual show as I was at Mix and saw two Regional Director buddies of mine, Vinod Unny (profile) and Venkatarangan TNC (profile) and we started chatting. Another person listening in thought the topic was interesting and said we ought to record it, so I busted out the recording gear and we did an on-the-spot recording on the effects of outsourcing from both the American and Indian perspective.

Subscribe: Subscribe to Hanselminutes Subscribe to my Podcast in iTunes

If you have trouble downloading, or your download is slow, do try the torrent with µtorrent or another BitTorrent Downloader.

Do also remember the complete archives are always up and they have PDF Transcripts, a little known feature that show up a few weeks after each show.

Telerik is our sponsor for this show.

Check out their UI Suite of controls for ASP.NET. It's very hardcore stuff. One of the things I appreciate about Telerik is their commitment to completeness. For example, they have a page about their Right-to-Left support while some vendors have zero support, or don't bother testing. They also are committed to XHTML compliance and publish their roadmap. It's nice when your controls vendor is very transparent.

As I've said before this show comes to you with the audio expertise and stewardship of Carl Franklin. The name comes from Travis Illig, but the goal of the show is simple. Avoid wasting the listener's time. (and make the commute less boring)

Enjoy. Who knows what'll happen in the next show?

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

Hanselminutes Podcast 105 - Rocky Lhotka on Data Access Mania, LINQ and CSLA.NET

March 28, '08 Comments [7] Posted in LINQ | Podcast | Programming
Sponsored By

rocky0005-120wMy one-hundred-and-fifth podcast is up. I got a chance to sit down with Rocky Lhotka (blog) and talk about the direction data access, business objects and multi-tier development are going, as well as where he things LINQ fits into his view of CSLA.NET. CSLA.NET is Rocky's application development framework that supports his multi-tiered view of business application development.

Subscribe: Subscribe to Hanselminutes Subscribe to my Podcast in iTunes

If you have trouble downloading, or your download is slow, do try the torrent with µtorrent or another BitTorrent Downloader.

Do also remember the complete archives are always up and they have PDF Transcripts, a little known feature that show up a few weeks after each show.

Telerik is our sponsor for this show.

Check out their UI Suite of controls for ASP.NET. It's very hardcore stuff. One of the things I appreciate about Telerik is their commitment to completeness. For example, they have a page about their Right-to-Left support while some vendors have zero support, or don't bother testing. They also are committed to XHTML compliance and publish their roadmap. It's nice when your controls vendor is very transparent.

As I've said before this show comes to you with the audio expertise and stewardship of Carl Franklin. The name comes from Travis Illig, but the goal of the show is simple. Avoid wasting the listener's time. (and make the commute less boring)

Enjoy. Who knows what'll happen in the next show?

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

The Weekly Source Code 22 - C# and VB .NET Libraries to Digg, Flickr, Facebook, YouTube, Twitter, Live Services, Google and other Web 2.0 APIs

March 27, '08 Comments [31] Posted in ASP.NET | Programming | Source Code | Web Services | XML
Sponsored By

Someone emailed me recently saying that they couldn’t find enough examples in .NET for talking to the recent proliferation of “Web 2.0 APIs” so I thought I’d put together a list and look at some source. I think that a nice API wrapper is usually a useful thing, but since these APIs are so transparent and basic, there's not really a huge need given LINQ to XML but I understand the knee-jerk reaction to hunt for a wrapper when faced with the word "API."

One thing to point out is that 99.9% of these APIs are calling

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);

under the covers the doing something with the resulting string. Some hide the URL creation, some use XmlDocuments, others use XmlSerialization. When you use a random API you find on the net you're usually getting what you pay for. You're getting a one person's free view on how they perceived a certain API should be called. Some will like be more performant than others. Some might be better thought out than others.

I'll try to juxtapose a few differences between them, but I just want you to remember that we're talking about pushing Angle Brackets around, and little else. You can always do it yourself.

And so, Dear Reader, I present to you twenty-first in a infinite number of posts of "The Weekly Source Code."

Digg

Digg is a community-voted controlled explosion of news stories. Their API is "REST" and speaks XML or JSON on the wire.

DiggApiNET is a .NET Wrapper for the Digg API. It has no releases, so you'll have to get the source code. It was last updated in May of 2007. There's also another at CodeProject called, creatively, digg API.NET.

Let's talk philosophy of design and look a the first library. Here's some snippets pulled from all over the code. This API builds the URL and loads the results of the call into an XmlDocument, holds it for a second and SelectNodes the values into Digg-specific objects. These objects know about the existence of System.Xml.

private const string get_popular = "http://services.digg.com/stories/popular/comments/{0}";

public DiggComments GetPopular()
{
return GetPopular(new Hashtable());
}
public DiggComments GetPopular(Hashtable args)
{
string uri = String.Format(get_popular, HttpBuildUrl(args));
return new DiggComments(Request(uri));
}
public DiggComments(XmlDocument xml_doc) : base(xml_doc, "events")
{
_comments = new List();
if (xml_doc.SelectSingleNode("events") == null
|| xml_doc.SelectSingleNode("events").SelectNodes("comment") == null) {
throw new DiggApiException("XML response appears to be malformed, or contains unexpected data.");
}
foreach (XmlNode node in xml_doc.SelectSingleNode("events").SelectNodes("comment")) {
_comments.Add(new DiggComment(node));
}
}

This is a pretty straight-forward if not totally "clean" way to do it. SelectSingleNode and SelectNodes aren't too fast, but we're looking at tiny chunks of data, probably under 100k. I'd probably do it with either XmlReader or XmlSerializer, or more likely, LINQ to XML. I'd make a service that handle the wire protocol, and make the objects know less.

Facebook

Facebook has a very sophisticated and deep API and there's lots of support for it on .NET that is well explained by Nikhil. You can develop for Facebook using the free Express Visual Studio editions.

There's quite a few available:

Nikhil's Facebook client APIs feel well factored, with separate services for each major Facebook service and a FacebookSession object proving contextual state. Requests are pulled out into FacebookRequest and include asynchronous options, which is thoughtful.

Here's an edited (for brevity) example of a WinForm that allows you to set your Facebook status. I like the IsPermissionGranted call, which I think is clean and clever, given that there is a large enum of permissions.

    public partial class StatusForm : Form {

private const string _apiKey = "[Your API Key]";
private const string _secret = "[Your Secret]";

private FacebookService _fbService;
private bool _loggingIn;

private void LoadStatus() {
_nameLabel.Text = "Loading...";

User user = _fbService.Users.GetUser(null, "name,status");
if (user != null) {
_nameLabel.Text = user.Name;

_statusTextBox.Text = user.Status.Message;
_dateLabel.Text = user.Status.UpdateDate.ToLocalTime().ToString("g");
}

bool canSetStatus = _fbService.Permissions.IsPermissionGranted(Permission.SetStatus);
_permissionsLink.Visible = !canSetStatus;
_updateButton.Enabled = canSetStatus;
_statusTextBox.ReadOnly = !canSetStatus;
}

protected override void OnActivated(EventArgs e) {
base.OnActivated(e);

if ((_fbService == null) && (_loggingIn == false)) {
_loggingIn = true;

try {
FacebookClientSession fbSession = new FacebookClientSession(_apiKey, _secret);
if (fbSession.Initialize(this)) {
_fbService = new FacebookService(fbSession);
LoadStatus();
}
}
finally {
_loggingIn = false;
}
}
}

private void OnUpdateButtonClick(object sender, EventArgs e) {
string text = _statusTextBox.Text.Trim();

_fbService.Users.SetStatus(text, /* includesVerb */ true);
LoadStatus();
}
}
}

Interestingly, the Facebook API also includes it's own JsonReader and JsonWriter, rather than using the new JsonSerializer, presumably because the lib was written a year ago.

Windows Live Services

There's a bunch of info on http://dev.live.com/ and a bunch of complete sample apps with source as well as a Live SDK interactive site. The Live Contacts API, for example . Unfortunately with the Contact's API there's no .NET samples I can find that includes wrappers around the angle brackets, so you'll be parsing in whatever way you prefer.

The objects that are provided in the Alpha SDK are really focused initially on security and permissions. For example, before I was able to access my contacts programmatically, I had to explicitly allow access and chose a length of time to allow it. I allowed it for a day to be extra secure.

Once you've retrieved some data, it's very simple so a request like https://cumulus.services.live.com/wlddemo@hotmail.com/LiveContacts would give you:

<LiveContacts> 
   <Owner>
       <FirstName/>
       <LastName/>
       <WindowsLiveID/>                 
   </Owner>                       
   <Contacts>         
<Contact>            
<ID>{ContactID}</ID>            
<WindowsLiveID>{Passport Member Name}</WindowsLiveID>
       <Comment>comment here</Comment>            
<Profiles/>            
<Emails/>            
<Phones/>           
<Locations/>        
</Contact>     
</Contacts> </LiveContacts>

The Live Search API speaks SOAP and has samples in six languages including C#, VB, Ruby, PHP, Python, and Java.

YouTube

YouTube has two different versions of their API, but the original/old version is officially deprecated. Now that they are Google, the YouTube APIs are all GData style, replacing their REST/XML-RPC APIs.

There is a .NET Library that speaks the GData XML format and querying YouTube with C# is fairly simple from there. You can even upload videos programmatically to YouTube like this gentleman.

This fellow eschews GData's uber libraries and uses a StringBuilder to build the GData payload and that's OK. :)

private string GetHeader(string title, string description, Catagory catagory,
                         string keywords, string videoFileName)
{
    StringBuilder xml = new StringBuilder();
    xml.Append(boundary + lineTerm + "Content-Type: application/atom+xml; charset=UTF-8" + lineTerm + lineTerm);
    xml.Append("<?xml version=\"1.0\"?><entry xmlns=\"http://www.w3.org/2005/Atom\" ");
    xml.Append("xmlns:media=\"http://search.yahoo.com/mrss/\" xmlns:yt=\"http://gdata.youtube.com/schemas/2007\">");
    xml.AppendFormat("<media:group><media:title type=\"plain\">{0}</media:title>", title);
    xml.AppendFormat("<media:description type=\"plain\">{0}</media:description>", description);
    xml.AppendFormat("<media:category scheme=\"http://gdata.youtube.com/schemas/2007/categories.cat\">{0}</media:category>", catagory);
    xml.AppendFormat("<media:keywords>{0}</media:keywords>", keywords);
    xml.Append("</media:group></entry>" + lineTerm);
    xml.Append(boundary + lineTerm + "Content-Type: video/*" + lineTerm + "Content-Transfer-Encoding: binary" + lineTerm + lineTerm);
    return xml.ToString();
}

GData

GData is Google's standard protocol for moving data around via XML and HTTP. There are GData endpoints for Blogger, Google Calendar, Notebook, Spreadsheets, Documents, Picassa, etc. From their site:

NET Developer Guides exist for specific Data APIs. They can be found under the page for each Data API

The GData C# client is written by Google, so I was really interested to read their code as their interview process is legendary and I assume everyone is a 17 year old PhD. The code is exceedingly object oriented with more than 165 files over 10 folders (not counting unit tests and project stuff). It's also VERY well commented, but interestingly, not always commented using the standard XML comments most MSFT Programmers use, but rather a different format I'm not familiar with.

All the APIs are fairly similar. Here's a GData sample that Queries the Calendar for events within a date range.

static void DateRangeQuery(CalendarService service, DateTime startTime, DateTime endTime)
{
EventQuery myQuery = new EventQuery(feedUri);
myQuery.StartTime = startTime;
myQuery.EndTime = endTime;

EventFeed myResultsFeed = service.Query(myQuery) as EventFeed;

Console.WriteLine("Matching events from {0} to {1}:",
startTime.ToShortDateString(),
endTime.ToShortDateString());
Console.WriteLine();
for (int i = 0; i < myResultsFeed.Entries.Count; i++)
{
Console.WriteLine(myResultsFeed.Entries[i].Title.Text);
}
Console.WriteLine();
}

Here's an example that downloads all the pictures from a specific username in Picassa using C#. Everything in GData is an "AtomEntry" and many have extensions. You can handle the GData types or use specific sub-classes like PhotoQuery, or whatever, to make thing easier.

private static void DownAlbum(string UserN, string AlbumN)
{
string fileName;
Uri uriPath;
WebClient HttpClient = new WebClient();
// Three important elements of PicasaWeb API are
// PhotoQuery, PicasaService and PicasaFeed
PhotoQuery query = new PhotoQuery();
query.Uri = new Uri(PhotoQuery.CreatePicasaUri(UserN, AlbumN));
PicasaService service = new PicasaService("Sams PicasaWeb Explorer");
PicasaFeed feed = (PicasaFeed)service.Query(query);

Directory.SetCurrentDirectory("c:\\");
foreach (AtomEntry aentry in feed.Entries)
{
uriPath = new Uri(aentry.Content.Src.ToString());
fileName = uriPic.LocalPath.Substring(uriPath.LocalPath.LastIndexOf('/')+1);
try {
Console.WriteLine("Downloading: " + fileName);
HttpClient.DownloadFile(aentry.Content.Src.ToString(), fileName);
Console.WriteLine("Download Complete");
}
catch (WebException we)
{ Console.WriteLine(we.Message); }
}
}

You can also certainly use any standard System.Xml APIs if you like.

GData is an extension of the Atom Pub protocol. Atom Pub is used by Astoria (ADO.NET Data Extensions) which can be accessed basically via "LINQ to REST."

Flickr

Flickr has a nice API and WackyLabs has a CodePlex project for their FlickrNET API Library written in C#. It's also confirmed to work on Compact Framework and Mono as well as .NET 1.1 and up. There's a fine Coding4Fun article on this library.

This API couldn't be much easier to use. For example, this searches for photos tagged blue and sky and makes sure it returns the DateTaken and OriginalFormat.

PhotosSearchOptions options = new PhotosSearchOptions();
options.Tags = "blue,sky";
options.Extras |= PhotoSearchExtras.DateTaken | PhotoSearchExtras.OriginalFormat;
Photos photos = flickr.PhotosSearch(options);

The PhotosSearch() method includes dozens of overloads taking date ranges, paging and other options. All the real work happens in GetResponse() via GetResponseCache(). The URL is built all in one method, the response is retrieved and deserialized via XmlSerializer. This API is the closest to the way I'd do it. It's pragmatic, uses as much of the underlying libraries as possible. It's not really extensible or overly OO, but it gets the job done cleanly.

Since Flickr is a data intensive thing, this library also includes a thread safe PersisitentCache for storing all that data. I'd probably just have used System.Web.Cache because it can live in any application, even ones outside ASP.NET. However, theirs is a Persistent one, saving huge chunks of data to a configurable location. It's actually an interesting enough class that it could be used outside of this lib, methinks. It stores everything in a super "poor man's database," basically a serialized Hashtable of blobs, ala (gasp) OLE Structured Storage.

WordPress and XML-RPC based Blogs

Most blogs use either the Blogger or MetaWeblog APIs and they are easy to call with .NET.  That includes MSN Spaces, DasBlog, SubText, etc. There's samples deep on MSDN on how to call XML-RPC with C# or VB.

Windows Live Writer and BlogJet use these APIs to talk to blogs when you're authoring a post, so I'm using .NET and XML-RPC right now. ;)

A very simple example in VB.NET using the very awesome XML-RPC.NET library is here. Here's a more complete example and here's a mini blogging client.

DasBlog uses this library to be an XML-RPC Server.

In this sample, the type "IWP" derives from XmlRpcProxy and uses the category structure. The library handles all the mappings an deserializaiton such that calling XML-RPC feels ;like using any Web Service, even though XML-RPC is a precursor to SOAP and not the SOAP you're used it.

Dim proxy As IWP = XmlRpcProxyGen.Create(Of IWP)()
Dim args() As String = {“http://myblog.blogstogo.com”, _
“username”, “password”}
Dim categories() As category
categories = proxy.getCategories(args)

You can also use WCF to talk XML-RPC

Twitter

I've talked about Twitter before and they have a Twitter API that is at least an order of magnitude more important than their site. There is a pile of source out there to talk to Twitter.

Last year Alan Le blogged about his adventures in creating a library around Twitter's API and Witty is a actively developed WPF C# application that fronts Twitter. You can browse their source and see their simple TwitterLib.

TwitterNet.cs is the meat of it and just builds up objects using XmlDocuments and does what I called "left hand/right hand" code. That's where you've got an object on the left and some other object/bag/pileOdata on the right and you spend a lot of lines just going "left side, right side, left side, right side.

For (trimmed) example:

 public UserCollection GetFriends(int userId)
{
UserCollection users = new UserCollection();

// Twitter expects http://twitter.com/statuses/friends/12345.xml
string requestURL = FriendsUrl + "/" + userId + Format;

int friendsCount = 0;

// Since the API docs state "Returns up to 100 of the authenticating user's friends", we need
// to use the page param and to fetch ALL of the users friends. We can find out how many pages
// we need by dividing the # of friends by 100 and rounding any remainder up.
// merging the responses from each request may be tricky.
if (currentLoggedInUser != null && currentLoggedInUser.Id == userId)
{
friendsCount = CurrentlyLoggedInUser.FollowingCount;
}
else
{
// need to make an extra call to twitter
User user = GetUser(userId);
friendsCount = user.FollowingCount;
}

int numberOfPagesToFetch = (friendsCount / 100) + 1;

string pageRequestUrl = requestURL;

for (int count = 1; count <= numberOfPagesToFetch; count++)
{
pageRequestUrl = requestURL + "?page=" + count;
HttpWebRequest request = WebRequest.Create(pageRequestUrl) as HttpWebRequest;
request.Credentials = new NetworkCredential(username, password);

try
{
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(response.GetResponseStream());
XmlDocument doc = new XmlDocument();
doc.Load(reader);
XmlNodeList nodes = doc.SelectNodes("/users/user");

foreach (XmlNode node in nodes)
{
User user = new User();
user.Id = int.Parse(node.SelectSingleNode("id").InnerText);
user.Name = node.SelectSingleNode("name").InnerText;
user.ScreenName = node.SelectSingleNode("screen_name").InnerText;
user.ImageUrl = node.SelectSingleNode("profile_image_url").InnerText;
user.SiteUrl = node.SelectSingleNode("url").InnerText;
user.Location = node.SelectSingleNode("location").InnerText;
user.Description = node.SelectSingleNode("description").InnerText;

users.Add(user);
}

}
}
catch (WebException webExcp)
{
// SNIPPED BY SCOTT
}
}
return users;
}

So far, there's a .NET lib for every Web 2.0 application I've wanted to use. I even banged a .NET Client out for Wesabe last year then did it again in IronRuby.

Enjoy. Which (of the hundreds) did I miss?

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

7 Blogging Statistics Rules - There is Life After Page Views

March 26, '08 Comments [50] Posted in Musings
Sponsored By

A lot of folks spend a lot of time analyzing blog traffic. Josh Bancroft wrote a very good article in January about "Site Statistics I Care About as a Blogger" where he talks about the various and sundry statistics that Google Analytics provides and how you should read them. Ultimately this all comes down to two things:

  • Do you care who reads your blog?

and if so

  • Will you change your behavior given statistics on who reads you blog?

I used to care deeply about my blog, the way one cares about tending a garden. I'd watch it every day and revel in each new visitor. Now, after almost 6 years of pretty active blogging, I now think more about people than pageviews. You can't trust a referrer or a trackback.

Rule #1 of blogging stats: The only way to know if a human is reading your blog is if they are talking with you.

Given that realization, I look at my stats maybe twice a month, and I'm most interested in seeing what posts folks really liked that month. I used to (maybe 3 years ago) look at every referrer and stats daily, but then I realized that my personal litmus test for my blog's success or failure is comments and other folks' blog posts, and nothing else.

I feel like we've (that means me and you, Dear Reader) have a little community here. When you comment, I am happy because I feel more connected to the conversation as this blog is my 3rd place. I blog to be social, not to have a soapbox. I'm even happier when the comments are better and more substantive than the post itself. I would take half the traffic and twice the comments any day. If you're a "lurker," why not join the conversation?

Anyway, some blogs use their stats as a measuring stick (to measure all sorts of thing) and some keep them secret. I was thinking I should just publish mine occasionally, and perhaps others would do the same. You can't trust stats, usually, as on never knows how many bots are visiting their site spidering. I know that Google Analytics and any analytics package worth its salt filters out spiders. DasBlog, for example, doesn't do this, so the statistics you'll get from DasBlog (any many other blogging engines) will be artificially inflated. The same thing happens if you just run a script over your web server logs looking for HTTP GETs.

Rule #2 of blogging stats: HTTP GETs don't equal warm bodies.

I was "tweeting" with Brendan Tomkins of CodeBetter about this and he thought it would foster a sense of openness and give everyone in our tiny slice of the blogosphere an idea of who's out here.

There's a little FeedBurner chicklet up there in my blog that shows a ballpark number of how many subscribers I have. Here's more on how FeedBurner comes up with that number. That number goes up and down based from day to day by 10-20%, depending on such mundane things as whether your computer was on to make the request.

I have only had Google Analytics on since March 3rd so I'm not sure how accurate this data is, but here's the stats since then. There seems to have been some kind of ramping up process, so this is about a 2.5 to 3 week (not a full month) slice, as I'm not sure how to count the ramp-up days.

 image

Notice the regular dips? Those are weekends. The peaks? Mondays. Folks love to read on Mondays.

Here's another rollup:

image

Rule #3 of blogging stats: PageViews don't equal warm bodies.

See the difference between Visits and PageViews? You can't take a number like PageViews and correlate that directly to "# of humans" although you'll see that a lot when folks quote stats.image

Rule #4 of blogging stats: You have a worldwide audience!

(Hi Sri Lanka!)

Folks come from all over!

image

...using lots of different OS's...

image

Rule #5 of blogging stats: If it can browse, someone will visit you with it. 

Not sure what to do with the 2,200 visits by 800x600 people. I have made an effort to make the site mobile friendly though.

image

Rule #6 of blogging stats: People like what they like

This I thought was really interesting - the number of URLs (posts/comments URIs) views vs. number of views, and the top pages for this ~3 week period. The Programmer Themes Gallery is popular this month, as is the tools list and my Outlook GTD post. Also, I can see that folks do enjoy the Weekly Source Code, so I know I'll keep doing that. I can also see that referrals via search sent 94,850 total visits via 64,239 keywords over this period.

It's funny, the posts that I like writing, the deep technical stuff, programming languages stuff, it seems like no one cares about. I think this is the Digg influence. If you post a Gallery or a List or anything post with a Prime Number and the word "Rules" in the title, you'll get traffic. You post smart, compelling content, you need to be wicked smart before folks take note. That said, here's rule #6.5

Rule #6.5 of blogging stats: Blog for you.

You can certainly use these statistics make decisions on what to blog and only blog things that the largest number of people would like, but "meh." Would you really want to do that? I continue to blog about Baby Sign Language and Diabetes and I get no traffic for those topics. Ultimately, I blog for me, and that's why I keep this blog on my own server where the content is my own.

image

I also use FeedBurner, which provides RSS-specific and site specific stats, and it sometimes offers differing stats. This might have to do with how many people browse with Javascript turned off (gasp!) or use an Ad Blocker like IE7Pro or AdBlock for Firefox. FeedBurner has an interesting view that breaks down the details of how many folks subscribe in what reader.

image 

Rule #7 from Mark Twain: There are three kinds of lies: lies, damned lies, and statistics.

Don't trust any of these values. If you've got an engaged audience, they'll comment, blog, talk, chat, twitter, email and generally engage in the conversation. All else is poo.

I've only been using Google Analytics for a few weeks, as you can see, but I think I'll install Microsoft adCenter's Analytics Package side-by-side and do some comparisons and see what kinds of stats I can get out out of it.

As Josh so rightly said, and I'll steal borrow from him, if you ever want to flatter me, just subscribe to my feed (and leave comments!) 

Well, that's all I've got, so Dear Reader, Blog your Stats and let's learn from each other what works.

Related Posts

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

IronPython and the DLR march on

March 22, '08 Comments [12] Posted in ASP.NET | ASP.NET MVC | DLR | Python | Ruby | Silverlight
Sponsored By

I've got a number of emails complaining that folks haven't heard much from the DLR (Dynamic Language Runtime) and things like IronPython and IronRuby.

I think it's due to mostly one thing, the fact that the ASP.NET Futures Page still says July 2007. That's one of the reasons I personally fought to have the ASP.NET MVC not use a Date in its name. It just makes things look, ahem, dated.

I'm working to get that page updated, but I just wanted to make sure folks know that there's lots going on around the DLR. I talked to Mahesh on a video call just yesterday.

There's lots going on and here's some collected resources for you:

Once you've had fun with all that, you might look at John's Dynamic Silverlight in ASP.NET MVC article.

Here's John and Jimmy's talk on Dynamic Silverlight at Mix08:

All other goodness is at http://dynamicsilverlight.net/. Enjoy.

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

The Weekly Source Code 21 - ASP.NET MVC Preview 2 Source Code

March 21, '08 Comments [9] Posted in ASP.NET | ASP.NET MVC | Learning .NET | Programming | Source Code
Sponsored By

And so, Dear Reader, I present to you twenty-first in a infinite number of posts of "The Weekly Source Code." I'm doubling up this week, but the ASP.NET MVC Source was released today and I wanted to share more thoughts. I would also encourage you to check out TWSC 17 on Community ASP.NET MVC code.

Read the Comments

When you're reading source, look for words like "TODO," "HACK," "REVIEW," etc, to find parts of the code that the writers are concerned about.

In the SelectBuilder.cs, there's a comment that says:

// TODO: Should these be HTML encoded or HTML attribute encoded? Need to review all helper methods that call this.
string thisText = HttpUtility.HtmlEncode(listData[key].ToString());
string thisValue = HttpUtility.HtmlEncode(key.ToString());

This is an interesting question. He's asking if they should use System.Web.HttpUtility.HtmlAttributeEncode or HtmlEncode. HTML Attribute Encoding encodes <, " and &.

In ViewUserControl.cs we see these:

public virtual void RenderView(ViewContext viewContext) {
// TODO: Remove this hack. Without it, the browser appears to always load cached output
viewContext.HttpContext.Response.Cache.SetExpires(DateTime.Now);
ViewUserControlContainerPage containerPage = new ViewUserControlContainerPage(this);
containerPage.RenderView(viewContext);
}

This is a tough one also. Chasing caching issues is a huge hassle and consumed at least 10% of my time when I was writing banking software. Even now it feels like there are subtle (and not-so-subtle) differences between IE and Firefox. Seems like Firefox really caches aggressively.

There's a few marked "REVIEW" like:

    // REVIEW: Should we make this public?
internal interface IBuildManager {
object CreateInstanceFromVirtualPath(string virtualPath, Type requiredBaseType);
ICollection GetReferencedAssemblies();
}

And this one, which is kind of funny. The property IsReusable in an HttpHandler indicates whether or not an instance has state and as such, should not be reused by ASP.NET property. If you write an HttpHandler and it has no state, just a ProcessRequest, you can "reuse" it which should result in a small perf gain.

protected virtual bool IsReusable {
get {
// REVIEW: What's this?
return false;
}
}

Here's one about overloads:

//REVIEW: Should we have an overload that takes Uri?
[SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings",
Justification = "As the return value will used only for rendering, string return value is more appropriate.")]
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
Justification = "Needs to take same parameters as HttpUtility.UrlEncode()")]
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic",
Justification = "For consistency, all helpers are instance methods.")]
public string Encode(string url) {
return HttpUtility.UrlEncode(url);
}

We've all written comments like these. The trick is to make sure you've included all your key words in Visual Studio so that all your comments will show up in the Task List and can be dealt with before you ship.

Check out SuppressMessage

Microsoft uses CodeAnalysis a lot and you should too. However, sometimes CodeAnalysis offers suggestions that are wrong or not really appropriate and you'll want to suppress those.

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "There is already a ViewData property and it has a slightly different meaning.")]
protected internal virtual void SetViewData(object viewData) {
_viewData = viewData;
}

Looking for references to SuppressMessage is a good way to find out where unwavering "purity" analytics fall down and pragmatism should win the day. That said, it never hurts to reevaluate these occasionally as opportunities for refactoring.

The most interesting aspect is the Justification attribute which is actual prose written by the developers. For example, this is the contents of GlobalSuppressions.cs:

[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Web.Mvc.TempDataDictionary.#System.Collections.Generic.ICollection`1<system.collections.generic.keyvaluepair  `2>)",
Justification = "There are no defined scenarios for wanting to derive from this class, but we don't want to prevent it either.")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Web.Mvc.TempDataDictionary.#System.Collections.Generic.ICollection`1<system.collections.generic.keyvaluepair `2>[],System.Int32)",
Justification = "There are no defined scenarios for wanting to derive from this class, but we don't want to prevent it either.")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "System.Web.Mvc.TempDataDictionary.#System.Collections.Generic.ICollection`1<system.collections.generic.keyvaluepair `2>>.IsReadOnly",
Justification = "There are no defined scenarios for wanting to derive from this class, but we don't want to prevent it either.")]

Here's a good example of a justification:

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "The return value is not a regular URL since it may contain ~/ ASP.NET-specific characters")]
public static string SubmitImage(this HtmlHelper helper, string htmlName, string imageRelativeUrl) {
return SubmitImage(helper, htmlName, imageRelativeUrl, null);
}

Code analysis is warning that there's a string parameter with the name "Url", but the justification is valid: "The value is not a regular URL since it may contain ~/ ASP.NET-specific characters"

Look to Utils

As I've said before, whenever I start reading code, I look for things marked "Util." These tell us a few things. Things named Util show the "underbelly" of code and point out where things could either be better factored, either in the thing your reading, or in the larger Framework whatever your reading lives in.

In ASP.NET MVC's project there's a Util folder and a Pair.cs file, so let's check it out.

//------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------

namespace System.Web.Util {
using System;

// Generic Pair class. Overrides Equals() and GetHashCode(), so it can be used as a dictionary key.
internal sealed class Pair {
private readonly TFirst _first;
private readonly TSecond _second;

public Pair(TFirst first, TSecond second) {
_first = first;
_second = second;
}

public TFirst First {
get {
return _first;
}
}

public TSecond Second {
get {
return _second;
}
}

public override bool Equals(object obj) {
if (obj == this) {
return true;
}

Pair other = obj as Pair;
return (other != null) &&
(((other._first == null) && (_first == null)) ||
((other._first != null) && other._first.Equals(_first))) &&
(((other._second == null) && (_second == null)) ||
((other._second != null) && other._second.Equals(_second)));
}

public override int GetHashCode() {
int a = (_first == null) ? 0 : _first.GetHashCode();
int b = (_second == null) ? 0 : _second.GetHashCode();
return CombineHashCodes(a, b);
}

// Copied from ndp\fx\src\xsp\System\Web\Util\HashCodeCombiner.cs
private static int CombineHashCodes(int h1, int h2) {
return ((h1 << 5) + h1) ^ h2;
}
}
}

This is a simple but clever class that uses generics to make a Pair of any two types. The interesting part is the CombineHashCodes method that takes the hash codes from each object and combines them in a way that makes that pair's hashcode unique enough for use in a Hashtable later.

The Pair class is used to create a combined object inside the TempDataDictionary class like this:

private Pair<Dictionary<string , object>, HashSet<string>> _sessionData;

...where the Key is the actual TempData storage dictionary, and the value is the list of keys that were modified during one request so that they might survive to the next.

There's lot more to learn from reading this code, and it's going to be fun to watch it grow, change and improve!

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

ASP.NET MVC Source Code Available

March 21, '08 Comments [5] Posted in ASP.NET | ASP.NET MVC | Programming | Source Code
Sponsored By

image My boss's boss has blogged about what's been going on this week: The ASP.NET MVC Source is up on CodePlex at http://www.codeplex.com/aspnet.

You can download, read and compile it now.

The goal is to start releasing drops really often. If you're into it, then watch the source code tab, if not, that's cool too, you can wait until it releases later.

You can enter bugs in the issue tracker or complain in the forums and watch the roadmap as it evolves. You can see how to compile it (unzip and build) as well.

Related Links

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

Hanselminutes Podcast 104 - Dave Laribee on ALT.NET

March 21, '08 Comments [6] Posted in ASP.NET MVC | Learning .NET | Nant | NCover | NUnit | Podcast
Sponsored By

RWS2-Big My one-hundred-and-fourth podcast is up. In this episode I talk to the always thought-provoking David Laribee (blog) who coined the term ALT.NET just last year. It's turned into a Open Spaces Conference and continues to challenge the status quo, reminding .NET developers of the importance of being agile and enabling processes for continuous improvement.

What does it mean to be to be ALT.NET? In short it signifies:

  1. You’re the type of developer who uses what works while keeping an eye out for a better way.
  2. You reach outside the mainstream to adopt the best of any community: Open Source, Agile, Java, Ruby, etc.
  3. You’re not content with the status quo. Things can always be better expressed, more elegant and simple, more mutable, higher quality, etc.
  4. You know tools are great, but they only take you so far. It’s the principles and knowledge that really matter. The best tools are those that embed the knowledge and encourage the principles 

Subscribe: Subscribe to Hanselminutes Subscribe to my Podcast in iTunes

If you have trouble downloading, or your download is slow, do try the torrent with µtorrent or another BitTorrent Downloader.

Do also remember the complete archives are always up and they have PDF Transcripts, a little known feature that show up a few weeks after each show.

Telerik is our sponsor for this show.

Check out their UI Suite of controls for ASP.NET. It's very hardcore stuff. One of the things I appreciate about Telerik is their commitment to completeness. For example, they have a page about their Right-to-Left support while some vendors have zero support, or don't bother testing. They also are committed to XHTML compliance and publish their roadmap. It's nice when your controls vendor is very transparent.

As I've said before this show comes to you with the audio expertise and stewardship of Carl Franklin. The name comes from Travis Illig, but the goal of the show is simple. Avoid wasting the listener's time. (and make the commute less boring)

Enjoy. Who knows what'll happen in the next show?

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

Squeezing the most out of IIS7 Media Bit Rate Throttling

March 21, '08 Comments [5] Posted in HttpHandler | HttpModule | IIS | Podcast
Sponsored By

image ScottGu blogged about the IIS 7.0 Bit Rate Throttling Module being released. Since IIS 7.0 is totally modular (finally!) I think you'll see lots of increasingly useful modules coming out from the team. I know a couple of cool people over there so I get a little insight into what's coming soon.

Since I'm into podcasting some, I started bugging a PM in the IIS team about the Bit Rate stuff in order to dig deeper into what's going on and how I can exploit it. The next step will be to convince Carl, my producer, that we need to get on this stuff.

His Gu-ness has a better description of Bit Rate Throttling that I could write, so here's the meat of it:

Progressive Download Scenario

In a progressive download scenario a client (like Flash or Silverlight) downloads a video directly off of a web-server, and begins playing it once enough video is downloaded for it to play smoothly.

The benefit of using a progressive download approach is that it is super easy to setup on a web-server. Just copy/ftp a video up to a web-server, obtain a URL to it, and you can wire it up to a video client player. It doesn't require any custom web-server configuration, nor require a streaming server to be installed, in order to enable.

The downside of using a progressive video download approach, though, is that web-servers are by default designed to download files as fast as possible.  So when a user clicks to watch a video on your site, your web-server will attempt to transmit it to the client as fast as possible.  This is fine if the user decides to watch the entire video.  But if the user stops watching the content half way through the video (or navigates to a different page), you will have downloaded a bunch of video content that will never be watched.

If the remaining un-watched video content is several megabytes (or even tens of megabytes) in size, you will end up over time spending a lot of money on bandwidth that is not benefiting your business/site at all....

That's the problem this module tries to solve. If you think it's not a problem, talk to me or Carl. It costs a metric buttload to pay for bandwidth of my podcast and it's a harder problem to solve than you think. (Yes, every show has bittorrent as an option, but few Feed Readers use it). A surprising number of folks visit the site and just click on a show and start listening in whatever Media Player they dig.

If someone starts streaming the first 20 seconds of a 45 minute show, decides they don't like it and stops, we may have to pay for the whole 45 minutes as it might already have been downloaded! This is also good for screencasts.

The media throttling stuff looks at the file you're downloading, "bursts" the first chunk to make the "buffering" part of your listening experience as fast as possible, then it throttles the bandwidth from that point on, making sure it doesn't have to buffer again, but still saving bandwidth.

There's some technical details over here that I care about like (emphasis mine) and more technical details here:

"It's also worth noting that the throttler uses a high-performance asyncronous loop to push the data out, without tying up server threads for what can be a very long operation. For responses coming from files (like most large video files), it also does not need to read the content's of the file being sent into memory, instead just instructing http.sys to send portions of the file out to the client at a time.  Because of this, it won't significantly affect your memory usage. While this mechanism is not as efficient as http.sys's own site-wide bandwidth throttling (which cannot be used to do what we are trying to accomplish here), it is pretty much as lean as it can be."

So, I'm going to give this a try on my local machine. I'll start with a recent WMA of the show and put it in a folder under IIS.

image

Notice that this show is a WMA encoded at 32kps (for low-fi/low-bandwidth listeners) and is in http://mymachine/show. I turned on Bit Rate Throttling from the IIS Management Console.

You can set the throttle rate as a percentage of the encoded rate. I'm leaving it as 100%, so that's 100% of 32kbps after the initial burst of 20 seconds.

image

ASIDE: One of the things that is way better in IIS7 than 6 is dealing with permissions and "blocking issues." You know, when you setup a folder, configure the VDir and you get something like "permission denied" then you spend 20 minutes trying to figure out why you can't serve a freaking text file out of a folder? IIS7 has a "Test Settings" dialog like this that just saved me those 20 minutes.

image

Ok, back to business. When I hit this file I get the quick burst, then throttling hits to slow it down to a value above, but close to, the encoded rate.

image

If I try the hi-fi version of the WMA, you can see the bitrate is higher, so the throttling is smart enough to slow the bandwidth but it's always higher than the encoded rate so there's no skipping.

image

Here's what the burst followed by the throttling looks like charted.

image

Remember that this is just a stock IIS Web Server plus this new module, so we're not running Media Services or a Streaming Server or whatever.

Extending Bit Rate Throttling with Custom Code

I thought this was cool and everything, but I immediately brought up the question that some of my users download directly via RSS Readers, while others click the "play now" button and stream. I don't want to throttle the bandwidth for folks who are downloading, just those who stream. However, I'm not running a streaming server (meaning, that everything is over http://, not some mms:// funky port) so I need to differentiate between the two.

I figured there were a couple ways to handle this. I could make different VDirs in IIS mapped to the same files and have BRT (Bit Rate Throttling) turned off on one and on for the other. It'd work, but meh.

Or, I could add Windows-Media-Player/11.0.5721.5145 the end of the URL and turn BRT off for those using a custom coded IIS Module.

How to I write a module? I could make a bin folder and build a DLL, etc, but I could also just make an App_Code folder, apply the appropriate permissions...

image

...and put the source for my custom module in it. I actually wrote it in Notepad.

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO;

namespace Hanselmodules
{
    public class CustomBRTModule : IHttpModule
    {
        public void Init(HttpApplication application)
        {
            application.BeginRequest += new EventHandler(OnBeginRequest);
        }

        public void Dispose(){}

        public void OnBeginRequest(Object source, EventArgs e)
        {
            HttpApplication app = (HttpApplication)source;
            HttpContext context = app.Context;

            if (context.Request.QueryString["download"] == "true") //make these fast
            {
                context.Request.ServerVariables.Set("ResponseThrottler-InitialSendSize", "60000");
                context.Request.ServerVariables.Set("ResponseThrottler-Rate", "6000");
            }
        }
    }
}

Basically, I just check for ?download=true on my URL and set my initial send size and rate to really big numbers. (Certainly they could be bigger, come from config, or be calculated by me.)

From the Modules section of the IIS Manager I hit Add Module and my new module shows up. I name it and select it and it appears in the web.config in my /show folder.

 image

After installing this module if I hit: http://mymachine/show/foo.wma I get appropriate throttling, and if I hit http://mymachine/show/foo.wma?download=true I don't. Below you can see screenshots of one download finished at 9megs a second while the other is throttled at 28K/s.

image

Now, this is just one example. I might want to do something cool with JavaScript that appends the ?download if a person clicks a button, or I might want to "sniff" for browser User-Agents versus Media Players like "Windows-Media-Player/11.0.5721.5145" or iTunes/Zune or the like. It's up to me, as writing the IIS7 modules is easy.

You could even use BRT and techniques like this to throttle bandwidth to data (non-media) files, and give different chunks of bandwidth to different levels of users. My module could have checked if it was a paid user and give them preferred download speeds. Pretty sweet. Now, off to talk to Carl! :)

Related Links:

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

Twitter: The Uselessfulness of Micro-blogging

March 19, '08 Comments [13] Posted in Musings
Sponsored By

twitter20070405 I haven't used Instant Messaging for anything significant in months. Why?

Last May (!) I used the Twitter micro-blogging service and made a post every time I managed my Diabetes. It ended up being a pretty cool thing and even was on the evening News in San Francisco.

The rise of blogs brought conversations on the 'net more out in the open. Blogging enabled conversation via essay, but as blogs have matured, posts have gotten longer and longer and threads more difficult to follow. Now, most posts are jumping off points for the more interesting conversations that inevitably move to the comments.

Public debates and conversations still exist on Mailing Lists, IRC, and USENET. Private instant messaging is about connecting and conversing but is usually one to one, which certainly doesn't conserve my keystrokes. However, these conversations miss three useful things.

  • Constraints
    • Everyone uses a different mail client or USENET client and formatting and replies make things sloppy at best.
    • Some folks comment inline, other quote, still others copy-paste, all of which makes it hard to follow.
  • Permalinks
    • Yes, aggregators like Google Groups can make Permalinks (URLs) to conversations, but it's still clunky at best.
    • IRC conversations have no permalinks and virtually no searchable logs.
  • API
    • The APIs for posting and reading to and from IRC and USENET are not easy to implement (although, they are very well understood, you can't bang a client out in an hour).

Twitter offers/imposes all three. You can "tweet" a max of 140 characters. Every tweet can be linked directly to and lives forever. There's a clear API. Any one or all of these things may seem lame or a deal-breaker to you, but these very things are what makes it useful for public conversation and sharing.

Twitter as Conversation Starter

Twitter is just one of the Microblogging services out there, but it's got a nice RESTesque API that has encouraged a number of a cool Twitter Clients like Witty, Twhirl, TinyTwitter (Windows Mobile) and others, as well as a nice mobile site at http://m.twitter.com. It also supports SMS so you can send and received Twitter messages with no UI or client at all.

Twitter's API also has spawned statistics sights like Twitterholic and Tweeterboard as well as fun things like Twaiku's (Twitter Haiku's) and Twoosh, when you use exactly the maximum of 140 characters in your "Tweets."

I'm finding that I use Twitter more to chat with folks and interact with the community. There's aspects of presence, as sometimes folks will tweet "heading downtown" and I've used this to have surprise meet-ups and lunches when I just happen to cross paths with someone.

One of the misconceptions about Twitter is that I (a user) need to permissively "follow" you (another user) in order for us to talk. However, one setting change means I'll see any tweet that includes my username preceded by an @ sign. So, the way you respond in Twitter is like this: "@shanselman you rock" and I'll see it.

image

One thing to point out (warn you) about Twitter is that tweets tend to be more sarcastic, silly, non-technical, whatever than on a blog. It's important to remember that what you say on Twitter is public, indexable, and arguably permanent, which is both a good thing and a bad thing, but be aware - The Internet Remembers.

Twitter as Message Bus

There's a number of examples of Twitter being used as a message bus. For example, you can create a Twitter user as a "bot" to be used to send an receive information via an API. One such user is "commuter" that supports the http://commuterfeed.com/ website.

If I "follow" @commuter I can send traffic updates to it like this "@commuter PDX Traffic on I5" and it'll show up on the Commuter Feed.

Twitter for News

I've been getting the majority of my news lately by watching/following the major news outlets that have adopted Twitter, like NPR News, CNN and BBC Tech. Political activists like Dave Winer have been providing commentary via Twitter, and even candidates like Barack Obama have a Twitter presence. Obama has over 16,500 followers at growing on Twitter! Here's a list of the "Twitterati" from http://twitter.alltop.com/.

Twitter as Conference/Subject Tagger

If you're at a conference or in a particular special interest group, you can include a "hashtag" in your tweet like "#mix08." Many folks did this while at Mix08 in Vegas this year and via another service at Hashtags.org a feed was created that allowed not only the folks on the ground to stay in touch, but also for others not in Vegas to follow the conference.

All of these things and more have made (so far) Twitter a really fun, dynamic, and ultimately enriching part of my day.

Feel free to go sign up for Twitter yourself. I'm http://www.twitter.com/shanselman and once you've signed up you can "follow me" and I can follow you and we'll all join the conversation. I encourage you to find a Twitter Client that works for you and give it a try.

How do you find folks you know on Twitter?

You can find folks to follow by letting Twitter check your email contacts, but I have found that just by picking a few folks to follow, then watching who they talk to has been a great way to keep the signal to noise ratio high.

I don't know how long this will last, but I'm finding Twitter to be a nice, fresh way to keep up with friends and community in a 3rd place separate from USENET, Mailing Lists, IRC and IM.

Related Links

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

The Weekly Source Code 20 - A Web Framework for Every Language

March 19, '08 Comments [15] Posted in Javascript | PHP | Programming | Ruby | Source Code
Sponsored By


vt102

We just keep oscillating back and forth between thin clients and chubby clients. We started with basic terminals receiving text from the server and displaying it, then added control codes and more smarts until we got into things like VT102 and beyond. We pushed all the User Interface over to the client for rendering.

Now that the Web is squarely here to stay, we've got islands of activeness in the form of browser plugins like Flash and Silverlight, some of which are cross platform and some less-so, but for now my quad-processor machine spends a lot of time either:

  • Waiting for markup to show up
  • Rendering markup

There's a thousand different ways to generate your UIs and send them down to the browser/client for rendering. Turns out there are as many ways as there are languages. If you've got a programming language, there's a web framework for it.

I don't know why this surprises me. Folks love their programming language, whatever it is, and it makes sense that the "ultimate" proof of their language's awesomeness would be the "ultimate web framework."

That said, it still seems funny to me that the greatest (er, most overtly visible) example of a language's superiority is how well it works as a Web Framework angle-bracket generator.

For example, Arc is Paul Graham's new LISP dialect that a number of people are talking about (with varying degrees of enthusiasm).  A tutorial on Arc is available here and there's an "Arc Challenge" being discussed here as folks try to this slightly more complex Hello World example:

"First generate a page with an input field and a submit button. If the user clicks on submit, he gets a second page with a link saying "click here." If he clicks on that, he gets a third page saying "you said: ..." where ... was whatever he put in the input field. This has to happen without the value being passed in the url; it should not be possible to change the behavior of the third page by editing the url in the second."

In Arc/LISP it looks like this:

  (defop said req
    (aform [w/link (pr "you said: " (arg _ "foo"))
             (pr "click here")]
      (input "foo")
      (submit)))

It's pretty terse to look at if you're used to doing things in more conventional languages. There's a lot of fun solutions like this entirely client-side one in JQuery:

$('body').append('<input id = "myInput" /><input type = "submit" />')
    .find('input[@type=submit]').click(function() {
       val = $('#myInput').val();
       $('body').html('<a href = '#'>click here</a>').find('a').click(function() {
          $('body').html('You said: ' + val);
       });
    });

Other examples include:

#!/usr/bin/env ruby
  require "ramaze"
  class MainController < Ramaze::Controller
    def index
      if f = session['foo'] then "you said #{f}"
      elsif session['foo'] = request['foo'] then A("click Here", :href => '/')
      else '<form><input name="foo" /><input type="submit"></form>'
      end
    end
  end
  Ramaze.start :port => 7001
  __END__ 

Then Rails:

def said
    if request.method == :post
      session[:said] = params[:said]
      render :action => "clickhere"
    else
      render :action => "result" if session[:said]
    end
  end

  default template said.rhtml:
  <% form_tag do %><%= text_field_tag "said", "" %><%= submit_tag %><% end %>

  clickhere.rhtml:
  <%= link_to "click here", "" %>
  
  result.rhtml:
  You said <%= session[:said] %>
| something |	
something := self request: 'Say something'.	
self inform: 'Click here'.	
self inform: something
serveAs "said" $ hasIndex $ \x -> "click me" `linksTo` (text ("You said " ++ x))
<%@ Page Language="C#" ClassName="WebApplication1._Default" %>
  <script runat="server">
    // C# and ASP.NET
    protected void SubmitButton_Click(object sender, EventArgs e)
    {
        MultiView1.ActiveViewIndex = 1;
    }
    protected void ClickHereButton_Click(object sender, EventArgs e)
    {
        SaidLabel.Text = string.Concat("You said: ", SayTextBox.Text);
        MultiView1.ActiveViewIndex = 2;
    }
  </script>

  <html>
  <head runat="server">
    <title></title>
  </head>
  <body>
    <form id="form1" runat="server">
      <asp:MultiView ID="MultiView1" runat="server" ActiveViewIndex="0">
        <asp:View runat="server">
          <asp:TextBox ID="SayTextBox" runat="server" />
          <asp:Button ID="SubmitButton" runat="server" Text="Submit" OnClick="SubmitButton_Click" />
        </asp:View>
        <asp:View runat="server">
          <asp:LinkButton ID="ClickHereButton" runat="server" Text="Click Here" OnClick="ClickHereButton_Click" />
        </asp:View>
        <asp:View runat="server">
          <asp:Label ID="SaidLabel" runat="server" />
        </asp:View>
      </asp:MultiView>
    </form>
  </body>
  </html>

Shorter but not-typical ASP.NET:

Shorter but non-idiomatic C#/ASP.NET:

    <%@ Page Language="C#" %>
    <html>
    <head>
    <title>Said</title>
    </head>
    <body>
        <form id="form" runat="server">
            <% if (!IsPostBack) { %>
                <input name="foo" />
                <input type="submit" />
            <% } else if (Request.Form["foo"] != null) {
                Session["foo"] = Request.Form["foo"]; %>
                <a href="javascript:form.submit()">click here</a>
            <% } else { %>
                you said: <%=Session["foo"]%>
            <% } %>
        </form>
     </body>
    </html>

There are so many ways to generate the same result. Big thanks to Ted Glaza for his indirect help on this post.

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

Getting LINQ to SQL and LINQ to Entities to use NOLOCK

March 18, '08 Comments [23] Posted in LINQ
Sponsored By

I was visiting a local company where a bunch of friends work and they asked how to get LINQ to SQL to use NOLOCK. They were explicitly asking how to get the SQL generated by LINQ to SQL to add the NOLOCK hints to the end.

However, with NOLOCK (even though "everyone" has used it at some point or another) is generally considered a last resort. Queries that use NOLOCK aren't guaranteed to return correct results or technically, return any results at all.  

SQL 2005 has snapshot-based isolation levels that prevent readers from blocking writers or writers from blocking readers without allowing dirty reads.

Now, I have said that NOLOCK has served me personally very well in the past on systems of some size, but I hear what folks who say no to NOLOCK are saying. It certainly depends on one's definition of "correct results." ;)

There's three ways to get the behavior your want. Using TransactionScope is the recommended way to affect the transaction options associated with the commands generated by either LINQ to SQL or LINQ to Entities.

LINQ to SQL also supports explicitly setting the transaction on the context, so you could get the connection from the context, open it, start a transaction, and set it on the context. This can be desirable if you think SQL 2005 is promoting transactions too often, but the preferred method is TransactionScope.

ProductsNewViewData viewData = new ProductsNewViewData();
using (var t = new TransactionScope(TransactionScopeOption.Required,
    new TransactionOptions { 
IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted
})) { viewData.Suppliers = northwind.Suppliers.ToList(); viewData.Categories = northwind.Categories.ToList(); }

Here's an example where I used it in some recent code. This TransactionScope could be hidden (layered away) in your DAL (Data Access Layer) or in your Data Context directly if you wanted it to be neater.

A second way is that you can still create and call Stored Procedures (sprocs) from LINQ to SQL and those sprocs could include NOLOCK, TransactionScope is a better choice for LINQ to SQL or LINQ to Entity generated SQL if you feel that your query doesn't need to lock down the table(s) it's reading from.

Note that you'll want to be aware of which statement in your LINQ to SQL actually starts talking to the database. You can setup a query ahead of time and it won't be executed, for example, until someone calls ToList() or the like. It's at this point you'll want to wrap it in the using(TransactionScope){}.

Another third way you could set it at a DataContext level (which, to be clear, would affect every generated LINQ to SQL query executed on that context) would be to execute the command:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED

...using the connection available inside your DataContext.

Thanks to David Browne and Michael Pizzo for their help on this!

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

List of .NET Dependency Injection Containers (IOC)

March 14, '08 Comments [29] Posted in IOC | Learning .NET | Programming
Sponsored By

I'm trying to expand my mind around dependency injection in .NET (beyond the two frameworks I've personally used) and an starting to put together a list of .NET Dependency Injection Containers and IOC resources.

Here's what I've got so far. What am I missing?

What projects have I forgotten? Thanks!

Related Links

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

I'm Just a Caveman - The Hanselman Corollary to the Clarke/Wheeler Laws

March 13, '08 Comments [21] Posted in ASP.NET | ASP.NET MVC | DevCenter | Learning .NET | Programming
Sponsored By

cavemen-unfrozen-lawyer-web Any problem in computer science can be solved with one additional layer of indirection. But that usually will create another problem. - David Wheeler

Any sufficiently advanced technology is indistinguishable from magic. - Arthur C. Clarke.

These are two classic truisms. Recently while staring at some code trying to figure out what the heck was going on, I realized the obvious.

One additional layer of indirection is indistinguishable from magic. - Scott Hanselman, this morning in a rare moment of clarity while hopped up on Diet Coke.

In recent talk at Mix on ASP.NET MVC (there's video there also) I mentioned that a certain line of code was magic:

      public void Update(int id)
      {
         try
         {
            viewData.Product = db.Products.Single(p => p.ProductID == id);
            //MAGIC STARTS HERE
Binding.UpdateFrom(viewData.Product, Request.Form);
//END MAGIC db.SubmitChanges(); RedirectToAction("List"); } catch (InvalidOperationException err) { viewData.ErrorMessage = err.Message; RenderView("edit", viewData); } }

Why did it feel like magic? A few reasons.

  • It does a LOT. It takes all the values from a Form POST and lines them up with the public Properties in an object instance. This is done in the context of a Form POST to a Controller Action in ASP.NET MVC.
  • It isn't named well. Update is a verb, so that's cool, but the "From" makes me feel inverted.
  • The parameters are (possibly) in the wrong order. Given the name, I'd have expected UpdateFrom(Form, Product), but even then it doesn't feel write.

All of this adds up to an impedance mismatch, IMHO. It's too confusing and I'm just a caveman (video). As such, I declare it as black magic.

This magic was brought up to the team (I think I remember hitting someone, there may have been some swearing. ;) ) and Rob changed it in a number of good ways.

  • It's discoverable. He hung it off of the Request where you can actually stumble upon in.
  • It's named directly. Rather than the inverted UpdateFrom, it is now DeserializeTo which reads better left to right as in Request.DeserializeTo(product).
  • It's simpler. Because it's hanging off the object that was previously a parameter, it has one less parameter and again, reads nicely left to right.
//FEELS LESS MAGICAL 
Request.DeserializeTo(viewData.Product);

Jeff Moser's (see I told you I'd mention him twice) fine post talks about how cognitive "chunks" that represent solutions to problems can be more easily stored in our brains if they flow. Scott Bellware has called this concept "solubility." Code that uses frameworks with some balanced between focus on aesthetic and usability is more easily grokked.

I don't know if this is the final method, it's probably not, but I really enjoy discussions like this that aim to make things less like magic and more obvious and discoverable.

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

The Weekly Source Code 19 - LINQ and More What, Less How

March 13, '08 Comments [19] Posted in Learning .NET | LINQ | Microsoft | Programming | Source Code
Sponsored By

Dear Reader, I present to you nineteenth in a infinite number of posts of "The Weekly Source Code."

At Mix, Clint Rutkas and I were messing around writing a plugin model for one of his apps. We were prototyping and I typed up this typical-looking plugin style code:

string[] filesToTest = Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plugins"), "*.dll");
foreach (string file in filesToTest)
{
    Assembly a = Assembly.LoadFrom(file);
    foreach (Type t in a.GetTypes())
    {
        if (!t.IsAbstract && t.BaseType == typeof(MyBaseClass))
        {
            myListOfInstances.Add((MyBaseClass)Activator.CreateInstance(t, credentials));
        }
    }
}

It's pretty straightforward and has little (read: no) error handling. It spins through the DLLs in the /plugins folder, loads them each (this should be more explicit rather than a blanket load of all dlls in a folder), then looks for any types that are derived from MyBaseClass that aren't abstract, then instantiates them and puts them in a list.

I'm going to quote Jeff Moser's very good blog post "What does it take to be a grandmaster" in both this post and the next one I write. It's that good. Seriously, go read it now, I'll wait here.

Jeff invokes Anders Heilsberg (Father of C# and LINQ) and:

"Anders' recent statement that future versions of C# will be about getting the programmer to declare "more of the what, and less of the how" with respect to how a result gets computed. A side effect of this is that your "chunks" tend to be more efficient for the runtime, and more importantly, your brain."

I'm going to repeat part I bolded. I like Programming Languages that allow me to declare "more of the what, and less of the how."  This how we tried to design the system at my last job and I automatically mentally migrate towards systems that encourage this kind of thinking.

So, back to the foreach loops above. My good friend (and fellow baby sign language fan) Kzu came by while Clint and I were working and suggested we turn this increasingly complex procedure into a LINQ query.

After a few tries, here's what we came up with.

myListOfInstances =
    (from file in Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plugins"), "*.dll")
     let a = Assembly.LoadFrom(file)
     from t in a.GetTypes()
     where !t.IsAbstract && t.BaseType == typeof(MyBaseClass) 
     select (MyBaseClass)Activator.CreateInstance(t, credentials))
    .ToList();

This is all one line, with some whitespace for readability. It says the same thing as the first chunk of code, but for some minds, it might be easier to read. This isn't a very good example of LINQ shining, from my point of view even though I like reading it. Certainly there's nowhere (I can see) for me to put error handling code that catches a bad load or bad cast in this example. There are, however, a LOT of places within that single line that one could set a breakpoint.

A better example would be like the one Jeff uses:

var primes = new int[] { 2,3,5,7,11,13,17,19,13,29,31,37,41 };
var primeSum = 0;
for (int i = 0; i < primes.Length; i++)
{
primeSum += primes[i];
}

...or even...

var primes = new int[] { 2,3,5,7,11,13,17,19,13,29,31,37,41 };
var primeSum = 0;
foreach (int i in primes)
{
  primeSum += i;
}

...could be more easily written and read like:

var primes = new int[] { 2,3,5,7,11,13,17,19,13,29,31,37,41 };
var primeSum = primes.Sum();

where .Sum() is part of LINQ as Arrays are IEnumerable.

I totally recommend you go get the free, no-installer LINQPad that comes with 200 examples from C# in a Nutshell.

Here are a few examples that I think really typify pretty code; certainly an improvement over the standard procedural code I'd usually write:

var names = new[] { "Tom", "Dick", "Harry" }.Where(n => n.Length >= 4);

Here's onen that does quite a few things to a list in a fairly clean syntax:

string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };

var results =
     (
       from n in names
       where n.Contains("a")  // Filter elements
       orderby n.Length       // Sort elements
       select n.ToUpper()     // Translate each element (project)
     ).ToList();       	

What LINQ-style queries have you written that feel more what than how?

Related, ahem, LINQS

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

ASP.NET MVC Cheesy Northwind Sample Code

March 12, '08 Comments [19] Posted in ASP.NET | ASP.NET MVC | Programming | Speaking
Sponsored By

image A number of folks wanted the code from my talk at Mix, specifically the "complete application" example with CRUD (Create, Read, Update, Delete) so here it is.

It's nothing pretty to look at, but it makes a number of points with ASP.NET MVC:

  • It uses the Default Routes to do everything.
  • It uses a ViewUserControl to avoid duplication in a Form.
  • It includes the PagedList<T> that makes Linq to SQL easier in grids.
  • It's insanely simple. There's like nothing there and it works well.
  • It uses lambdas to generate URLs like:
    • Html.ActionLink<ProductController>(c => c.Edit(p.ProductID),"Edit")
    • Which says "if someone were to call Edit with ProductID as a parameter, what would the URL look like?" The system will then use the lambda along with the already declared routes and figure out the best URL. This is very DRY and lets us change our URLs in one place and have those changes reflected everywhere.

If you want to run it you need:

I known it's cheesy to do demos with Northwind, and we're actively working on more real world, more interesting samples. That said, I'd much rather show off your code, so if you do something cool and Open Source with MVC, let me know. BTW, I've added an ASP.NET MVC category to this blog.

UPDATE #1: PhilHa updated Brad Abrams older (and much more comprehensive) Northwind code to ASP.NET MVC Preview 2, so you can download that much better code below as well.

UPDATE #2: Looks like ASP.NET MVC Preview 2 runs pretty well on Mono. Anyone want to get Northwind up over there as well?

Technorati Tags: ,,

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

Hanselminutes Podcast 103 - Quetzal Bradley on Testing after Unit Tests and the Myth of Code Coverage

March 8, '08 Comments [3] Posted in Podcast
Sponsored By

oopsMy one-hundred-and-third podcast is up. On the recommendation of Chris Sells, I gave Quetzal (ket-zal) Bradley a call to talk about Code Coverage. Quetzal is a Developer in the Connected Systems Division and has some interesting ideas on testing after unit testing and code coverage. Think 100% Code Coverage is enough?

Subscribe: Subscribe to Hanselminutes Subscribe to my Podcast in iTunes

If you have trouble downloading, or your download is slow, do try the torrent with µtorrent or another BitTorrent Downloader.

Do also remember the complete archives are always up and they have PDF Transcripts, a little known feature that show up a few weeks after each show.

Telerik is our sponsor for this show.

Check out their UI Suite of controls for ASP.NET. It's very hardcore stuff. One of the things I appreciate about Telerik is their commitment to completeness. For example, they have a page about their Right-to-Left support while some vendors have zero support, or don't bother testing. They also are committed to XHTML compliance and publish their roadmap. It's nice when your controls vendor is very transparent.

As I've said before this show comes to you with the audio expertise and stewardship of Carl Franklin. The name comes from Travis Illig, but the goal of the show is simple. Avoid wasting the listener's time. (and make the commute less boring)

Enjoy. Who knows what'll happen in the next show?

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

ASP.NET MVC Session at Mix08, TDD and MvcMockHelpers

March 8, '08 Comments [38] Posted in ASP.NET | ASP.NET MVC | Mix | Programming | Source Code | Speaking
Sponsored By

NOTE: This post is based on an older preview version of ASP.NET MVC and details have very likely CHANGED. Go to http://www.asp.net/mvc or http://www.asp.net/forums for updated details and the latest version of ASP.NET MVC.

image All the sessions from Mix are up on http://sessions.visitmix.com/ for your viewing pleasure. I had a total blast giving the ASP.NET MVC Talk. The energy was really good and the crowd (of around 600, I hear) was really cool.

You can download the MVC talk in these formats:

I think the sound is a little quiet, so I had to turn it up some. It's better turned up a bit so you can hear the interaction with the crowd.

Here's some of the code from the talk you might be interested in. I'll post the rest very soon.

MvcMockHelpers

The first are the MVCMockHelpers used in the Test Driven Development part of the talk, and also in the 4th ASP.NET MVC Testing Video up at www.asp.net/mvc.

NOTE AND DISCLAIMER: This is just a little chunks of helper methods, and I happened to use Rhino Mocks, an Open Source Mocking Framework, the talk at Mix. At my last company I introduced TypeMock and we bought it and lately I've been digging on Moq also. I'm not qualified yet to have a dogmatic opinion about which one is better, because they all do similar things. Use the one that makes you happy. I hope to see folks (that's YOU Dear Reader) repost and rewrite these helpers (and better, more complete ones) using all the different mocking tools. Don't consider this to be any kind of "stamp of approval" for one mocking framework over another. Cool?

Anyway, here's the mocking stuff I used in the demo. This is similar to the stuff PhilHa did last year but slightly more complete. Still, this is just the beginning. We'll be hopefully releasing ASP.NET MVC bits on CodePlex maybe monthly. The goal is to release early and often. Eilon and the team have a lot more planned around testing, so remember, this is Preview 2 not Preview 18.

MvcMockHelpers - RhinoMocks

using System;
using System.Web;
using Rhino.Mocks;
using System.Text.RegularExpressions;
using System.IO;
using System.Collections.Specialized;
using System.Web.Mvc;
using System.Web.Routing;

namespace UnitTests
{
    public static class MvcMockHelpers
    {
        public static HttpContextBase FakeHttpContext(this MockRepository mocks)
        {
            HttpContextBase context = mocks.PartialMock<httpcontextbase>();
            HttpRequestBase request = mocks.PartialMock<httprequestbase>();
            HttpResponseBase response = mocks.PartialMock<httpresponsebase>();
            HttpSessionStateBase session = mocks.PartialMock<httpsessionstatebase>();
            HttpServerUtilityBase server = mocks.PartialMock<httpserverutilitybase>();

            SetupResult.For(context.Request).Return(request);
            SetupResult.For(context.Response).Return(response);
            SetupResult.For(context.Session).Return(session);
            SetupResult.For(context.Server).Return(server);

            mocks.Replay(context);
            return context;
        }

        public static HttpContextBase FakeHttpContext(this MockRepository mocks, string url)
        {
            HttpContextBase context = FakeHttpContext(mocks);
            context.Request.SetupRequestUrl(url);
            return context;
        }

        public static void SetFakeControllerContext(this MockRepository mocks, Controller controller)
        {
            var httpContext = mocks.FakeHttpContext();
            ControllerContext context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller);
            controller.ControllerContext = context;
        }

        static string GetUrlFileName(string url)
        {
            if (url.Contains("?"))
                return url.Substring(0, url.IndexOf("?"));
            else
                return url;
        }

        static NameValueCollection GetQueryStringParameters(string url)
        {
            if (url.Contains("?"))
            {
                NameValueCollection parameters = new NameValueCollection();

                string[] parts = url.Split("?".ToCharArray());
                string[] keys = parts[1].Split("&".ToCharArray());

                foreach (string key in keys)
                {
                    string[] part = key.Split("=".ToCharArray());
                    parameters.Add(part[0], part[1]);
                }

                return parameters;
            }
            else
            {
                return null;
            }
        }

        public static void SetHttpMethodResult(this HttpRequestBase request, string httpMethod)
        {
            SetupResult.For(request.HttpMethod).Return(httpMethod);
        }

        public static void SetupRequestUrl(this HttpRequestBase request, string url)
        {
            if (url == null)
                throw new ArgumentNullException("url");

            if (!url.StartsWith("~/"))
                throw new ArgumentException("Sorry, we expect a virtual url starting with \"~/\".");

            SetupResult.For(request.QueryString).Return(GetQueryStringParameters(url));
            SetupResult.For(request.AppRelativeCurrentExecutionFilePath).Return(GetUrlFileName(url));
            SetupResult.For(request.PathInfo).Return(string.Empty);
        }
       
    }
}

MvcMockHelpers - Moq

Here's the same thing in Moq. Muchas gracias, Kzu.

using System;
using System.Web;
using System.Text.RegularExpressions;
using System.IO;
using System.Collections.Specialized;
using System.Web.Mvc;
using System.Web.Routing;
using Moq;

namespace UnitTests
{
	public static class MvcMockHelpers
	{
		public static HttpContextBase FakeHttpContext()
		{
			var context = new Mock<httpcontextbase>();
			var request = new Mock<httprequestbase>();
			var response = new Mock<httpresponsebase>();
			var session = new Mock<httpsessionstatebase>();
			var server = new Mock<httpserverutilitybase>();

			context.Expect(ctx => ctx.Request).Returns(request.Object);
			context.Expect(ctx => ctx.Response).Returns(response.Object);
			context.Expect(ctx => ctx.Session).Returns(session.Object);
			context.Expect(ctx => ctx.Server).Returns(server.Object);

			return context.Object;
		}

		public static HttpContextBase FakeHttpContext(string url)
		{
			HttpContextBase context = FakeHttpContext();
			context.Request.SetupRequestUrl(url);
			return context;
		} 

		public static void SetFakeControllerContext(this Controller controller)
		{
			var httpContext = FakeHttpContext();
			ControllerContext context = new ControllerContext(new RequestContext(httpContext, new RouteData()), controller);
			controller.ControllerContext = context;
		}

		static string GetUrlFileName(string url)
		{
			if (url.Contains("?"))
				return url.Substring(0, url.IndexOf("?"));
			else
				return url;
		}

		static NameValueCollection GetQueryStringParameters(string url)
		{
			if (url.Contains("?"))
			{
				NameValueCollection parameters = new NameValueCollection();

				string[] parts = url.Split("?".ToCharArray());
				string[] keys = parts[1].Split("&".ToCharArray());

				foreach (string key in keys)
				{
					string[] part = key.Split("=".ToCharArray());
					parameters.Add(part[0], part[1]);
				}

				return parameters;
			}
			else
			{
				return null;
			}
		}

		public static void SetHttpMethodResult(this HttpRequestBase request, string httpMethod)
		{
			Mock.Get(request)
				.Expect(req => req.HttpMethod)
				.Returns(httpMethod);
		}

		public static void SetupRequestUrl(this HttpRequestBase request, string url)
		{
			if (url == null)
				throw new ArgumentNullException("url");

			if (!url.StartsWith("~/"))
				throw new ArgumentException("Sorry, we expect a virtual url starting with \"~/\".");

			var mock = Mock.Get(request);

			mock.Expect(req => req.QueryString)
				.Returns(GetQueryStringParameters(url));
			mock.Expect(req => req.AppRelativeCurrentExecutionFilePath)
				.Returns(GetUrlFileName(url));
			mock.Expect(req => req.PathInfo)
				.Returns(string.Empty);
		}
	}
}

Maybe RoyO will do the same thing in TypeMock in the next few hours and I'll copy/paste it here. ;)

MvcMockHelpers - TypeMock

Thanks to Roy at TypeMock.

using System;
using System.Collections.Specialized;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using TypeMock;

namespace Typemock.Mvc
{
    static class MvcMockHelpers
    {
        public static void SetFakeContextOn(Controller controller)
        {
            HttpContextBase context = MvcMockHelpers.FakeHttpContext();
            controller.ControllerContext = new ControllerContext(new RequestContext(context, new RouteData()), controller);
        }

        public static void SetHttpMethodResult(this HttpRequestBase request, string httpMethod)
        {
            
            using (var r = new RecordExpectations())
            {
                r.ExpectAndReturn(request.HttpMethod, httpMethod);
            }
        }

        public static void SetupRequestUrl(this HttpRequestBase request, string url)
        {
            if (url == null)
                throw new ArgumentNullException("url");

            if (!url.StartsWith("~/"))
                throw new ArgumentException("Sorry, we expect a virtual url starting with \"~/\".");
            var parameters = GetQueryStringParameters(url);
            var fileName = GetUrlFileName(url);
            using (var r = new RecordExpectations())
            {
                r.ExpectAndReturn(request.QueryString, parameters);
                r.ExpectAndReturn(request.AppRelativeCurrentExecutionFilePath, fileName);
                r.ExpectAndReturn(request.PathInfo, string.Empty);
            }
        }

        static string GetUrlFileName(string url)
        {
            if (url.Contains("?"))
                return url.Substring(0, url.IndexOf("?"));
            else
                return url;
        }
        static NameValueCollection GetQueryStringParameters(string url)
        {
            if (url.Contains("?"))
            {
                NameValueCollection parameters = new NameValueCollection();

                string[] parts = url.Split("?".ToCharArray());
                string[] keys = parts[1].Split("&".ToCharArray());

                foreach (string key in keys)
                {
                    string[] part = key.Split("=".ToCharArray());
                    parameters.Add(part[0], part[1]);
                }

                return parameters;
            }
            else
            {
                return null;
            }
        }
        public static HttpContextBase FakeHttpContext(string url)
        {
            HttpContextBase context = FakeHttpContext();
            context.Request.SetupRequestUrl(url);
            return context;
        }
        public static HttpContextBase FakeHttpContext()
        {
            HttpContextBase context = MockManager.MockObject<HttpContextBase>().Object;
            HttpRequestBase request = MockManager.MockObject<HttpRequestBase>().Object;
            HttpResponseBase response = MockManager.MockObject<HttpResponseBase>().Object;
            HttpSessionStateBase sessionState = MockManager.MockObject<HttpSessionStateBase>().Object;
            HttpServerUtilityBase serverUtility = MockManager.MockObject<HttpServerUtilityBase>().Object;
            using (var r = new RecordExpectations())
            {
                r.DefaultBehavior.RepeatAlways();
                r.ExpectAndReturn(context.Response, response);
                r.ExpectAndReturn(context.Request, request);
                r.ExpectAndReturn(context.Session, sessionState);
                r.ExpectAndReturn(context.Server, serverUtility);
            }
            return context;
        }

    }
}
Technorati Tags: ,

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

The Weekly Source Code 18 - Deep Zoom (Seadragon) Silverlight 2 MultiScaleImage Mouse Wheel Zooming and Panning Edition

March 7, '08 Comments [25] Posted in Mix | Silverlight | Source Code
Sponsored By

Silverlight Project Test Page - Windows Internet Explorer (4)Dear Reader, I present to you eighteenth in a infinite number of posts of "The Weekly Source Code." Here's some source I was reading - and writing - this week at Mix.

I have been checking out Deep Zoom in Silverlight 2, but I thought it was a bummer that there wasn't (yet) a "Hello DeepZoom World!" example that includes panning, zooming (mouse wheel) support out of the box. The Vertigo example behaves exactly as I'd want my stuff to behave. You can see my working Deep Zoom example here or by clicking the picture at right.

Adding Mouse Wheel support to Silverlight can happen a couple of ways. Mouse Wheel events are sourced by the browser, not by Silverlight itself (which is the way you'd want it as Silverlight lives inside the browser, it shouldn't replace its behaviors, IMHO).

So, you could use the Javascript code from Adomas along with the work that Jeff Prosise did to reach into Silverlight and call methods. The events would be handled in JavaScript and the Zoom method would be called via JavaScript over the bridge into Silverlight managed code.

However, you can also reach out from inside managed code and set managed handlers for DOM (JavaScript events) like Pete Blois does with his Mouse Wheel Helper class. I use this class directly by downloading it from Pete's blog and adding it to my project. This is significant because it doesn't require ANY external JavaScript files. All the events are handled by managed code.

if (HtmlPage.IsEnabled) {
  HtmlPage.Window.AttachEvent("DOMMouseScroll", this.HandleMouseWheel);
  HtmlPage.Window.AttachEvent("onmousewheel", this.HandleMouseWheel);
  HtmlPage.Document.AttachEvent("onmousewheel", this.HandleMouseWheel);
}

I took this along with snippets from Yasser Makram and John posting in Yasser's blog's comments got me mostly what I needed.

I've seen some basic examples using either mouse clicking or key-downs to get the zooming effect, but I wanted to support mouse wheel events as well, just like the stuff shown off at Mix.

This more complete example gives you:

  • Drag to pan
  • Click to zoom in, Shift Click to zoom out
  • Mouse wheel to zoom in and out
  • No JavaScript dependency at all - everything is in managed code.

First, start with the output of the DeepZoom composer (I just used the Windows Wallpapers I had on this machine to compose a DeepZoom image in the editor) and copy the resulting exported folder structure somewhere (I put it under bin/debug for ease, but you can put it wherever as long as the source attribute lines up in your XAML:

<UserControl
	xmlns="http://schemas.microsoft.com/client/2007"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	x:Class="SilverlightApplication1.Page"
	Width="800" Height="600" >
  <Grid
    x:Name="LayoutRoot"
    Background="Gray">
    <MultiScaleImage 
      x:Name="msi"
      ViewportWidth="1.0"
      Source="http://www.yourdomain.com/foo/items.bin" />
  </Grid>
</UserControl>

RANDOM NOTE: Here's a cool switch you can set on MultiScaleImage. It's UseSprings="false" and it'll turn off the zooming animation. Why would you want to do this?  Well, that very zoom-in/zoom-out animation gives DeepZoom an opportunity to perform its "visual slight of hand" and transition between images. When the animation happens, you are less likely to notice the transition between tiles (a good thing). Of course, I want to get my head around how it all works so I liked seeing the transitions.

Keep in mind it's four in the morning, so this code is a little wonky and not at all thought-out and I've only been at it for an hour. I'm REALLY interested in what you, Dear Reader, can do with it and make it better so we all have a canonical example to start from. This is NOT that example, I'm actually kind of reticent to post it here because it's so hacked together, but that's half the fun, right? It works OK, I think.

One thing to point out, note that the name of the control is "msi," set in the XAML above via x:name="msi" so you'll see me referencing properties like msi.thisandthat in the managed XAML code-behind below. Also, the "using akadia" below is Pete's MouseHandler code's namespace referenced from my page.

My code hooks up a bunch of events from the constructor using anonymous delegates, and those work together to call a single Zoom() helper method.

using System; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Documents; 
using System.Windows.Ink; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Animation; 
using System.Windows.Shapes; 
using System.Windows.Threading; 
using akadia; 

namespace SilverlightApplication1 
{ 
    public partial class Page : UserControl 
    { 
        Point lastMousePos = new Point(); 
        double _zoom = 1; 
        bool mouseButtonPressed = false; 
        bool mouseIsDragging = false; 
        Point dragOffset; 
        Point currentPosition; 

        public double ZoomFactor 
        { 
            get { return _zoom; } 
            set { _zoom = value; } 
        } 

        public Page() 
        { 
            this.InitializeComponent(); 

            this.MouseMove += delegate(object sender, MouseEventArgs e) 
            { 
                if (mouseButtonPressed) 
                { 
                    mouseIsDragging = true; 
                } 
                this.lastMousePos = e.GetPosition(this.msi);   
            }; 

            this.MouseLeftButtonDown += delegate(object sender, MouseButtonEventArgs e) 
            { 
                mouseButtonPressed = true; 
                mouseIsDragging = false; 
                dragOffset = e.GetPosition(this); 
                currentPosition = msi.ViewportOrigin; 
            }; 

            this.msi.MouseLeave += delegate(object sender, MouseEventArgs e) 
            { 
                mouseIsDragging = false; 
            }; 

            this.MouseLeftButtonUp += delegate(object sender, MouseButtonEventArgs e) 
            { 
                mouseButtonPressed = false; 
                if (mouseIsDragging == false) 
                { 
                    bool shiftDown = (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift; 

                    ZoomFactor = 2.0; 
                    if(shiftDown) ZoomFactor = 0.5; //back out when shift is down 
                    Zoom(ZoomFactor, this.lastMousePos); 
                } 
                mouseIsDragging = false; 
            }; 

            this.MouseMove += delegate(object sender, MouseEventArgs e) 
            { 
                if (mouseIsDragging) 
                { 
                    Point newOrigin = new Point(); 
                    newOrigin.X = currentPosition.X - (((e.GetPosition(msi).X - dragOffset.X) / msi.ActualWidth) * msi.ViewportWidth); 
                    newOrigin.Y = currentPosition.Y - (((e.GetPosition(msi).Y - dragOffset.Y) / msi.ActualHeight) * msi.ViewportWidth); 
                    msi.ViewportOrigin = newOrigin; 
                } 
            }; 

            new MouseWheelHelper(this).Moved += delegate(object sender, MouseWheelEventArgs e) 
            { 
                e.Handled = true; 
                if (e.Delta > 0) 
                    ZoomFactor = 1.2; 
                else 
                    ZoomFactor = .80; 

                Zoom(ZoomFactor, this.lastMousePos); 
            }; 
        } 

        public void Zoom(double zoom, Point pointToZoom) 
        { 
            Point logicalPoint = this.msi.ElementToLogicalPoint(pointToZoom); 
            this.msi.ZoomAboutLogicalPoint(zoom, logicalPoint.X, logicalPoint.Y); 
        } 
    } 
}

Three One thing I am having trouble with, but I haven't run this under a debugger yet as I haven't installed the single Silverlight 2 Beta 1 Tools Installer (Troubleshooting Silverlight 2 Beta 1 Tools Installer). I just built it in Expression Blend 2.5 and Notepad2. I was enjoying myself so much I didn't want to stop and install anything. ;)

  • One, if you scroll WHILE the "spring zoom" animation is happening, something goes wrong and you'll continue zooming in, no matter what direction you're scrolling.
  • Second, more subtlety, if you scroll in, stop, then start scrolling out, it'll scroll in a step, then start scrolling out, so there's clearly a state issue I'm goofing up. Stated another way, if a managed event comes in WHILE the animation is happening, the direction it's currently zooming just keeps going. (Maybe a subtle bug, or my code needs to be debounced because it seems like the mouse wheel events are coming in too fast.) In my example, you have to let the animation "settle down" in order to zoom the other way. Of course, if you just F5 to Refresh you can get back to home and reset the zoom.
  • I figured it out, I was using a relative calculated ZoomFactor when I should have used an absolute factor.
  • Third, I want to prevent the image from panning outside the viewable area (meaning, I don't want folks to get lost) and I'd like to stop the zooming in and out at some reasonable max/min.
  • Don't forget to setup .xap files as the mime type application/x-silverlight-app on on your hoster's IIS instance.

Very cool and fairly easy considering all that's going on. I'll look into other ways this can be exploited in the morning.

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

Deep Zoom (Seadragon) Silverlight 2 MultiScaleImages at Mix

March 6, '08 Comments [22] Posted in Mix | Silverlight
Sponsored By

outrun One of the cool things seen at Mix 08 today was the Deep Zoom technology. This was originally called Seadragon and came out of the Live Labs research.

You can see it live at the Hard Rock Cafe Memorabilia site. This Silverlight application includes a tiled montage of 512 many-megapixel images, some 10+megapixels, while some are stitched together to sizes upwards of 40-50 megapixels, it seems.

Remember the first 3D games like Sega's OutRun? They seemed 3D, but basically bitmaps were being scaled as you "drove forward" and each graphical asset, like a house, for example, would have a number of pre-rendered versions. Another example are .ico files that have 16x16, 32x32, etc, versions. As the viewer there is one image, but there's multiple resolutions depending on how far in you want to zoom. This is the basic idea behind "multi-scale images."

You can see examples of exceedingly large images, often called "giga-pixel" images all over the web. A great example of zooming using Flash is at Harlem 13-Gigapixels.com. The large Harlem image is 2,045 individual images of 12 megapixels each. The rendering when zooming in is a lot like Google Earth. You zoom into an image of a specific resolution then that first image transitions into a tile of a second image rendered another resolution. You can create these kinds of images with Panorama stitching software like AutoPano.net, and PanoramaFactory. I took a large 50+ megapixel panorama of Ngorongoro Crater in Tanzania that I think would make a good example for this kind of thing. I'll dig it up and make a Deep Zoom example project.

DeepZoom includes an optimized local Silverlight Renderer and XAML control that makes creating these fairly easy. Try it out over at the HardRock Memorabilia site and take specific notice of this guitar I've circled in the picture below.

Hardrock Memorabilia

Zoom in...

image

And more...into the screw knob in the lower left corner...

image

You can actually see a reflection of the bench and the camera and Scott Stanfield from Vertigo who's taking the shot of this guitar, reflected in the shiny knob. Vertigo wrote the Hardrock site and they also have a great Deep Zoom Gallery.

Project Name

You can go download the Preview of the Deep Zoom Composer now. You can see its heritage when you make a new Project and it puts it in a folder called "Seadragon Projects." Here's the Deep Zoom Composer User Guide.

You place a number of pictures (or a large one) on the surface and export the collection. There will be a scene graph XML file created indicating location, size, z-order and aspect ratio of the images.

SparseImageSceneGraph.xml - Notepad

Then an output directory will be created with folders for each image and, depending on the relative largeness of the source images, a series of tiles generated a number of zoom levels. Here's an example directory structure:

11

Silverlight Project Test Page - Windows Internet Explorer (2)See how that image at zoom level 11 has 20 different tiles? They've got to balance many factors to make it all seamless. They're considering how many tiles are needed for that resolution as it relates to a zoom level's need for memory, as well as considering a reasonable number of requests to the server. There's the extreme need for a good user-experience so you want the images to come fast and furious, be easily cached and quickly shown to give the perception of speed. You don't have to sweat any of this stuff, as the multi-resolution tiles stitched together automatically by the client rendered.

After you've exported a project into these files, you create a MultiScale image in XAML like this, referencing a bin file manifest that knows contains info about the directory structure and files:

<MultiScaleImage x:Name="deepZoomObject" Source="foo/info.bin"/>

This gets you the image rendered. To zoom a MultiScaleImage, you call ZoomAboutLogicalPoint:

public void ZoomAboutLogicalPoint(double zoomIncrementFactor, double zoomCenterLogicalX, double zoomCenterLogicalY)

But there's a decent amount of state management that needs to be done as well as deciding how you want things animated, whether or not your image contains sub-images, etc. In the morning I'll take a look at how to hook up JavaScript events like Mouse Wheel Scrolls and do some zooming and panning. Hopefully Vertigo will release their hard-won source code for the canonical example to the community. It's pretty slick.

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

ASP.NET MVC Preview 2 Screencast Tutorials

March 5, '08 Comments [44] Posted in ASP.NET | ASP.NET MVC | Learning .NET | Microsoft | Mix | Screencasts
Sponsored By

ASP.NET MVC Preview 2 released today and you can head over to http://www.asp.net/mvc to get the new stuff. I put together four new screencasts for this release.

I really feel that a well-done screencast is a great learning tool. Last month I did a survey of you all, Dear Reader, and got 1000 responses and published the results. I took all the comments, stats and general feedback and tried to improve these screencasts. As a result, I am more judicious in my use of PIP (Picture in Picture) and the screencasts are shorter and more to the point.

If you watched the original MVC Screencast, some of the content in videos #1 and #2 will be repeated as it's been updated for Preview 2, but I hope it doesn't waste your time. However, there's new things sprinkled around and a number of changes are called out. Additionally, videos 3 and 4 are completely new content. Video #4 talks about how to use a Mocking Framework to to TDD with MVC and #3 touches on a number of advanced techniques that you can practice at the Mix HOL (Hands-On-Labs).


#1 | MVC Preview - Part 1 - Displaying Data

16 minutes, 41 seconds

#2 | MVC Preview - Part 2 - Editing Data and Making Forms

18 minutes, 38 seconds

#3 | MVC Preview - Part 3 - Advanced Techniques

12 minutes, 56 seconds


#4 | MVC Preview - Part 4 - Testing

23 minutes, 20 seconds

UPDATE: There's only a template for MSTest in the new File | New MVC Project wizard right now because we're reaching out to the big Unit Testing libraries to get them to help create templates. If you have a Unit Testing Library, you can get into the MVC File | New Project dialog by packaging up your templates like this. I'd guess/hope that the installers for the major libraries would just include their templates in their next versions.

imageI'll work with Phil to get all the source to the apps in these videos posted soon. I hope you enjoy them as much as I enjoyed making them.

Note that while the player on the http://www.asp.net site is Silverlight, you can download versions on the same page in WMV format, as well as ones customized for the Zune, iPod, PSP, and MP4 and 3GP.

One other important new feature on the ASP.NET site is that Videos now accept comments and those comments include RSS feeds. Do feel free to leave comments on this blog, but you can also comment on videos and screencast tutorials all over the ASP.NET site. We all read these comments (including my boss) so your feedback IS paid attention to.

Technorati Tags: ,

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

Heading to Mix08 and Tweeting along the way

March 4, '08 Comments [4] Posted in Mix | Speaking
Sponsored By

image In the morning I'm heading to Mix. I'm going to be using Twitter as a microblog during my time at Mix. I'll post where I am and what I'm up to. You can sign up and "follow" me on Twitter, and select optional notifications via SMS or IM.

Scott Watermasysk has a great post called "Why Twitter" and I've been "tweeting" more and more of late. It's a great way to communicate, kind of like IRC except with permalinks and angle brackets. ;) Twitter is a lot more casual and there's a lot more off-the-cuff random discussions.

ScottWater has also setup a series of Hashtags such that any "tweets" on Twitter that include "#mix" "#mix08" in them will show up on this realtime feed. This is a great way to keep up on the hour by hour Mixiness that'll be going on, whether you're at the conference or not.

Also, if you haven't done your Mix Schedule, check out this ClickOnce WPF app that does Mix Scheduling. I'll be speaking on ASP.NET MVC on Thursday at 4:15 in Lando 4201 and Phil Haack and I will be hosting a Q&A in the Mix Open Spaces area immediately following.

I'll be looking to do a number of technical podcasts while in Vegas so if there's certain cool or obscure (or both) questions you want answered, leave them here and I'll try to chase them down. There's also a Mix category on my blog so posts while I'm there will be tagged as well.

See you in Vegas. Man, I hate this town.

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

2008 Window Scripting Games - Advanced PowerShell Event 7

March 4, '08 Comments [25] Posted in Learning .NET | PowerShell | Ruby
Sponsored By

Olympic_flameIn a few days the 2008 Scripting Games will come to an end. This is a yearly event that the Script Center does. There's a beginner and an advanced division and a bunch of deceptively hard problems. I was selected to be on of the "Guest Commentators (list here)" which really means they wanted me to solve one of the problems and provide the solution as an example. I'm not sure my solution is the best way, but it did solve the problem they assigned me.

My problem was Event 7: Play Ball! and I was to write a script that schedules all the games for a round-robin baseball tournament. The complete scenario is here, but in a nutshell:

"In a round-robin baseball tournament (or any kind of round-robin tournament, for that matter), every team plays each of the other teams in the tournament one time and one time only. For example, suppose we have a tournament with three teams (teams A, B, and C). In that case, the tournament would consist of the following set of games:

  • A vs. B
  • A vs. C
  • B vs. C

See how that works? Team A plays Team B and Team C; Team B plays Team A and Team C; and Team C plays Teams A and B."

A few other wrinkles thrown in are that the games must be randomized, otherwise Team A will play too many in a row and you need to schedule six teams, A through F. Of course, to be clear, every team must pay every other team once and only once. Here's my solution, hacked together quickly.

#this only works with an even number of teams
cls
[array]$global:games = $nul
function rotateArray($a)
{
 $first, $rest = $a
 $a = $rest + $first
 return $a
}
function makeGames($a)
{
 $i = 0;
 while($i -lt $a.Length/2)
 {
  $global:games = $global:games + ($a[$i].ToString() + " vs. " + $a[$a.Length-1-$i].ToString())
  $i++
 }  
}
$a = "A","B","C","D","E","F"
$z = 0
while($z -lt $a.Length-1)
{
 makeGames($a)
 # hold on to the first one
 
 $first, $rest = $a
 #rotate the rest
 $rest = rotateArray($rest)
 $a = [array]$first + $rest
 $z++
}
#randomize games
$a = [collections.arraylist]$global:games
$r = $a.count..1 |% {$R = new-object random}{$R.next(0,$a.count) |%{$a[$_];$a.removeat($_)}}
$r

Doing this in PowerShell took my brain a while to get. Note the RotateArray method's use of multi-variable assignment to chop up he array into first and rest. That wasn't obvious to me as a C-guy for the last 15 years, but it made my solution simpler when I refactored and introduced it.

The solution (remember, it's randomized) will look something like this:

B vs. D
B vs. C
A vs. D
B vs. F
C vs. D
A vs. F
A vs. B
C vs. F
E vs. F
A vs. E
D vs. F
B vs. E
D vs. E
A vs. C
C vs. E

Enjoy. Here's the same solution in Perl from Jan Dubois and again in VBScript. Who wants to do the F# version and the Ruby version? What about just LINQ to Objects?

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

Six Months in the Inside - Am I evil yet?

March 3, '08 Comments [58] Posted in Microsoft | Musings
Sponsored By

imageYikes, I got an email from HR today that said "Congratulations on your 6th month anniversary at Microsoft!" Has it been that long? Apparently so, although it feels like maybe three months, tops.

I learned a lot about the culture in the first three weeks. There is a lot of groupthink and echo-chamber type behaviors from folks that have been there a long time, but most people I've interacted directly with are always re-asserting their independence and catching themselves thinking old-style thoughts, which is comforting to me.

It's one thing to exhibit an undesirable behavior, but it's much worse to be ignorant of that behavior. I think Microsoft's biggest problem right now is answers like "We've always done it that way," whether it's naming, versioning, documentation, conferences, whatever. I like to keep things a little more agile and tighten up the feedback loop. I think a process of continual improvement and self examination is so important.

What is my job?

I get this a lot. Seems like an obvious question. I took the title "Developer Division Community Liaison" because it was very broad. Phil likes to say I'm paid to be Scott Hanselman, but I don't like that very much because it implies some kind of punditry. If I ever start sounding Dvorak-esque, please let me know.

When my dad or the public (read: non-computer person) asks what my job is, I tell them "I'm writing a book that will never end (blog) and create training materials like videos and tutorials for Microsoft." When a technical person asks, I say the same thing with more detail. Also, if ScottGu coughs, I get him lozenges. Ahem. Yessir!

My boss runs http://www.asp.net, http://www.silverlight.net, http://www.windowsclient.net, etc, but our team is very small, only four FTE (Microsoft-speak for Full Time Employees). But, we're growing. At Microsoft we're graded on our ability to "meet our commitments" and my commitments (which were written up in a formal meeting) are mostly metrics like "write n number of significantly technical blog posts, write such-and-such book, give x number of presentations." Commitments are supposed to be a stretch, and I've got 7 different significant commitments so I've had some really late nights. The "liaison" portion of the job is also becoming more formalized in the next month, so I may take over some specific MVP-type groups.

This paragraph added for clarity (see comments): No, my job isn't to blog, although the blog is a good place to point to stuff I do. Folks originally wanted me to make a blog at msdn.com but I thought that was silly. For my day job, I do screencasts, tutorials, presentations, community outreach, books, articles for MSDN, internal presentations, advocate for the customer (this takes up more time than you'd think), provide direct feedback to the product groups, route your feedback (you know this if you've ever emailed me with a problem) and troubleshoot obscure bugs, as well as working on technical strategy with certain wonks at Microsoft. As I said in the Blue Badge post, the podcast is still belongs solely to Carl and me and I pick the guests with tips from the audience.

When I write, there's no editorialization, meaning that no-one reads my posts before I send them, although I often check with product groups to make sure I'm not completely wrong on some technical detail. Microsoft is pretty mellow and says "blog smart." That said, I'm sure I could get fired if I wrote something truly bogus, but otherwise there's no ghost writing. (Yes, ScottGu still writes his own posts...so far).

Sometimes ScottGu will email me with a one line note like this one yesterday about IIS7 and FTP7 "This might be a cool blog post to cover.  It has a ton of new features (including integration with membership/roles), new admin tool and a bunch of cool new features."

However, I already had FTP7 on my list of cool things since I've been talking to the IIS7 team and that'll hopefully be done today or tomorrow. It's funny we usually think about the same general things and I'm mostly either a day ahead or a day behind whatever cool thing is going on.

Am I Evil yet?

P0000259 I haven't been invited to any evil (or eeeveeel) meetings and haven't found the evil mailing list. As far as I can see Microsoft is less evil as it is unorganized. Within groups, communication is pretty good, but between divisions is trickier because there's always a doppleganger out there, like Evil Spock who is working on the same thing you're working on. I guess that means there's a Scott Hanselman out there without a goatee writing a similar post as this?

That pic is me in my early twenties. If Chins Could Kill...I'm trying to find a non-bearded pic but that's the best I can do! That guy doesn't look evil, right?

Working Remotely

0900aecd8054e6ca_null_null_null_10_09_06-2 The Remote Office thing is still lonely, but I'm making up for it with regular lunches and LOTS of video conferencing thanks to ooVoo. My master plan to make a robot out of a Roomba has fizzled due to lack of a battery (and my lack of a mechanical engineering degree) that could last a full day. My new "Plan B" is get a 42" LCD (as seen at right) and make a Portal into my house.

What have I done well?

I thought the first practice videos turned out well, and I have taken all the feedback you've given and applied it to a new four-part tutorial on MVC that will be coming soon, hopefully formalizing those best practices.

I think the Wiki (beta) is turning out pretty cool. I seeded it, but there's already contributors who have exceeded the number of posts I've added and greatly improved it.  I think there's a lot of work we can do to make the administration better for moderators, but it's looking nice regardless, so far.

I've had a lot of fun speaking lately. When I spoke while I was working at Corillian, I usually had to take vacation days or do it on the sly side, although for large conferences my boss at the time was exceedingly cool about it.

What have I done poorly?

I am not managing my time very well and since we've got a 3 month old and an enthusiastic 2 year old and I'm working from home I feel like days are very fragmented. It's good to be home, but my wife thinks I'm actually around less which is definitely a not good thing to feel. I need to do a better job of getting up earlier and banging out work. I like being informed, but I think I may need to go on an info-diet to find more time to get things done.

Have you started a new job in the last year? How is going for you?

Related Posts

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.