November 2007 - Posts

In my years of web development, there's always been one sticky point - Images.  Each time I being writing an application, I can't help but think - there must be a better way than this.  I've found many different ways over the years, but none of really stuck out as "better".  The problem really breaks down to - how do you work with dynamic images in a databound web application?  In order to find the best solution for this, we need to break this question down into two smaller questions.

1) Do images belong in your database, or should they remain on your file system.

2) How do you dynamically display the image corresponding to the data on the page.

I'm sure you're used to hearing this by now - the answer to the first question is "it depends".  Unless you have an easy way to insert and update images in your database, there's a bit of overhead you'll have to struggle with if you want to serve up images directly from a DBMS.  For small quick applications I would generally recommend using the files system, and use known file names to link images to data.  However, if you're working on a large scale solution, A direct database streaming solution is a good investment.  Now, I'm not a SQL expert, so if any of you are - I'd love to hear the pros and cons of this from a SQL DBAdmin's perspective.   

No matter what you decide, the first part to the solution will be the same - embed an Image into your page.  We all know how to do this in a simple ASP.NET page, but what if you need to display images in a list using a Repeater or DataGrid with Templating capabilities?  The Infragistics UltraWebGrid just like the Microsoft GridView has a "TemplateColumn" which can be used to customize the view of individual cells in that column.  Simply add an asp:image tag inside of the template, and then use some declarative databinding to bind the ImageUrl property.  Here's an example column for the WebGrid

    <igtbl:TemplatedColumn BaseColumnName="PhotoUrl" IsBound="True" Key="PhotoUrl">

        <Header Caption="Photo">

            <RowLayoutColumnInfo OriginX="1" />

        </Header>

        <Footer>

            <RowLayoutColumnInfo OriginX="1" />

        </Footer>

        <CellTemplate>

            <asp:Image runat="server" ID="image1" ImageUrl="<%# Container.Value %>" />

        </CellTemplate>

    </igtbl:TemplatedColumn>

Notice that the ImageUrl is being bound to the Container.Value, which is the value of the PhotoUrl field.  This value can either point directly to an image url, or in my case I pointed it towards an HttpHandler.  The PhotoUrl field is not an actual field in my table, but rather a virtual field that I create through my Sql Query.  Here's an example of how I created mine:

SELECT FirstName, 'imgstrm.ashx?id=' + CAST(EmployeeID AS Varchar(16)) AS PhotoUrl, Photo, EmployeeID FROM Employees

In the query above, I'm pulling data out of the classic Northwind database, using the Employees table.  Notice that I'm affixing 'imgstrm.ashx?id=' to the front of the string value.   imgstrm.ashx is my HttpHandler, and I'm using ?id= to pass in a QueryString value along with the request.  HttpHandlers are a great way to manage images because they can be used to write the image bits directly to the output stream, without the overhead of an ASPX page.  In this example, the handler simply looks at the value of the id QyeryString parameter and writes the appropriate bytes to the OutputStream.  If you're instead going with a file system solution, you can slightly modify the code in the HTTP handler to redirect the response to the actual ImageUrl.  By pointing all of your requests through the handler, you can easily switch between a file system or an embedded database solution, without having to modify your code. 

Streaming the bytes out is the easy part, simply grab your byte[] out of your database, and write it to the output stream of the response object.  Don't forget to set the ContentType of the response to match your image format.  And if you're using a .PNG format, you'll want to load the bytes into a memory stream before writing them to the OutputStream. 

You can download the entire source for this project here.  Note, this was built with NetAdvantage 2007 Volume 3.  If you do not have NetAdvantage installed, simply replace the UltraWebGrid with a GridView or a Repeater, and remove the references from the web.config.

Whenever working with an image generator in an ASP.NET applications, you have difficult tradeoffs that you must face.  Usability Vs. Scalability.  Application Security Vs. Information Security, etc.  With the Infragistics WebChart many of these scenarios are taken care of with options to use Session State or the File system to temporarily store images before they are streamed out to the browser.   However, there comes a time when neither of these options will work.

The Problem

Session state has the annoying ability to fall apart should the app domain recycle during the session.  There are ways of working around this, but of course they all have tradeoffs as well.  Because of this, in some scenarios using session state my not be an option for your application. 

Disk IO on a heavily utilized web server can be a precious commodity.  Relying on the constant generation of files whose lifetime may only be a couple of seconds, can become impractical in certain scenarios.

Streaming a chart out directly to the browser upon receiving a request is the optimal solution.  Unfortunately, this isn't how images in an HTML page work.  In normal operation a page is first requested, and upon parsing the response the browser opens another request for the image whose URL was specified in the markup.  The fact that this must happen in two stages complicates things, since in order to stream out the image you must be able to generate it, which means you must be able to create a chart instance or somehow pass the required information to create it in your URL somehow. 

