Scott Hanselman

Hanselminutes Podcast 239 - Creative Outlets with Rob Conery

November 18, 2010 Comment on this post [0] Posted in Podcast
Sponsored By

devlifeglassesRob Conery joins Scott this week as the they talk about their new (and very different) podcast "This Developer's Life." Why does Rob feel the need to create? What's the process? How does one create their own podcast and what are some tips for not just success, but feeling good after!

NOTE: If you want to download our complete archives as a feed - that's all 239 shows, subscribe to the Complete MP3 Feed here.

Also, please do take a moment and review the show on iTunes.

Subscribe: Subscribe to Hanselminutes Subscribe to my Podcast in iTunes

Download: MP3 Full Show

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.

Building quality software is never easy. It requires skills and imagination. We cannot promise to improve your skills, but when it comes to User Interface and developer tools, we can provide the building blocks to take your application a step closer to your imagination. Explore the leading UI suites for ASP.NET AJAX,MVC,Silverlight,Windows Forms and WPF. Enjoy developer tools like .NET Reporting, ORM, Automated Testing Tools, Agile Project Management Tools, and Content Management Solution. And now you can increase your productivity with JustCode, Telerik’s new productivity tool for code analysis and refactoring. Visit www.telerik.com.

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 bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Hanselminutes Podcast 238 - ASP.NET MVC 3 RC and NuGet Package Management with Phil Haack

November 18, 2010 Comment on this post [0] Posted in ASP.NET | ASP.NET MVC | Podcast
Sponsored By

image It's PDC week and Scott's on campus with Phil Haack talking about ASP.NET MVC 3 RC and the NuPack NuGet Package Manager. Get ASP.NET MVC 3 RC now! Also, check out my PDC 2010 presentation on both topics!

NOTE: If you want to download our complete archives as a feed - that's all 238 shows, subscribe to the Complete MP3 Feed here.

Also, please do take a moment and review the show on iTunes.

Subscribe: Subscribe to Hanselminutes Subscribe to my Podcast in iTunes

Download: MP3 Full Show

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.

Building quality software is never easy. It requires skills and imagination. We cannot promise to improve your skills, but when it comes to User Interface and developer tools, we can provide the building blocks to take your application a step closer to your imagination. Explore the leading UI suites for ASP.NET AJAX,MVC,Silverlight,Windows Forms and WPF. Enjoy developer tools like .NET Reporting, ORM, Automated Testing Tools, Agile Project Management Tools, and Content Management Solution. And now you can increase your productivity with JustCode, Telerik’s new productivity tool for code analysis and refactoring. Visit www.telerik.com.

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 bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

A Better ASP.NET MVC Mobile Device Capabilities ViewEngine

November 16, 2010 Comment on this post [37] Posted in ASP.NET | ASP.NET MVC | Mobile | Open Source
Sponsored By

UPDATE from 2011: These View Engines have a subtle release mode caching bug. Peter Mourfield and I have released a better MobileViewEngine for ASP.NET MVC 3 that is closer to what MVC 4 will look like. The updated blog post with the new MobileViewEngine is here.

In March of 2009 I spoke at Mix 09, Microsoft's Web Conference and presented a number of ASP.NET MVC features. I extended the NerdDinner Sample with a naive implementation of what I called a MobileCapableWebFormViewEngine. Here's the basic implementation. Don't use this, it's broken.

public class MobileCapableWebFormViewEngine : WebFormViewEngine
{
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
ViewEngineResult result = null;
var request = controllerContext.HttpContext.Request;

// Avoid unnecessary checks if this device isn't suspected to be a mobile device
if (request.Browser.IsMobileDevice)
{
result = base.FindView(controllerContext, "Mobile/" + viewName, masterName, useCache);
}

//Fall back to desktop view if no other view has been selected
if (result == null || result.View == null)
{
result = base.FindView(controllerContext, viewName, masterName, useCache);
}

return result;
}
}

