Scott Hanselman

The Weekly Source Code 51 - Asynchronous Database Access and LINQ to SQL Fun

March 2, '10 Comments [25] Posted in ASP.NET | ASP.NET MVC | LINQ | Open Source | Source Code
Sponsored By

You can learn a lot by reading other people's source code. That's the idea behind this series, "The Weekly Source Code." You can certainly become a better programmer by writing code but I think good writers become better by reading as much as they can.

I was poking around in the WebFormsMVP project's code and noticed an interesting pattern.

You've seen code to get data from a database and retrieve it as an object, like this:

public Widget Find(int id)
{
Widget widget = null;
widget = (from w in _db.Widgets
where w.Id == id
select w).SingleOrDefault();
return widget;
}

This code is synchronous, meaning basically that it'll happen on the same thread and we'll wait around until it's finished. Now, here's an asynchronous version of the same code. It's a nice combination of the the new (LINQ, in this case, LINQ to SQL) and the older (DataReaders, etc). The LINQ (to SQL) query is in query, then they call GetCommand to get the underlying SqlCommand for that query. Then, they call BeginExecuteReader on the SqlCommand which starts asynchronous execution of that command.

SqlCommand _beginFindCmd = null;

public IAsyncResult BeginFind(int id, AsyncCallback callback, Object asyncState)
{
var query = from w in _db.Widgets
where w.Id == id
select w;
_beginFindCmd = _db.GetCommand(query) as SqlCommand;
_db.Connection.Open();
return _beginFindCmd.BeginExecuteReader(callback, asyncState, System.Data.CommandBehavior.CloseConnection);
}

public Widget EndFind(IAsyncResult result)
{
var rdr = _beginFindCmd.EndExecuteReader(result);
var widget = (from w in _db.Translate<Widget>(rdr)
select w).SingleOrDefault();
rdr.Close();
return widget;
}

When it's done, in this example, EndFind gets called and they call DataContext.Translate<T> passing in the type they want (Widget) and the source, the DataReader retrieved from EndExecuteReader. It's an asynchronous LINQ to SQL call.

I found it clever so I emailed my parallelism friend and expert Stephen Toub and asked him if this was any or all of the following:

a. clever

b. necessary

c. better done with PFX/TPL (Parallel Extensions to the .NET Framework/Task Parallel Library)

Stephen said, in his own get-down-to-business fashion:

a) It's a standard approach to converting a LINQ query to a command to be executed with more control over how it's executed.  That said, I don't see it done all that much, so in that capacity it's clever.

b) It's necessary to run the query asynchronously; otherwise, the call to MoveNext on the enumerator will block. And if ADO.NET's MARS support is used (multiple asynchronous result sets), you could have multiple outstanding operations in play.

c) TPL can't improve upon the interactions with SQL Server, i.e. BeginExecuteReader will still need to be called.  However, TPL can be used to wrap the call such that you get a Task<Widget> back, which might be a nicer API to consume.  Once you have it as a Task, you can do useful things like wait for it, schedule work for when its done, wait for multiple operations or schedule work when multiple operations are done, etc.

One other thing that's interesting, is the WebFormsMVP project's PageAsyncTaskManagerWrapper:

namespace WebFormsMvp.Web
{
/// <summary>
/// Represents a class that wraps the page's async task methods
/// </summary>
public class PageAsyncTaskManagerWrapper : IAsyncTaskManager
{
readonly Page page;

/// <summary />
public PageAsyncTaskManagerWrapper(Page page)
{
this.page = page;
}

/// <summary>
/// Starts the execution of an asynchronous task.
/// </summary>
public void ExecuteRegisteredAsyncTasks()
{
page.ExecuteRegisteredAsyncTasks();
}

/// <summary>
/// Registers a new asynchronous task with the page.
/// </summary>
/// <param name="beginHandler">The handler to call when beginning an asynchronous task.</param>
/// <param name="endHandler">The handler to call when the task is completed successfully within the time-out period.</param>
/// <param name="timeout">The handler to call when the task is not completed successfully within the time-out period.</param>
/// <param name="state">The object that represents the state of the task.</param>
/// <param name="executeInParallel">The vlaue that indicates whether the task can be executed in parallel with other tasks.</param>
public void RegisterAsyncTask(BeginEventHandler beginHandler, EndEventHandler endHandler, EndEventHandler timeout, object state, bool executeInParallel)
{
page.RegisterAsyncTask(new PageAsyncTask(beginHandler, endHandler, timeout, state, executeInParallel));
}
}
}

They made a nice wrapper for these existing System.Web.UI.Page methods and they use it like this, combined with the asynchronous LINQ to SQL from earlier:

AsyncManager.RegisterAsyncTask(
(asyncSender, ea, callback, state) => // Begin
{
return widgetRepository.BeginFindByName(e.Name, callback, state);
},
result => // End
{
var widget = widgetRepository.EndFindByName(result);
if (widget != null)
{
View.Model.Widgets.Add(widget);
}
},
result => { } // Timeout
, null, false);
AsyncManager.ExecuteRegisteredAsyncTasks();

