Posts tagged .NET
GDI+ Image.FromFile has a problem – here’s how to fix it
Jul 30th
In GDI+ you can call Image.FromFile to load an image from a file. BUT there are several issues with this call, the biggest being that GDI+ will keep the file open long after you are done with it. Here is an image loader that gets around this issue.
If you are running a high volume web site, and your images are on a SAN you’ll find this technique necessary to prevent an eventual exhaustion of filehandles.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.IO;
using System.Data;
namespace Utility
{
public static class ImageLoader
{
// This isn’t going to help much – you’ll run out of memory anyway on very large images – but if you are keeping several in memory it might …
public const int MaximumImageDimension = 10000;
///
/// Method to safely load an image from a file without leaving the file open,
/// also gets the size down to a manageable size in the case of HUGE images
///
/// An Image – don’t forget to dispose of it later
public static Image LoadImage (string filePath)
{
try
{
FileInfo fi = new FileInfo(filePath);
if (!fi.Exists) throw new FileNotFoundException(“Cannot find image”);
if (fi.Length == 0) throw new FileNotFoundException(“Zero length image file “);
// Image.FromFile is known to leave files open, so we use a stream instead to read it
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
if (!fs.CanRead) throw new FileLoadException (“Cannot read file stream”);
if (fs.Length == 0) throw new FileLoadException(“File stream zero length”);
using (Image original = Image.FromStream(fs))
{
// Make a copy of the file in memory, then release the one GDI+ gave us
// thus ensuring that all file handles are closed properly (which GDI+ doesn’t do for us in a timely fashion)
int width = original.Width;
int height = original.Height;
if (width == 0) throw new DataException(“Bad image dimension width=0″);
if (height == 0) throw new DataException(“Bad image dimension height=0″);
// Now shrink it to Max size to control memory consumption
if (width > MaximumImageDimension)
{
height = height * MaximumImageDimension / width;
width = MaximumImageDimension;
}
if (height > MaximumImageDimension)
{
width = width * MaximumImageDimension / height;
height = MaximumImageDimension;
}
Bitmap copy = new Bitmap(width, height);
using (Graphics graphics = Graphics.FromImage(copy))
{
graphics.DrawImage(original, 0, 0, copy.Width, copy.Height);
}
return copy;
}
}
}
catch (Exception ex)
{
ex.Data.Add(“FileName”, filePath);
throw;
}
}
}
A simple redirect route handler for ASP.NET 3.5 routing
Apr 20th
ASP.NET 3.5 Routing is a very powerful tool not just for registering routes for newer ASP.NET MVC applications but also for adding SEO friendly routes to older Webforms (ASPX) applications, or for routing multiple URLs to a single page. But that’s not all it can do. You can create your own IRouteHandler and then have complete control over what to do with any incoming HttpRequest.
Here for example is a way to do a permanent redirect when a given route is matched. To use it you might, for example, do:-
routes.Add(new Route("sample.aspx", new RedirectRouteHandler("/home/start")));
Here is the RedirectRouteHandler that can turn any request into a 301 redirect for you:-
/// <summary>
/// Redirect Route Handler
/// </summary>
public class RedirectRouteHandler : IRouteHandler
{
private string newUrl;
public RedirectRouteHandler(string newUrl)
{
this.newUrl = newUrl;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new RedirectHandler(newUrl);
}
}
/// <summary>
/// <para>Redirecting MVC handler</para>
/// </summary>
public class RedirectHandler : IHttpHandler
{
private string newUrl;
public RedirectHandler(string newUrl)
{
this.newUrl = newUrl;
}
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext httpContext)
{
httpContext.Response.Status = "301 Moved Permanently";
httpContext.Response.StatusCode = 301;
httpContext.Response.AppendHeader("Location", newUrl);
return;
}
}
Note: I’m not saying this is the best or only way to handle this. You’ll want to look at Url Rewriting and the Application and Request Routing module for IIS7 in particular.
Why functional programming and LINQ is often better than procedural code
Apr 15th
Functional programming is a relatively new component in the C# language. It can potentially replace for-loops in many situations with simpler code, but the question remains ‘what’s wrong with a good old for loop?’
Here are some of the reasons I think functional programming is important and in particular how LINQ can improve the readability, maintainability, and parallelizability (if there were such a word) of your code:
- Functional approaches are potentially easier to parallelize either manually using PLINQ or by the compiler. As CPUs move to even more cores this may become more important.
- Functional approaches make it easier to achieve lazy evaluation in multi-step processes because you can pass the intermediate results to the next step as a simple variable which hasn’t been evaluated fully yet rather than evaluating the first step entirely and then passing a collection to the next step (or without using a separate method and a yield statement to achieve the same procedurally).
- Functional approaches are often shorter and easier to read.
- Functional approaches often eliminate complex conditional bodies within for loops (e.g. if statements and ‘continue’ statements) because you can break the for loop down into logical steps – selecting all the elements that match, doing an operation on them, …
These days I opt for the functional syntax more often than not and fall back to for-loops when:-
A. The body of the loop contains complex logic that cannot be disentangled into a cleaner sequential application of functions and it simply easier to just write a for-loop with the complex conditional code in it.
B. The task is inherently not functional, i.e. has side effects
C. The task needs exception handling in it. Sure you can write big lambda blocks with try catch in them but at some point it becomes easier and cleaner just to use a for-loop.
Using Home Automation to Monitor Cable Modem
Apr 8th
Some time ago I was having an issue with my cable modem connection. The connection was intermittent at certain times of day but fine otherwise. Calling the cable company with an intermittent problem is pointless: you get the standard answer “But it’s working fine now”.
So, since I have the world’s smartest home automation system running in my closet it was trivial to add a quick HTML scraper to read the cable modem statistics every few minutes, persist them to storage and then add some graphs showing how they vary over time.
The .NET Chart Control is what I’m using now to render these charts. I’ve tried Google charts, Open Flash Charts and even my own pure-CSS chart generator. The .NET Chart control has the best options at the moment and it’s a plain JPG file so it will work on any browser.
After a few days I had a better picture as to how the signal strength and signal to noise ratio was varying over time and was quickly able to realize that it was in fact weather related. Waterproofing the cable connections cured the problem.
10 reasons my O(n²) algorithm is better than your O(n) algorithm
Apr 8th
OK, so it’s a controversial title, but the point I want to make is that programmers often get confused about the time complexity, i.e. order O() of an algorithm and performance. They will happily declare that their O(n) algorithm is so much better than someone else’s O(n²) algorithm as they rip out some simple, stable, working code and replace it with their vastly more complex, un-tested, but ‘optimized’ code, often without even measuring to see if there is in fact any performance gain. The fact is that O() notation refers to the asymptotic performance of an algorithm and that in a typical environment with typical data you aren’t going to be anywhere close to asymptotic behavior. Performance is, in fact, influenced by many other factors besides the time complexity of an algorithm.
So what factors can make an O(n²) algorithm better than an O(n) algorithm?
1. It might take a very large N before the O(n²) algorithm is slower than the O(n) algorithm. And we aren’t talking n=10, n=100, … here, but perhaps millions or billions before the cross-over in performance is achieved.
2. O() refers to time-complexity not to how long the algorithm actually takes to execute on a particular CPU. For that you need to look at the mix of instructions used and how long each of those instructions takes. But that in itself is a gross-simplification as we shall see next …
Even if the two algorithms have a very similar number of operations using similar CPU instructions, they still are not going to run at the same speed:
3. The compiler might be able to apply optimizations to one algorithm that it cannot apply to the other, dramatically boosting performance.
4. The CPU itself might be able to optimize the instruction execution sequence through pipelining or other techniques to dramatically boost performance. A dumb scan of an array for example can be accomplished really quickly by most CPUs because they have been optimized to handle such frequent, repetitive tasks.
5. A simpler O(n²) algorithm might have tighter loops in it than the more-complex O(n) algorithm allowing it to run entirely in on-chip memory, dramatically boosting performance.
Even if two algorithms have similar operations and similar raw CPU performance they may have different space requirements. The O(n) algorithm might require 10x as much data space to run or maybe a different order of space requirement to the other, e.g. O(n²) space requirement vs O(n) space requirement.
6. The algorithm that requires least data space in the inner loop may find that its data is being held in registers or on-chip RAM during execution and thus receive a significant performance boost.
7. The more space hungry algorithm might have its data paged to disk during operation incurring a huge additional time penalty.
Some algorithms are inherently more parallelizable than others:
8. As we move to a world of multi-core processors it’s going to be more important to be able to parallelize code to get performance gains of 4x, 8x, … PLINQ and other parallel extensions in modern languages can easily be applied to ‘dumb’ nested for-loops to gain significant improvements in performance. Applying those same speedups to your ‘optimized’ tree-based code might be much harder or even impossible.
‘Better’ is a very subjective word, better performance is just one of many issues
9. The better algorithm is probably the one that’s easiest to write, easiest for others to read, easiest to test and easiest to maintain. A simple nested for-loop may be 1000x better than some O(n) algorithm when best is defined like this.
10. Even if your algorithm beats mine with all of these factors taken into account that’s no guarantee it will do so in the future. Compilers evolve, CPUs get smarter and much of that effort is devoted to making dumb code faster.
Conclusion
Premature optimization is always a bad idea.
Many ‘optimizations’ can actually lead to worse not better performance.
Always measure, then optimize, then measure again.
It’s often cheaper to buy another server than it is to have someone ‘optimize’ your code.
O(n) is a much misunderstood topic
On modern servers space complexity is nearly always more important than time complexity these days. If you can run in on-chip RAM you have a huge advantage.
Development Tools and Libraries I use
Mar 29th
Libraries and Code Snippets
TweetSharp
Predicate Builder for LINQ query building: http://www.albahari.com/nutshell/predicatebuilder.aspx
Useful tools
LinqPad http://www.linqpad.net/
Source Control
Subversion with TortoiseSVN
Continuous Integration
JetBrains TeamCity, recently moved off CruiseControl.NET
Deployment
Subversion as a repository for binary images, custom deployment code
Web Server
IIS7
Useful articles
System.Data.UpdateException: A value shared across entities or associations is generated in more than one location. Check that mapping does not split an EntityKey to multiple store-generated columns
Mar 20th
This Visual Studio 2010 RC bug is really causing grief. If you see the error message above, check your EDMX file. Visual Studio may have kindly inserted an extra
StoreGeneratedPattern="Identity"
into your EDMX file on a child table for the association back to the parent table.
For example, this …
<EntityType Name="ExceptionRecordData">
<Key>
<PropertyRef Name="ExceptionDataUID" />
</Key>
<Property Name="ExceptionDataUID" Type="bigint" Nullable="false" StoreGeneratedPattern="Identity" />
<Property Name="ExceptionUID" Type="bigint" Nullable="false" />
<Property Name="Name" Type="nvarchar" Nullable="false" MaxLength="32" />
<Property Name="Value" Type="nvarchar" Nullable="false" MaxLength="128" />
</EntityType>
becomes this … after a simple Update operation
<EntityType Name="ExceptionRecordData">
<Key>
<PropertyRef Name="ExceptionDataUID" />
</Key>
<Property Name="ExceptionDataUID" Type="bigint" Nullable="false" StoreGeneratedPattern="Identity" />
<Property Name="ExceptionUID" Type="bigint" Nullable="false" StoreGeneratedPattern="Identity" />
<Property Name="Name" Type="nvarchar" Nullable="false" MaxLength="32" />
<Property Name="Value" Type="nvarchar" Nullable="false" MaxLength="128" />
</EntityType>
Note how it has added an extra StoreGeneratedPattern=”Identity” on the ExceptionUID property. I hope this gets fixed soon.
If you experience this problem, here’s the link to up-vote it:- https://connect.microsoft.com/data/feedback/details/540058
Update: Microsoft has confirmed that this will be fixed in the released version of Visual Studio 2010 – see comments. Thanks!
Putting a feedback button on every page with ASP.NET MVC and JQuery
Mar 13th
You’ve probably seen many web sites with the floating ‘feedback’ button down the side. Here’s how to add one to your site using jQuery, jQuery UI and ASP.NET MVC.
First make sure you have jQuery and jQuery UI referenced in your master page view together with the CSS file for whichever jQuery UI theme you have chosen.
We’ll make a few changes to the master page view to add the pop-up feedback form, we’ll add an action on a controller to accept the feedback that is posted, and we’ll need a small amount of CSS.
So, after referencing those javascript files and the theme CSS, the first thing to do is to add the following HTML to the bottom of your master page view:
<div id="feedbackdialog" style="width:300px; height:300px;text-align:left;">
<p>Your name and/or email: <br />
<input type="text" id="feedbackEmail" name="feedbackEmail" size="34" value="<%: this.Model.AccountEmailOrEmpty %>" />
</p>
<p>Comment:<br />
<textarea id="feedbackComment" name="comment" cols="35" rows="5"></textarea></p>
<br />
<div id="feedbackResult"></div>
</div>
Now add this code to your global javascript file that also referenced from your master page view … don’t embed it in the page, go ahead and do the right thing and put it in a .js file so it’s not a burden on every page.
//function for the feedback form
$(document).ready(
function () {
/* Create the feedback dialog */
$("#feedbackdialog").dialog(
{
closeOnEscape: true,
modal: true,
autoOpen: false,
resizable: false,
title: 'Feedback',
width: 400,
buttons: { "Send": function () {
var dlg = $(this);
$.post("/corporate/suggest",
{
email: dlg.find("input[name='feedbackEmail']").val(),
comment: dlg.find("#feedbackComment").val(),
url: document.location.href
},
function (data) {
dlg.dialog('close');
}
);
$(this).html("<p id='feedBackSending'>Sending</p>").dialog({ buttons: {} });
}
}
});
$('.contact_us').click(function () {
$("#feedbackdialog").dialog("open");
return false;
});
});
Next we’ll add the action referenced here, in the example we used the url ‘/corporate/suggest’ so, assuming you have a controller called CorporateController, add the following action to it …
public ActionResult Suggest (string email, string comment, string url)
{
if (!string.IsNullOrWhiteSpace(comment))
{
// here we will log the feedback to the database and/or send it in email
}
return View();
}
Create a view for ‘Suggest’, it doesn’t matter what’s in it as we don’t use the result currently.
And, finally we need a bit of CSS for the feedback icon itself:
/* Feedback tab */
#feedbackTab
{
right:0;
position:fixed;
width:32px;
height:150px;
top: 150px;
z-index:1;
}
The feedback button now floats on every page, 150px from the top and it’s glued to the right hand side.
Of course you’ll need your own feedback image, or feel free to borrow the one here:- http://www.signswift.com/images/feedback.png
So with that all in place, click the feedback button and a form like this should appear. Fill the information in and send it to the server. Note how we silently grab the url of the page too so we can see which page they were on when the submitted the feedback.
A simple web crawler in C# using HtmlAgilityPack
Mar 10th
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HtmlAgilityPack;
using System.Net;
namespace LinkChecker.WebSpider
{
/// <summary>
/// A result encapsulating the Url and the HtmlDocument
/// </summary>
public abstract class WebPage
{
public Uri Url { get; set; }
/// <summary>
/// Get every WebPage.Internal on a web site (or part of a web site) visiting all internal links just once
/// plus every external page (or other Url) linked to the web site as a WebPage.External
/// </summary>
/// <remarks>
/// Use .OfType WebPage.Internal to get just the internal ones if that's what you want
/// </remarks>
public static IEnumerable<WebPage> GetAllPagesUnder(Uri urlRoot)
{
var queue = new Queue<Uri>();
var allSiteUrls = new HashSet<Uri>();
queue.Enqueue(urlRoot);
allSiteUrls.Add(urlRoot);
while (queue.Count > 0)
{
Uri url = queue.Dequeue();
HttpWebRequest oReq = (HttpWebRequest)WebRequest.Create(url);
oReq.UserAgent = @"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.5) Gecko/20091102 Firefox/3.5.5";
HttpWebResponse resp = (HttpWebResponse)oReq.GetResponse();
WebPage result;
if (resp.ContentType.StartsWith("text/html", StringComparison.InvariantCultureIgnoreCase))
{
HtmlDocument doc = new HtmlDocument();
try
{
var resultStream = resp.GetResponseStream();
doc.Load(resultStream); // The HtmlAgilityPack
result = new Internal() { Url = url, HtmlDocument = doc };
}
catch (System.Net.WebException ex)
{
result = new WebPage.Error() { Url = url, Exception = ex };
}
catch (Exception ex)
{
ex.Data.Add("Url", url); // Annotate the exception with the Url
throw;
}
// Success, hand off the page
yield return new WebPage.Internal() { Url = url, HtmlDocument = doc };
// And and now queue up all the links on this page
foreach (HtmlNode link in doc.DocumentNode.SelectNodes(@"//a[@href]"))
{
HtmlAttribute att = link.Attributes["href"];
if (att == null) continue;
string href = att.Value;
if (href.StartsWith("javascript", StringComparison.InvariantCultureIgnoreCase)) continue; // ignore javascript on buttons using a tags
Uri urlNext = new Uri(href, UriKind.RelativeOrAbsolute);
// Make it absolute if it's relative
if (!urlNext.IsAbsoluteUri)
{
urlNext = new Uri(urlRoot, urlNext);
}
if (!allSiteUrls.Contains(urlNext))
{
allSiteUrls.Add(urlNext); // keep track of every page we've handed off
if (urlRoot.IsBaseOf(urlNext))
{
queue.Enqueue(urlNext);
}
else
{
yield return new WebPage.External() { Url = urlNext };
}
}
}
}
}
}
///// <summary>
///// In the future might provide all the images too??
///// </summary>
//public class Image : WebPage
//{
//}
/// <summary>
/// Error loading page
/// </summary>
public class Error : WebPage
{
public int HttpResult { get; set; }
public Exception Exception { get; set; }
}
/// <summary>
/// External page - not followed
/// </summary>
/// <remarks>
/// No body - go load it yourself
/// </remarks>
public class External : WebPage
{
}
/// <summary>
/// Internal page
/// </summary>
public class Internal : WebPage
{
/// <summary>
/// For internal pages we load the document for you
/// </summary>
public virtual HtmlDocument HtmlDocument { get; internal set; }
}
}
}
Using Exception.Data to add additional information to an Exception
Mar 7th
Introduction
Whether you are writing a WinForms application or a complex .NET web site, you will invariably be catching exceptions, logging them and reporting them somewhere. (In this post, I’m not going to explain how to log exceptions). Simply reporting the exception as-thrown rarely captures enough information to be able to diagnose what happened. A FileNotFoundException for instance isn’t much use unless you know which file it was.
One way to deal with this issue is to wrap an exception up in a more explicit exception that includes the extra information, e.g.
string filename ...
try
{
//... do something with the file
}
catch (FileNotFoundException ex)
{
CustomException ex2 = new CustomException("Missing cache: " + filename", ex);
throw ex2;
}
This approach works but it leads to a lot of custom exceptions that are just extra work to create and maintain. Sometimes you’ll want a custom exception because you are going to handle it in a different way in some outer scope, but often you just want to log the error and redirect the user to an error page as there is nothing else you can do to fix the problem.
In cases like this, you can simplify things greatly by using the little-known .data property on an Exception. This is an IDictionary for a “collection of key/value pairs that provide additional user-defined information about the exception” [MSDN].
Using this approach, you can write:
try
{
...
}
catch (FileNotFoundException ex)
{
ex.Data.Add("cache filename", filename);
throw;
}
Each surrounding scope can include a similar Try-Catch that adds more information to .Data so by the time you get to the top-most scope you have added a complete picture as to what might have caused the exception. And in doing so you haven’t lost any of the StackTrace information, nor have you wrapped the exception up needlessly in another exception.
At a higher level in your Global.asax file where you catch all unhandled exceptions, you can add even more to the .Data collection and perhaps include all the interesting parameters on HttpContext like RawUrl, cookies, …
ex.Data.Add("RawUrl", request.RawUrl);
try
{
foreach (string cookieName in request.Cookies)
{
try
{
HttpCookie cookie = request.Cookies[cookieName];
string key = "Cookie " + cookie.Path + " " + cookieName;
if (!ex.Data.Contains(key))
{
ex.Data.Add(key, cookie.Value.ToString());
}
}
catch
{
// deliberately nothing in here, should
// never happen, just being cautious
}
}
// An extension method I use to spot bots - write your own ...
if (request.IsABot())
{
ex.Data.Add("BOT", "************* BOT *****************");
}
ex.Data.Add("UserAgent", request.UserAgent);
ex.Data.Add("Referrer", request.UrlReferrer);
ex.Data.Add("User Host", request.UserHostName);
}
catch
{
// deliberately nothing in here, should
// never happen, just being cautious
// but we definitely don't want to cause
// an exception while handling one!
}
Exception Reporting Code
Now in your exception reporting code, you can write out the exception message and stack trace followed by a dump of all the key value pairs in .Data. I tend to use log4net on each server writing to a rolling log file and SQL server to capture the exception data centrally. For SQL, you’ll probably want one table for the Exception itself and another table with a row for each key/value pair in .Data.
Comments
One cause of Exceptions on web servers is bots and client-side ‘web accelerators’. Both of these can hit pages with incorrect or outdated parameters that you simply didn’t expect to receive. That’s why I add a BOT warning on every exception as the exception itself may seem severe but in reality it’s benign and no user has ever seen it. I even found one antivirus product that takes each request you make and sends the URL to Japan where another server makes a second request back to check the page for viruses! It even pretends not to be a Bot in the UserAgent and of course, all your ‘security- through-obscurity’ URLs are now sitting on a server in Japan, but you know security through obscurity is no security at all, right?
Another browser add on called FunWebProducts would routinely corrupt Viewstate information so if you see that in your exceptions log, you know who to blame.