The Solution

To tackle this problem, a solution must be found which enables the chart to be 'recreated' when it comes time to stream it out.  This can be accomplished by hosting the chart inside of a UserControl.  UserControls are powerful objects in ASP.NET.  They can be dynamically loaded and added to existing WebForms, or otherwise processed.  In this solution we will dynamically create a UserControl which contains the instance of our chart.  During the initial phase when the page is rendered, we will add the UserControl to our web form.  In phase 2, we will use a specially crafted URL which points to an HTTP handler to dynamically create the UserControl, and stream the image out to the response stream. 

The Tradeoff

Like everything in life, there's a tradeoff.  This drawback to this solution is that it makes dynamic modifications of the chart difficult.  Because the chart must live outside of the scope of the Page, any communication between the page and the chart must be done through querystring parameters.  However, when faced with no other alternatives, using a querystring doesn't seem like such a hard thing to live with.

The Code

You can download a complete example of this code here.  The code is documented, and there is a quickstart.txt to help you understand how to use the solution.

I can't believe Dev Connections is already over.  Perhaps it was the self induced memory loss that's saving me from remembering my horrible luck in the casino, but the show did seem to speed by.  All in all, I thought it was a good show - good, but not the best.  I'm not docking points for technical content, I thought that was all superb.  It was the peripherals that I've seen done better in the past.  My biggest gripe was the lack of available refreshments.  Manning the booth for most of the conference, you quickly get a dry throat and there usually wasn't a bottle of water in sight. When I went to TechEd orlando, it seemed that there were more water coolers there than people.  I would suggest making water available for dev connections.  And what was with the air in that hotel?  I've never before had such a dry throat and chapped lips hit so suddenly!  I guess it's the extra oxygen pumped in the air to keep everyone alert.

I met a bunch of people at the booth, so many at times that lines stretched around the corner.  The best part was, the traffic wasn't just for people looking to get stamps.  If you weren't at the show, there was a drawing for a Harley, but in order to enter you had to get a card stamped by every vendor.  That tends to drive some extra traffic, but it also puts people whose only interest is winning the Harley, in your booth.  That's wasn't such a terrible thing, as I got to meet some great people who's paths I may not have otherwise crossed.

I also got to spend a lot of time with my coworkers, some of whom I've never really seen outside of the office.  I can now say, I work with the greatest group of people.  We managed to have fun whether standing around in the booth, or even just eating breakfast together.  Not only are they skilled at their jobs, but they're skilled at life too, and I'm glad to see that. 

At the end of the show, there was a Best Of Connections award ceremony.  Infragistics got nominated for NetAdvantage for .NET, and made it to the final round.  I was in the middle of explaining to a gentleman why Infragistics was #1 in the UI tools space, when I had to excuse myself to go and represent Infragistics (and accept an award should we win) - and we won!!!  Before I could even get back to the booth, another customer carried the news back to my coworkers.   I came back with the award in my hand moments later and I think the smile on all of our faces made everything worth it.  The dry mouth, chapped lips, the gambling debt, all disappeared as we looked at the award.  I didn't get to make an acceptance speech there, so consider this it: 

Thanks to everyone who made this possible;  the customers who have always been there to tell us what to do, and sometimes where to go ;)  I truly appreciate the time you take to share the good, the bad, and sometimes the ugly with us.  And thanks to the skilled developers, QA and DS staff, Docs team, and the Visual Design team at Infragistics who together create the most impressive product on the market.  Now before my head gets too big, there's always room for improvement.  Being the best in the crowd doesn't mean you can't be better.  I'm lucky to work with a bunch of over achievers and workaholics who are always trying to beat themselves - and that's just what we'll continue to do. 

Viva Las Vegas, or so they say.  I'm here at Dev Connections thru Wednesday;  I'll be spending most of my time at the Infragistics booth, so if you're at the show stop by the vendor area and introduce yourself.  

I got here yesterday, and arriving on a Sunday during football season seemed pretty exciting.  Unfortunately, the novelty of it all wore off with each $6 beer.  Yes, that's my only complaint so far, but it's an important one. 

Now it's time to adapt to the 4 hour time change.  It's only a 3 hour difference from EST to PST, but counting in the Daylight Savings Time change on Sunday, it combines to a 4 hour diff.  Luckily, it just means I get tired earlier and don't spend my entire evening throwing my money at the casino.  It also means I'll have no problem getting to early sessions or keynotes. 

More Posts