This sample was never meant to be official anything, just show possibilities, but a lot of folks built on top the general idea. However, as reader Michael Baden Roufa (along with Will Creedle and Paulie Srinuan) pointed out to me in email, the logic is wrong. The FindView (and its sibling FindPartialView) method gets called by the ASP.NET MVC framework with useCache=true first, then useCache=false if nothing is found. This little sample view engine, as it is, is dependant on what kind of browser requests a page first because it's using one cache for two kinds of requests. I've noticed this on the NerdDinner site...depending on which device hits the site first, it can get stuck (cached) in a situation where non-mobile pages are being served to iPhones and mobile browsers.

I'll write a much longer post in a few days about how one goes from Broken, to Complex, Refactors, then finally gets to a Simpler Design. Here's how you'd use this code today. This is an example usage within Global.asax. You'd change it depending on the devices you'd care about.

void Application_Start()
{
RegisterRoutes(RouteTable.Routes);

ViewEngines.Engines.Clear();
ViewEngines.Engines.AddIPhone<WebFormViewEngine>();
ViewEngines.Engines.AddMobile<WebFormViewEngine>("blackberry", "Mobile/BlackBerry");
ViewEngines.Engines.AddMobile<WebFormViewEngine>(c => c.UserAgentContains("SomethingCustom"), "Mobile/SomethingCustom");
ViewEngines.Engines.AddGenericMobile<WebFormViewEngine>();
ViewEngines.Engines.Add(new WebFormViewEngine());
}

You're probably familiar with the concept of ViewEngines and you have heard, at least around town, that you can add 3rd party View Engines, and even mix them and match them. You can have WebForms Views, Spark Views, and Razor Views all in the same project. Rather than deriving from WebFormsViewEngine (or now in ASP.NET MVC 3, RazorViewEngine) and making a super-smart ViewEngine that has too many responsibilities (remember the Single Responsibility Principle), instead I talked to a bunch of folks and realized that I didn't want a smart ViewEngine with knowledge of a lot of devices. ASP.NET MVC already has the notion of an ordered list of ViewEngines.

Since I was on campus this week visiting, I talked to lots of folks like Levi Broderick, Dmitry Robsman, Damian Edwards, Brad Wilson, Marcin Dobosz and more. That's what I really miss while working from Oregon...the wandering around and brainstorming.

Anyway, adding some extension methods to ViewEngineCollection for standard options, while creating a CustomMobileViewEngine as a base to build on makes for a more flexible solution and cleaner code. The example above is the "everything example." You've got an iPhone View, a generic Mobile View, a BlackBerry View based on UserAgent, as well as a Custom View using a custom Lambda for some custom device, then finally the desktop view, which in this case is the WebFormViewEngine.

If you just wanted a Desktop Site and an iPhone site with Razor you'd do this. Note that the ordering is important. View folders are checked in the order they are listed here, each falling to the next until a View is found.

ViewEngines.Engines.Clear();
ViewEngines.Engines.AddIPhone<RazorViewEngine>();
ViewEngines.Engines.AddGenericMobile<RazorViewEngine>();
ViewEngines.Engines.Add(new RazorViewEngine());

Mixing WebForms Views and Razor Views next to Desktop Views and Mobile Views

You can also mix and match. A common scenario might be that you already have a nice WebFormViewEngine ASP.NET MVC site, but you'd like to add a mobile site and start using Razor at the same time for just those mobile views.

ViewEngines.Engines.Clear();
ViewEngines.Engines.AddGenericMobile<RazorViewEngine>();
ViewEngines.Engines.Add(new WebFormViewEngine());

So where are these Views to be found? Let's look at a more custom example. Given this example above, if I am in the HomeController's Index method and it calls View(), we'll look first for ~/Views/Home/Mobile/Index.cshtml, then ~/Views/Home/Index.aspx.

I could also add a custom UserAgent string to look for, with its own folder, with one line:

ViewEngines.Engines.Clear();
ViewEngines.Engines.AddMobile<WebFormViewEngine>("blackberry", "Mobile/BlackBerry");
ViewEngines.Engines.AddGenericMobile<RazorViewEngine>();
ViewEngines.Engines.Add(new RazorViewEngine());

