Posts tagged C#
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.
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
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.
A strongly-typed natural language engine (C# NLP)
Feb 28th
Here is an explanation of the natural language engine that powers my home automation system. It’s a strongly-typed natural language engine with tokens and sentences being defined in code. It currently understands sentences to control lights, heating, music, sprinklers, … You can ask it who called, you can tell it to play music in a particular room, … it tells you when a car comes down the drive, when the traffic is bad on I-90, when there’s fresh snow in the mountains, when it finds new podcasts from NPR, … and much more.
The natural language engine itself is a separate component that I hope one day to use in other applications.
Existing Natural Language Engines
- Have a large, STATIC dictionary data file
- Can parse complex sentence structure
- Hand back a tree of tokens (strings)
- Don’t handle conversations
C# NLP Engine
- Defines strongly-typed tokens in code
- Uses type inheritance to model ‘is a’
- Defines sentences in code
- Rules engine executes sentences
- Understands context (conversation history)
Sample conversation
Goals
- Make it easy to define tokens and sentences (not XML)
- Safe, compile-time checked definition of the syntax and grammar (not XML)
- Model real-world inheritance with C# class inheritance:
- ‘a labrador’ is ‘a dog’ is ‘an animal’ is ‘a thing’
- Handle ambiguity, e.g.
play something in the air tonight in the kitchen remind me at 4pm to call john at 5pm
C# NLP Engine Structure
Tokens – Token Definition
- A hierarchy of Token-derived classes
- Uses inheritance, e.g. TokenOn is a TokenOnOff is a TokenState is a Token. This allows a single sentence rule to handle multiple cases, e.g. On and Off
- Derived from base Token class
- Simple tokens are a set of words, e.g. « is | are »
- Complex tokens have a parser, e.g. TokenDouble
A Simple Token Definition
public class TokenPersonalPronoun : TokenGenericNoun
{
internal static string wordz { get { return "he,him,she,her,them"; } }
}
- Recognizes any of the words specified
- Can use inheritance (as in this example)
A Complex Token
public abstract class TokenNumber : Token
{
public static IEnumerable<TokenResult> Initialize(string input)
{
…
- Initialize method parses input and returns one or more possible parses.
TokenNumber is a good example:
- Parses any numeric value and returns one or more of TokenInt, TokenLong, TokenIntOrdinal, TokenDouble, or TokenPercentage results.
The catch-all TokenPhrase
public class TokenPhrase : Token
TokenPhrase matches anything, especially anything in quote marks
e.g. add a reminder "call Bruno at 4pm"
The sentence signature to recognize this could be
(…, TokenAdd, TokenReminder, TokenPhrase, TokenExactTime)
This would match the rule too …
add a reminder discuss 6pm conference call with Bruno at 4pm
TemporalTokens
A complete set of tokens and related classes for representing time
- Point in time, e.g. today at 5pm
- Approximate time, e.g. who called at 5pm today
- Finite sequence, e.g. every Thursday in May 2009
- Infinite sequence, e.g. every Thursday
- Ambiguous time with context, e.g. remind me on Tuesday (context means it is next Tuesday)
- Null time
- Unknowable/incomprehensible time
TemporalTokens (Cont.)
Code to merge any sequence of temporal tokens to the smallest canonical representation,
e.g.
the first thursday in may 2009
->
{TIMETHEFIRST the first} + {THURSDAY thursday} + {MAY in may} + {INT 2009 -> 2009}
->
[TEMPORALSETFINITESINGLEINTERVAL [Thursday 5/7/2009] ]
TemporalTokens (Cont.)
Finite TemporalClasses provide
All TemporalClasses provide
Existing Token Types
- Numbers (double, long, int, percentage, phone, temperature)
- File names, Directories
- URLs, Domain names
- Names, Companies, Addresses
- Rooms, Lights, Sensors, Sprinklers, …
- States (On, Off, Dim, Bright, Loud, Quiet, …)
- Units of Time, Weight, Distance
- Songs, albums, artists, genres, tags
- Temporal expressions
- Commands, verbs, nouns, pronouns, …
Rules – A simple rule
/// <summary>
/// Set a light to a given state
/// </summary>
private static void LightState(NLPState st, TokenLight tlight, TokenStateOnOff ts)
{
if (ts.IsTrueState == true) tlight.ForceOn(st.Actor);
if (ts.IsTrueState == false) tlight.ForceOff(st.Actor);
st.Say("I turned it " + ts.LowerCased);
}
Any method matching this signature is a sentence rule:- NLPState, Token*
Rule matching respects inheritance, and variable repeats … (NLPState st, TokenThing tt, TokenState tokenState, TokenTimeConstraint[] constraints)
Rules are discovered on startup using Reflection and an efficient parse graph is built allowing rapid detection and rejection of incoming sentences.
State – NLPState
- Every sentence method takes an NLPState first parameter
- State includes RememberedObject(s) allowing sentences to react to anything that happened earlier in a conversation
- Non-interactive uses can pass a dummy state
- State can be per-user or per-conversation for non-realtime conversations like email
- Chat (e.g Jabber/Gtalk)
- Web chat
- Calendar (do X at time Y)
- Rich client application
- Strongly-typed natural language engine
- Compile time checking, inheritance, …
- Define tokens and sentences (rules) in C#
- Strongly-typed tokens: numbers, percentages, times, dates, file names, urls, people, business objects, …
- Builds an efficient parse graph
- Tracks conversation history
- Company names, locations, documents, …
- From TimeExpressions
User Interface
Works with a variety of user interfaces
Summary
Future plans
Expanded corpus of knowledge
Generate iCal/Gdata Recurrence
String extension methods for truncating and adding ellipsis
Feb 2nd
Some useful extension methods for working with strings and IEnumerable<string>. These come in handy when logging as you often want to show just 80 characters (say) in a log entry.
/// <summary>
/// Substring but OK if shorter
/// </summary>
public static string Limit(this string str, int characterCount)
{
if (str.Length <= characterCount) return str;
else return str.Substring(0, characterCount).TrimEnd(‘ ‘);
}
/// <summary>
/// Substring with elipses but OK if shorter, will take 3 characters off character count if necessary
/// </summary>
public static string LimitWithElipses(this string str, int characterCount)
{
if (characterCount < 5) return str.Limit(characterCount); // Can’t do much with such a short limit
if (str.Length <= characterCount – 3) return str;
else return str.Substring(0, characterCount – 3) + “…”;
}
/// <summary>
/// Substring with elipses but OK if shorter, will take 3 characters off character count if necessary
/// tries to land on a space.
/// </summary>
public static string LimitWithElipsesOnWordBoundary(this string str, int characterCount)
{
if (characterCount < 5) return str.Limit(characterCount); // Can’t do much with such a short limit
if (str.Length <= characterCount – 3)
return str;
else
{
int lastspace = str.Substring(0, characterCount – 3).LastIndexOf(‘ ‘);
if (lastspace > 0 && lastspace > characterCount – 10)
{
return str.Substring(0, lastspace) + “…”;
}
else
{
// No suitable space was found
return str.Substring(0, characterCount – 3) + “…”;
}
}
}
The Blog of Ian Mercer: Home Automation++
Jan 26th
This is the blog of Ian Mercer, Serial entrepreneur, Digital Mapping Pioneer, Inventor of Windows Movie Maker, Microsoft Producer, HighMat, SnapTune, … Patent holder, Guinness world record holder, and founder and CEO of SignSwift, LLC,
Here you’ll find my thoughts on Home automation, Energy Conservation, Programming, Operations, Entrepreneurship, Business, Startups, Quality, Agile, Optimization, .NET, Natural Language Processing, Digital Signage and more.
My home automation system recently won 1st and 3rd place in the international, Microsoft MyDotNetStory competition. You can view a demonstration of it under the home automation menu above. I believe that it is the world’s smartest house because I haven’t seen anything smarter. It controls lights, heating, sprinklers, audio, and more; it has hundreds of sensors including PIR, strain gauges, door switches, and more; it interfaces to Caller ID, PBX, Google Calendar, Google Contacts, Email, NOAA, Twitter, Facebook, RSS, Traffic, Podcasts and more. For the most part it works autonomously doing the right thing and reducing our energy consumption by over 40%. You can control it using a web, voice or chat interface. It has a unique Natural Language Interface that understands sentences and can hold a two-way conversation with you.
Under the SEO menu you’ll find my SEO keyword mapping tool; it can untangle a list of keyword phrases and volumes (like you get from Google’s Adwords tools) to give you a clearer picture of the keywords you need to put on your pages. Using variable sized boxes and lines it shows you which words are the most important and how they are linked.
Thoughts under programming are mostly related to Microsoft’s .NET platform and the C# language but there are some that are more general in nature. I’ve been using Visual Studio 2010 and .NET 4 extensively recently including the Entity Framework, WCF, WPF and Silverlight.
You can view my professional profile on Linkedin or follow me on Twitter.



I’m also an avid photographer and videographer and I’m in the process of adding a few of my favorite pictures to the site.




Interesting or useful links on Twitter this week relating to .NET and C#
Feb 22nd
Posted by Ian in Commentary
No comments