They fire off their task, which then does its database work asynchronously, and then it all comes together.

I'll leave (for now) the wrapping of the APIs to return a Task<TResult> as an exercise for the reader, but it'd be nice to see if this pattern can benefit from the Task Parallel Library or not.

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

Put Missing Kids on your 404 Page - Entirely Client-Side Solution with YQL, jQuery, and MSAjax

February 25, '10 Comments [51] Posted in ASP.NET | ASP.NET Ajax | Open Source
Sponsored By

image I noticed a post over at a blog called "The other side of the moon" where the author suggests that we put pictures and details of missing children on on 404 pages. It's a simple and brilliant idea. Millions of 404s are delivered every day. We are reporting on missing pages, but not on missing children. He includes a simple PHP solution. I set out to create an ASP.NET solution, but then realized that a server-side solution wasn't really necessary.

Could I do it all on the client side? This way anyone could add this feature to their site, regardless of their server-side choice. This could make the solution much more palatable to folks who may not be into .NET.

Here's what I came up with. You can see it in action if you request a file that doesn't exist from my site, like http://www.hanselman.com/foo.aspx.

The file is called missingkids404.html and it's just static html. You will need to configure your webserver to serve this page when it needs to serve a 404. For me, as I do run ASP.NET and IIS, I needed to add this to my web.config in the System.Web section:

<customErrors mode="On"> 
<error statusCode="404" redirect="/missingkids404.html" />
</customErrors>

The file, in its entirety, is this:

<html>
<head>
<title>Missing Kids 404</title>
<style type="text/css">
.sys-template { display:none; }
.missingkid { clear:both; }
</style>
<script src="http://ajax.microsoft.com/ajax/beta/0911/Start.js" type="text/javascript"></script>
</head>
<body>
<script type="text/javascript">
Sys.require([Sys.components.dataView, Sys.scripts.jQuery], function() {
$("#missingkids").dataView();

var statecode = "ZZ";
var dataurl = "http://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20From%20xml%0D%0A%20Where%20url%3D'http%3A%2F%2Fwww.missingkidsmap.com%2Fread.php%3Fstate%3D" + statecode + "'%0D%0A&format=json&callback=?";
var data = $.getJSON(dataurl, function(results){
Sys.get("$missingkids").set_data(results.query.results.locations.location);
}
);
});

function getSrc(url) {
var lastIndex = url.lastIndexOf('=');
return url.substring(lastIndex+1);
}
</script>

<p>
<strong>Sorry, the page you're trying to find is missing.</strong>
</p>
<p>
We may not be able to find the page, but perhaps you could help find one of these missing children:
</p>

<div id="missingkids" class="sys-template">
<div class="missingkid">
<img sys:width="60" sys:align="left" sys:src="{binding medpic, convert=getSrc}" />
<strong>{{firstname + " " + lastname}}</strong>, Age: {{age}} from
{{city}}, {{st}}</br>
Contact: {{policeadd}} at {{policenum}}<br/>
<br/>
</div>
</div>
</body>
</html>

I'm using the standard {{token}} syntax as well as one custom syntax with a convert=callback so I can pre-process the data. The source data feed includes an unfortunate chunk of html, rather than a direct link to a picture. I need to strip everything after the last equals sign (=) in order to get the image src URL. That method is called getSrc, and the binding looks like:

<img sys:width="60" sys:align="left" sys:src="{binding medpic, convert=getSrc}" />

If this is useful, the next step is to add geolocation. You can look at http://www.hanselman.com/missingkids404geo.html for the beginnings of a geo-location aware one. The open issue right now is that the missing kids feed works only in the US, Canada and the UK. I would need to figure out now to determine the two-letter STATE code from the geolocation API, which doesn't provide codes in that way. Worst case scenario, I'd have a lookup table of state names to abbreviations.

Enjoy! Thoughts?

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 202 - A different way to do ASP.NET WebForms with WebFormsMVP

February 21, '10 Comments [10] Posted in ASP.NET | ASP.NET MVC | Open Source | Podcast
Sponsored By

2008-GrafittiSmall My two-hundred-and-second podcast is up. I sit down (in my home, actually) with Tatham Oddie to talk about the WebFormsMVP open source project created by he and Damian Edwards.  What does it add?  Can we have the best of both worlds, convenience, controls and testability?

Subscribe: Subscribe to Hanselminutes Subscribe to my Podcast in iTunes

Download: MP3 Full Show

Links from the 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.

I want to add a big thanks to Telerik. Without their support, there wouldn't be a Hanselminutes. I hope they, and you, know that. Someone's gotta pay the bandwidth. Thanks also to Carl Franklin for all his support over these last 4 years!

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, TFS, 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 twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Hanselminutes Podcast 201 - The Making of a Open Source MonoTouch iPhone app with Chris Hardy

February 21, '10 Comments [0] Posted in Mono | Open Source | Podcast
Sponsored By