Now if I called the HomeController's Index's method looking for the Index view, we'll look first for ~/Views/Home/Mobile/Blackberry/Index.aspx, then ~/Views/Home/Mobile/Index.cshtml, and finally ~/Views/Home/Index.aspx. However, we'll only be checking Mobile if it's a mobile device (per the Browser Capabilities object) and only checking the BlackBerry folder if the UserAgent contains "blackberry."

Sometimes, though, you'll want more complex custom "is this the right device" logic. Then you can pass in a lambda using whatever reasoning makes you happy, be it UserAgents, cookies, headers, whatever.

ViewEngines.Engines.Clear();
ViewEngines.Engines.AddIPhone<WebFormViewEngine>();
ViewEngines.Engines.AddMobile<WebFormViewEngine>("blackberry", "Mobile/BlackBerry");
ViewEngines.Engines.AddMobile<RazorViewEngine>(c => c.SomeExtensionMethod("SomethingCustom"), "Mobile/SomethingCustom");
ViewEngines.Engines.AddGenericMobile<RazorViewEngine>();
ViewEngines.Engines.Add(new WebFormViewEngine());

Now I've added a new line that digs around the ControllerContext (that's "c") for some information to base my decision on, and if that is true, it'll use the Mobile/SomethingCustom folder for the views. Remember that order matters.

Certainly this is just one way to do things, but it does maximize controller reuse. Not every site can be done like this. Different sites have different flows. You might end up with custom controllers for certain devices or maybe just custom actions. Or, you may need two totally different sites. It depends on your business needs, but it's nice to know the flexibility is here.

CustomMobileViewEngine

To do a hello world example, make a new MVC project (this will work for ASP.NET MVC 2 or ASP.NET MVC 3) and add this class to it (be aware of your namespace.) Again, I'll get this in NuGet soon.

public class CustomMobileViewEngine : IViewEngine
{
public IViewEngine BaseViewEngine { get; private set; }
public Func<ControllerContext, bool> IsTheRightDevice { get; private set; }
public string PathToSearch { get; private set; }

public CustomMobileViewEngine(Func<ControllerContext, bool> isTheRightDevice, string pathToSearch, IViewEngine baseViewEngine)
{
BaseViewEngine = baseViewEngine;
IsTheRightDevice = isTheRightDevice;
PathToSearch = pathToSearch;
}

public ViewEngineResult FindPartialView(ControllerContext context, string viewName, bool useCache)
{
if (IsTheRightDevice(context))
{
return BaseViewEngine.FindPartialView(context, PathToSearch + "/" + viewName, useCache);
}
return new ViewEngineResult(new string[] { }); //we found nothing and we pretend we looked nowhere
}

public ViewEngineResult FindView(ControllerContext context, string viewName, string masterName, bool useCache)
{
if (IsTheRightDevice(context))
{
return BaseViewEngine.FindView(context, PathToSearch + "/" + viewName, masterName, useCache);
}
return new ViewEngineResult(new string[] { }); //we found nothing and we pretend we looked nowhere
}

public void ReleaseView(ControllerContext controllerContext, IView view)
{
throw new NotImplementedException();
}
}

public static class MobileHelpers
{
public static bool UserAgentContains(this ControllerContext c, string agentToFind)
{
return (c.HttpContext.Request.UserAgent.IndexOf(agentToFind, StringComparison.OrdinalIgnoreCase) > 0);
}

public static bool IsMobileDevice(this ControllerContext c)
{
return c.HttpContext.Request.Browser.IsMobileDevice;
}

public static void AddMobile<T>(this ViewEngineCollection ves, Func<ControllerContext, bool> isTheRightDevice, string pathToSearch)
where T : IViewEngine, new()
{
ves.Add(new CustomMobileViewEngine(isTheRightDevice, pathToSearch, new T()));
}

public static void AddMobile<T>(this ViewEngineCollection ves, string userAgentSubstring, string pathToSearch)
where T : IViewEngine, new()
{
ves.Add(new CustomMobileViewEngine(c => c.UserAgentContains(userAgentSubstring), pathToSearch, new T()));
}

public static void AddIPhone<T>(this ViewEngineCollection ves) //specific example helper
where T : IViewEngine, new()
{
ves.Add(new CustomMobileViewEngine(c => c.UserAgentContains("iPhone"), "Mobile/iPhone", new T()));
}

public static void AddGenericMobile<T>(this ViewEngineCollection ves)
where T : IViewEngine, new()
{
ves.Add(new CustomMobileViewEngine(c => c.IsMobileDevice(), "Mobile", new T()));
}
}

Go to your Global.asax.cs (or .vb) as I've showed before, and add maybe these lines to the end of your Application_Start().

ViewEngines.Engines.Clear();
ViewEngines.Engines.AddIPhone<RazorViewEngine>();
ViewEngines.Engines.AddGenericMobile<RazorViewEngine>();
ViewEngines.Engines.Add(new RazorViewEngine());

If you're using ASP.NET MVC 2, or if you simply prefer to, change RazorViewEngine to WebFormViewEngine. Or, mix and match to your taste.

In your Views folder, make ~/Views/Home/Mobile and ~/Views/Home/Mobile/iPhone and copy ~/Views/Home/Index.* to it. Then, open the new Mobile Index and Mobile/iPhone Index and change them. I'm put in "I'm a mobile view" and "I'm an iPhone view" just so I'd know for this hello world example. Like this:

All my Mobile View Folders just so

Next, either get a User Agent Switcher for your browser, or download a fake iPhone Simulator called iBBDemo2 (it's totally fake, but it'll do). Hit the site, and boom, there you go.

My page in a Fake iPhone

Adding iPad is trivial:

ViewEngines.Engines.AddMobile<RazorViewEngine>("iPad", "Mobile/iPad");

As is Windows Phone 7 if you like:

ViewEngines.Engines.AddMobile<RazorViewEngine>("Windows Phone", "Mobile/WP7");

You get the idea.

More to Come

Since that post, the Live.com team in Ireland that released and supported the original Mobile Device Browser File (MDBF) has stopped producing it. The best source for mobile browser device data is WURFL (that was one of the places that MDBF pulled from.)

While I haven't spoken with the 51degrees.mobi folks (yet), they have an ASP.NET module that sucks information from WURFL and populates the Browser Capabilities object. Remember that everyone's requirements are different. You may not need to know everything about every device. Maybe you want to support iPhones and Desktop and that's it. Or, maybe you need to know the screen width and JPEG capabilities of some obscure old Nokia device. Know what you need before you buy into a detection decision or mobile database. We're working on more mobile recommendations and pest practices on the ASP.NET team and I'll continue share everything I know.

I'll be working with my new teammate Steven Sanderson on this template, maybe getting it into NuGet in the short term, and more baked into the framework in the long term. I'll update the NerdDinner sample - older versions for Mobile, and newer versions for MVC3 and mobile. I'll also try to look at jQuery Mobile and some of the nice JavaScript mobile libraries and improve the default templates. I'll also look at adding "opt out" options, so you can hit a site, get a mobile experience, but click a link that says "give me desktop anyway."

For now, it's a much better proof of concept for you to play with, Dear Reader.

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 bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Back to (Parallel) Basics: Don't Block Your Threads, Make Async I/O Work For You

November 15, 2010 Comment on this post [11] Posted in Back to Basics | Learning .NET | Programming | Source Code
Sponsored By

Stephen Toub is one of my favorite folks at Microsoft. I've asked him questions before, sometimes for myself, sometimes on your behalf, Dear Reader, and I've always received thoughtful and well reasoned answered. Because I believe strongly in Jon Udell's "Conserve Your Keystrokes" philosophy, I always try to get great information out to the community, especially when it's been emailed. Remember, when you slap the keyboard and write an epic email to just a few people, there's millions of people out there that miss out. Email less, blog more. More on this in a moment.