4248245993_aa337e2578_o My two-hundred-and-first podcast is up. Two Englishmen in a row? What a sellout I am. This week I chat with Chris "ChrisNTR" Hardy, an ASP.NET programmer by day who writes C# code for the iPhone by night. He took it upon himself to answer a tweet from me and write the beginnings of a "Hanselminutes iPhone Application." How did he do it? Now we just gotta get it in the App Store, and I need to start on a Windows Phone 7 version. ;)

Subscribe: Subscribe to Hanselminutes Subscribe to my Podcast in iTunes

Download: MP3 Full Show

Links from the 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.

I want to add a big thanks to Telerik. Without their support, there wouldn't be a Hanselminutes. I hope they, and you, know that. Someone's gotta pay the bandwidth. Thanks also to Carl Franklin for all his support over these last 4 years!

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, TFS, 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 twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

The Weekly Source Code 50 - A little on "A generic error occurred in GDI+" and trouble generating images on with ASP.NET

February 18, '10 Comments [12] Posted in ASP.NET | Source Code
Sponsored By

I got a nice little Yellow Screen of Death (YSOD) error on some code running under IIS that worked fine when running on the VS Developer Web Server. The error was "A generic error occurred in GDI+" and you know that if an error is generic, it's sure in the heck not specific.

My little application takes an overhead map that's stored in a local file, does some calculations from user input and draws an X on the map, then returns the resulting dynamically generated image.

There's basically three ways to do images on the server side. Use Native APIs and Interop, which only works in full trust, use System.Drawing, which "isn't supported" or use WPF on the server side, which also, ahem, isn't officially supported. I'm still trying to figure out why, but just to be clear, I used System.Drawing in extremely high traffic sites with no problems. As long as paid close attention to my unmanaged resources, I have never had a problem. I've heard anecdotally of people having trouble with GDI+ (System.Drawing) and switching over to WPF and having no problem with that. As with all things, test what you're doing. There's even some ASP.NET Controls on CodePlex that might help.

Now this post can't answer ALL reasons you're getting "a generic error occurred in GDI+" but it can answer mine. In my particular case (and I think this is the most common mistake) I was saving the composited image as a PNG.

First, I'll show you a little chunk of a code from 5 years ago that took two images and built a single image from them.

public class SomeCheckImageHandler : IHttpHandler
{
//some stuff snipped

public SomeCheckImageHandler(){}

public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "image/jpg";

//some stuff snipped
GetCheckImageRequest req = new GetCheckImageRequest();
//some stuff snipped, get the params from the QueryString
GetCheckImageResponse res = banking.GetCheckImage(req);

//some stuff snipped
if (res.ImageBack != null)
{
//merge them into one image
using(MemoryStream m = new MemoryStream(res.BackImageBytes))
using(Image backImage = System.Drawing.Image.FromStream(m))
using(MemoryStream m2 = new MemoryStream(res.BrontImageBytes))
using(Image frontImage = System.Drawing.Image.FromStream(m2))
using(Bitmap compositeImage = new Bitmap(frontImage.Width,frontImage.Height+backImage.Height))
using(Graphics compositeGraphics = Graphics.FromImage(compositeImage))
{
compositeGraphics.CompositingMode = CompositingMode.SourceCopy;
compositeGraphics.DrawImageUnscaled(frontImage,0,0);
compositeGraphics.DrawImageUnscaled(backImage,0,frontImage.Height);
compositeImage.Save(context.Response.OutputStream, ImageFormat.Jpeg);
}
}
else //just show the front, we've got no back
{
using(MemoryStream m = new MemoryStream(frontImageBytes))
using(Image image = System.Drawing.Image.FromStream(m))
{
image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
}
}
}
}

This code generated a JPEG. No problems, runs fine even today. Now, the code I was working on created a PNG and when you create a PNG you need a seekable stream. This little sample uses the BaseHttpHandler Phil and I made.

Note the highlighted lines in this sample. (You'll see the highlights if you view this post on my blog directly, rather than from RSS.)

The Trick for making PNGs: Without the extra intermediate MemoryStream to save to, you won't be able to make PNGs. You can't image.Save() a PNG directly to Response.OutputStream.

namespace FooFoo
{
public class MapHandler : BaseHttpHandler
public override void HandleRequest(HttpContext context)
{
string filename = context.Server.MapPath(".") + @"\images\newmap2000.jpg";

using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
using (System.Drawing.Image image = System.Drawing.Image.FromStream(fs))
{
using (Graphics g = Graphics.FromImage(image))
{

BrickFinderDrawing drawer = new BrickFinderDrawing(...somedata...);
drawer.DrawBrick(g);
drawer.DrawName(g);

using (MemoryStream stream = new MemoryStream())
{
image.Save(stream, ImageFormat.Png);
stream.WriteTo(context.Response.OutputStream);
}
}
}
}

}

public override bool RequiresAuthentication
{
get { return false; }
}

public override string ContentMimeType
{
get { return "image/png"; }
}

public override bool ValidateParameters(HttpContext context)
{
return true;
}
}
}

Just a little something I wish I remembered when I hit the YSOD. Sure glad I have a blog to put this kind of stuff on! ;)

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

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