TIP: If you're interested in Patterns for Parallel Programming, run, don't walk, and download the FREE and extensive eBook called, yes, you guessed it, Patterns for Parallel Programming: Understanding and Applying Parallel Patterns with the .NET Framework 4. Yes, that title is long, but it feels shorter if you process it in parallel. Seriously, it's free and there's a C# and Visual Basic version. It's brilliant.
Now, if you're REALLY interested in the topic, go get the book Parallel Programming with Microsoft .NET by Stephen Toub, Ade Miller, Colin Campbell, and Ralph Johnson. The complete book as HTML is also hosted here.

I recently noticed a blog post from my friend Steve Smith where he shares some quick sample code to "Verify a List of URLs in C# Asynchronously." As I know Steve wouldn't mind me digging into this, I did. I started by asking Stephen Toub in the Parallel Computing group at Microsoft.

Steve Smith wanted to verify a list of URLs for existence. This is the basic synchronous code:

private static bool RemoteFileExists(string url)
{
try
{
var request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "HEAD";
var response = request.GetResponse() as HttpWebResponse;
return (response.StatusCode == HttpStatusCode.OK);
}
catch
{
return false;
}
}

Then Steve changed the code to be Parallel using the new Parallel features of .NET 4 as Stephen Toub helped me explain in "Back to Parallel Basics" in April.

var linkList = GetLinks();

Action<int> updateLink = i =>
{
UpdateLinkStatus(linkList[i]); //fetch URL and update its status in a shared list
};
Parallel.For(0, linkList.Count, updateLink);

Using Parallel.For is a really great way to introduce some basic naive parallelism into your applications.

I'm no expert in parallelism (I've read a great whitepaper...) but I asked Stephen Toub if this was the best and recommended way to solve this problem. Stephen responded from a plane using (his words) "email compiled and tested" examples. With his permission, I've included a derivation of his response here in this blog post for my own, and possibly your, edification.

From Stephen:

First, it looked like the author was proposing using a parallel loop to handle this.  That's ok, and certainly easy, but that’s the kind of thing you’d only really want to do in a client application and not a server application.  The issue here is that, while easy, it blocks threads; for a client application, having a few more threads that are blocked typically isn’t a big deal; for a server app, though, if for example you were doing this in response to an incoming ASP.NET or WCF service request, you'd be blocking several threads per request, which will greatly hinder scalability.  Still, to get up and running quickly, and if the extra few threads isn’t problematic, this is a fine way to go. 

Assuming you want you "fan out" quickly and easily and it's OK to block a few threads, you can either use a parallel loop, tasks directly, or Stephen's personal favorite, a PLINQ query, e.g. if I have a function "bool ValidateUrl(string url);", I can use PLINQ to process up to N at a time:

bool [] results = (from url in urls.AsParallel() select ValidateUrl(url)).ToArray();

In this example, PLINQ will use up to N threads from the ThreadPool, where N defaults to Environment.ProcessorCount, but you can tack on .WithDegreeOfParallelism(N) after the AsParallel() and provide your own N value.

If Steve was doing this in a console app, which is likely, then as Stephen points out, that's no big deal. You've usually got threads to spare on the client. On the server side, however, you want to avoid blocking threads as much as you can.

A better solution from a scalability perspective, says Stephen, is to take advantage of asynchronous I/O.  When you're calling out across the network, there's no reason (other than convenience) to blocks threads while waiting for the response to come back. Unfortunately, in the past it's been difficult to do this kind of aggregation of async operations.  We' need to rewrite our ValidateUrl method to be something more like:

public void ValidateUrlAsync(string url, Action<string,bool> callback);

where the method returns immediately and later calls back through the provided callback to alert whether a given URL is valid or not.  We'd then change our usage of this to be more like this. Notice the use of using System.Collections.Concurrent.ConcurrentQueue representing a thread-safe first in-first out (FIFO) collection, and CountdownEvent, that represents a synchronization primitive that is signaled when its count reaches zero.

using(var ce = new CountdownEvent(urls.Length))

{
var results = new ConcurrentQueue<Tuple<string,bool>>();

Action callback = (url,valid) =>
{
results.Enqueue(Tuple.Create(url,valid));
ce.Signal();
};

foreach(var url in urls) ValidateUrlAsync(url, callback);

ce.Wait();
}

Assuming ValidateUrlAsync is written to use async, e.g. (you'd really want the following to do better error-handling, but again, this is email-compiled):

public void ValidateUrlAsync(string url, Action<string,bool> callback)
{
var request = (HttpWebRequest)WebRequest.Create(url);
try
{
request.BeginGetResponse(iar =>
{
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.EndGetResponse(iar);
callback(url, response.StatusCode == HttpStatusCode.OK);
}
catch { callback(url, false); }
finally { if (response != null) response.Close(); }
}, null);
}
catch { callback(url, false); }
}

This example would then this would end up only blocking the main thread launching all of the requests and then blocking waiting for all of the responses, rather than blocking one thread per request.  With a slight change, we could also make the launcher async, for example:

public static void ValidateUrlsAsync(string [] urls, Action<IEnumerable<Tuple<string,bool>> callback)
{
var ce = new CountdownEvent(urls.Length);
var results = new ConcurrentQueue<Tuple<string,bool>>();
Action callback = (url,valid) =>
{
results.Enqueue(Tuple.Create(url,valid));
if (ce.Signal()) callback(results);
};
foreach(var url in urls) ValidateUrlAsync(url, callback);
}

Still, this is all really complicated, and much more difficult than the original one-liner using PLINQ. 

This is where Tasks and the new Async CTP come in really handy. Imagine that instead of

void ValidateUrlAsync(string url, Action<bool> callback);

we instead had

Task<bool> ValidateUrlAsync(string url);

The Task<bool> being returned is much more composable, and represents the result (both the successful completion case and the exceptional case) of the async operation. 

BETA NOTE: It's not possible to have both ASP.NET MVC 3 and the Async CTP installed at the same time. This is a beta conflict thing, it'll be fixed, I'm sure.

If we had such an operation, and if we had a Task.WhenAll method that took any number of tasks and returned a task to represent them all, then we can easily await all of the results, e.g.

bool [] results = await Task.WhenAll(from url in urls select ValidateUrlAsync(url));

Nice and simple, entirely asynchronous, no blocked threads, etc. 

(Note that in the Async CTP, Task.WhenAll is currently TaskEx.WhenAll, because since it was an out-of-band CTP we couldn't add the static WhenAll method onto Task like we wanted to.)

With the Async CTP and the await keyword, it's also much easier to implement the ValidateUrlAsync method, and to do so with complete support for exception handling (which I didn't do in my previous example, i.e. if something fails, it doesn't communicate why).

public async Task<bool> ValidateUrlAsync(string url)
{
using(var response = (HttpWebResponse)await WebRequest.Create(url).GetResponseAsync())
return response.StatusCode == HttpStatusCode.Ok;
}

Even without the Async CTP, though, it's still possible to implement ValidateUrlAsync with this signature.

Notice the use of System.Threading.Tasks.TaskCompletionSource. From MSDN:

In many scenarios, it is useful to enable a Task (Of(TResult)) to represent an external asynchronous operation. TaskCompletionSource (Of( TResult)) is provided for this purpose. It enables the creation of a task that can be handed out to consumers, and those consumers can use the members of the task as they would any other.

public Task<bool> ValidateUrlAsync(string url)
{
var tcs = new TaskCompletionSource<bool>();
var request = (HttpWebRequest)WebRequest.Create(url);
try
{
request.BeginGetResponse(iar =>
{
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.EndGetResponse(iar);
tcs.SetResult(response.StatusCode == HttpStatusCode.OK);
}
catch(Exception exc) { tcs.SetException(exc); }
finally { if (response != null) response.Close(); }
}, null);
}
catch(Exception exc) { tcs.SetException(exc); }
return tsc.Task;

}

So, with this method, even without the Async CTP, we can use existing .NET 4 support to handle this relatively easily:

Task.Factory.ContinueWhenAll(
(from url in urls select ValidateUrlAsync(url)).ToArray(),
completedTasks => { /* do some end task */ });

Now, using just what comes with .NET 4 proper I get the best of all worlds.

Big thanks to Stephen Toub. There's lots of new high- and low-level constructs for Task creation, Threading, and Parallelism in .NET 4. While the naive solution is often the right one, the components we have to work with in .NET 4 (and the even newer ones in the Visual Studio 2010 Async CTP adding the 'await' and 'async' keywords) will give you surprisingly fine-grained control over your multi-threaded parallel systems without a whole lot of code.

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 bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Streaming Live or On-Demand Video from IIS7 to iOS Devices (iPhone/iPad) and Silverlight Clients

November 08, 2010 Comment on this post [20] Posted in IIS | Silverlight
Sponsored By

DSC_0001 Earlier this year I posted on Installing and Setting Up and Encoding for IIS 7 Smooth Streaming and Silverlight. The general idea was to show folks that it's actually pretty easy to setup Smooth Streaming for Silverlight. Of course, not everyone uses Silverlight, and lots of folks (myself included) have iPhones and iPads.

I've long been singing the praises of IIS7. If you're on IIS6, get off it. IIS7 is the #1 reason to get on Windows Server 2008R2, IMHO. Mostly because of the crapload of awesome (CoA) that keeps coming out of the IIS team. Many of these modules aren't on the radar of the average developer. I think that Web Platform Installer helps some, but still, I'd like to get the word about some of this free stuff like the Web Farm Framework, the all new FTP 7.5, Bit Rate Throttling, and my favorite, Application Request Routing (which I use in my robot).

Anyway, last week IIS released IIS Media Services 4.0 that you can download now. It's free if you have IIS on your machine. They also released a number of things this summer like the Transform Manager 1.0 Alpha.

Getting existing Video from IIS7 to iOS devices elegantly

Why do I care? Well, I'm sitting here looking at an IPhone 3G, an iPhone 4 and an iPad on my desk. Many of your customers have these devices. How do you get video to them, cleanly, quickly, easily and from a single source file?

Turns out there's features in IIS7 and Media Services that target iOS directly. Yes, it's true. Cats and dogs, living together, mass hysteria. Get your snowballs out, hell has frozen over and the slopes look sweet.

On-Demand Encoded Video for iOS and Silverlight

First, I've got this file on my hard drive called ScottGuHDTest1. It's a picture in picture (PIP) test that ScottGu and I did a while ago while looking into new ways to deliver educational video content. Consider this my stock video for this example.

Windows Media Player - ScottGuTest Video Frame

If I want to deliver this content to the web over Silverlight Adaptive Smooth Streaming PLUS I want it to look great on an iPhone or iPad regardless of bandwidth, and I want it all done automatically, I'll need these things.

imageFor existing videos or videos you're making on a regular basis, the idea is that you'll just drop them in a folder and a task will automatically kick off. You have total control over everything, to be clear, but for the out of the box example, I'm going to drop my ScottGu WMV into a folder and two things will pop out:

  • A folder with HTML page with a Silverlight media player that supports Smooth Streaming. The ScottGu video will be encoded in various bit rates and be all set for adaptive Smooth Streaming
  • A folder with MPEG-2 TS format files with .m3u8 manifests that conform to the Apple Live Streaming Protocol.

Here's what you do. Once you've installed the Transform Manager, it'll show up in your IIS Manager as a node. You'll setup account credentials (an account with some power, as it'll need to run stuff and copy files into IIS folders. I used my own. Naughty.)

Now, go to Job Templates and notice there are a bunch of built-in templates. You can create whatever you want to encode video in whatever way makes you happy.

I double-clicked on them and checked all the paths and stuff. You'll need to change the templatePath property to decide on your input and output directories.

 Edit Job Template

I have my input folder here, in c:\inetpub\media\Transform Manager, and there's sub-folders beneath that as seen in this screenshot. Notice that tasks can do multiple things, so mine takes an input file and "fans out" into multiple tasks.

 Transform Manager

If I drop my ScottGu file in, a task immediately starts up (it's watching the folder) and I can see the progress from the IIS manager. Here it is in the middle of encoding.

Internet Information Services (IIS) Manager

The Transforms Manager has lots of interesting parameters that you can set, basically using simple template expansion. It'll make a folder with the Date and Time and a Unique Number, but again, you control all this.

Now I have two folders, one for the Silverlight format...

20101108231223_22BCFBBAA9864545A8864DEDC41E6A87

...and one for the Apple format:

 TS

I'll add a HTML page for an iOS device to hit. NOTE: You'll be pointing the iPhone/iPad to this m3u8.ism file and it'll decide what streams to watch:

<html>
<head>
<title>iPhone page</title>
</head>
<body>
<h1>Encoded stream</h1>
<video width="640"
height="480"
src="ScottGu720pTest1-m3u8-aapl.ism/manifest(format=m3u8-aapl).m3u8"
poster="ScottGuSplashPage.png"
autoplay="true"
controls="true" >Live</video>
</body>
</html>

I've also added splashscreen "poster" that's just a PNG frame from the video. Here's a photograph of my iPhone 3G, iPad and iPhone 4 with the video...

DSC_0001

...and here it is in Silverlight. Note the chart showing the smooth streaming bandwidth adjusting.

ScottGu720pTest1 - Windows Internet Explorer

Cool, that was easy. How about LIVE video streaming to both iPhone/iPad clients and Web Browsers?

Live Streaming Video from IIS7 to iOS devices and Silverlight

Doing LIVE Streaming video is pretty easy also. You start with IIS Media Services 4 and Expression Encoder 4 Pro. The encoder will dynamically transcode the incoming video into the right format for outputting to Apple Devices as well as Silverlight clients. It'll also archive the video for later.

Start by selecting Live Smooth Streaming, and create a Publishing Point.

image

Make sure to select "Enable Output to Apple Mobile Digital Devices" and also Enable iOS 3.0 Compatibility for older phones.

 Add Publishing Point

In the Encoder I can also add multiple live sources (multiple cameras, USB, Firewire, whatever) as well as recorded sources (we'll be right back, advertisements, whatever) and then cue them up appropriately.

Fire up Encoder 4 and select Live Broadcasting Project.

Load a new project

Add a Live Source (I used my Webcam, although Firewire and other video sources work great), then select the H.264 Encoding you want. I chose "H.264 IIS Smooth Streaming iPad Wifi"

image

Under Output, enter in your Publishing Point. This is a file on your IIS server. Mine was http://hexpower7/livestreaming/magicalgoodness.isml because that's the folder I started the Publishing Point in.

Click Cue and cue your live source, then Start the broadcasting.

Create an HTML page like before, this time using the ISML file.

<html>
<head>
<title>iPhone Live Baby!</title>
</head>
<body>
<h1>Live Stream</h1>

<video width="640"
height="360"
src="magicalGoodness.isml/manifest(format=m3u8-aapl).m3u8"
autoplay="true"
controls="true" >Live</video>
</body>
</html>

That's it! Here's a shot of me Encoding while streaming to an iPhone 4.

Me streaming LIVE locally to myself on my iPhone

Plus, just for fun (and to make a point) I can take the Silverlight Player from the first step at the beginning of the blog post (or make my own with the Silverlight Media Framework 2.2) and just point its source to my live streaming endpoint: "<MediaSource>MagicalGoodness.isml%5CManifest</MediaSource>" and I'm using the same live video feed to serve video to my Silverlight client, my iPhone and iPad. Also, the new Silverlight Media Framework 2.2 supports Windows Phone 7 as well. ;)

 ScottGu720pTest1 - Windows Internet Explorer (2)

And, it's not just a LIVE stream, but it's a live stream with DVR support, so folks can pause, back up, etc and it's all being archived to my server.

image

All this about a half an hour of setup. I was pretty impressed at how easy it was and with a little planning, I think I could this up for a local User Group with multiple lives streams and broad client support with some hardware a few hours. You might think about trying it for your local event as a practice, Dear Reader.

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 bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

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