rss
Follow me on twitter!
    Find out what I'm doing, Follow Me :)
Your Ad Here

Error Handling in MVC with ELMAH

On my todo list for today, I had a entry for building a error handling mechanism in my MVC application that would write errors to a central location and send me notifications when they occurred. I had a vague idea of how I could accomplish this. One thought was creating an exception filter and logging the errors to a SQL table for later review. Therefore, I started doing some research. I figured I would have to read some MSDN documentation and perhaps a few blog articles to make sure I had a good grasp on what technologies to use to build a proper solution. Luckily, while I was searching Google for "MVC error handling" I stumbled across ELMAH. 5 minutes later I had ELMAH fully implemented and running on my site! Yes, it is that easy.

Just for background, ELMAH (Error Logging Modules and Handlers) is an application-wide error logging facility that is completely pluggable. It can be dynamically added to a running ASP.NET web application, or even all ASP.NET web applications on a machine, without any need for re-compilation or re-deployment.

Once ELMAH has been dropped into a running web application and configured appropriately, you get the following facilities without changing a single line of your code:

  • Logging of nearly all unhandled exceptions.
  • A web page to remotely view the entire log of recoded exceptions.
  • A web page to remotely view the full details of any one logged exception.
  • In many cases, you can review the original yellow screen of death that ASP.NET generated for a given exception, even with customErrors mode turned off.
  • An e-mail notification of each error at the time it occurs.
  • An RSS feed of the last 15 errors from the log.

Sounds great! So, how do you get this running in your MVC app. Well here are the steps (Taken almost in verbatim from the ELMAH Wiki page  http://code.google.com/p/elmah/wiki/MVC) :

  1. Download the latest binaries from http://code.google.com/p/elmah/downloads/list and add a reference to the ELMAH.dll assembly.
  2. Edit your web.config file to call ELMAH
    • First add the following code to your <configSections> to make ELMAH read it’s configuration from web.config:
      <sectionGroup name="elmah">
         <section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah" />
         <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
         <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
         <section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah" />
      </
      sectionGroup>
    • Add this block to your section to add the elmah file handler. This will reroute all requests to a file called elmah.axd to the ELMAH error-overview page. So when you want to look at the list of errors you’ll access http://server/elmah.axd. The name doesn’t matter, feel free to rename it, but be aware that the extension has to be mapped to the ASP.NET pipeline inside IIS (so naming it .html wouldn’t work if not configured correctly). 
      <add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
    • At last add the ELMAH logging module to your section:
      <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
  3. Configure where you want the errors to be logged. If you want to log to XML files add this line in your web.config
    <elmah>
       <errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="~/App_Data" />
    </
    elmah>
  4. Finally, configure the routing. Up until now we were 100% true to the normal configuration routine for normal ASP.NET applications, there is only one slight adjustment to making it work in MVC.

    You need to allow the requests to the ELMAH front-end (elmah.axd in this example) to pass through the MVC routing logic unchanged so that it gets handled by normal ASP.NET behind MVC. This is as trivial as adding an ignore route to your routing table in Global.asax.cs:

    public static void RegisterRoutes(RouteCollection routes)
    {
    routes.IgnoreRoute("elmah.axd");

Now ELMAH is installed. So now its time to test it out. This can be done by throwing an exception in one of your controllers. Just for simplicity, I added the line throw new exception("testing ELMAH") in the Index Method of my default controller. When the exception is thrown it is written to an XML (could also be a Oracle, Access or SQL database). Then by accessing http://server/elmah.axd you can view the errors remotely:

image

All of this functionality is yours, for free, thanks to ELMAH.

Concepts2Code - Your Source For Software Design, Development, and Consulting

Metallica Concert Tonight!

metallica1

I tend not to blog about non-tech stuff but today is an exception. Tonight, I am going to the Metallica concert at the HSBC arena in Buffalo NY. This will be my fourth time seeing Metallica and hopefully not the last!

I started listening to Metallica in high school. During those 4 years, I had a 30 minute walk and I needed something to kill the time. In the old days we did not have iPods, we had portable cassette players. In any case, I remember listening to the Master of Puppets cassette so many times that I actually wore out the tape. Eventually I repurchased all of my Metallica music on CDs and nowadays they reside on my IPod. There aren't too many bands that made the transition from my cassette player to my IPod. Metallica is definitely an icon and I will never get tired of listening to their music. I wonder if 30 years from now if Metallica will still be touring, it would be hard to play a guitar while holding a cane. The Rolling Stones seem to manage though....

Concepts2Code - Your Source For Software Design, Development, and Consulting

SQL 2008 – The Power of the MERGE Command

In my humble opinion, one of the best features of SQL 2008 is the MERGE command. In a nutshell, the MERGE statement allows you to insert, update, or delete data based on certain join conditions in a single statement. Traditionally this type of work was accomplished by creating a cursor, looping over each row and running some conditional logic to determine if you needed to insert, update or delete some rows.

If you view the syntax for the MERGE command with SQL books online you may be a little overwhelmed. The 100+ line syntax sample is not easy on the eyes. Therefore I was afraid that some people may be discouraged from using it, and I felt obligated to give an example that is a little easier to understand. First we will start by creating two tables and mocking up some data to test the MERGE command with:

   1:  CREATE Table Planets (
   2:      ID int not null,
   3:      Name varchar(25) not null,
   4:      Comments varchar(50) null
   5:  )
   6:  GO
   7:  CREATE Table MorePlanets (
   8:      ID int not null,
   9:      Name varchar(25) not null,
  10:      Comments varchar(50) null
  11:  )
  12:  GO
  13:  INSERT INTO Planets
  14:  VALUES 
  15:      (1, 'Mercury', 'Smallest planet in our solar system.'), 
  16:      (2, 'Venus', 'Named after the Roman goddess of love.'), 
  17:      (3, 'Earth', ''), 
  18:      (4, 'Mars', 'The red planet.' ),
  19:      (5, 'Jupiter', '' ),
  20:      (6, 'Saturn', 'Not the car company.' )
  21:  GO    
  22:  INSERT INTO MorePlanets
  23:  VALUES     
  24:      (3, 'Earth', 'All your base belong to us.'),     
  25:      (5, 'Jupiter', 'The largest plant in our solar system.' ),
  26:      (7, 'Uranus', 'I know what your thinking!' ),
  27:      (8, 'Neptune', 'The blue giant.' ),
  28:      (9, 'Pluto', 'Not Mickey''s dog' )
  29:      
  30:  select * from Planets
  31:  select * from MorePlanets

Note: In SQL 2008 we can also insert multiple values into a table with a single insert command!

You should end up with two tables, once called Planets and another named MorePlanets. If you observe the row data you will find that two records (Earth and Jupiter) exist in both tables.

image

There are also 3 additional records in the MorePlanets table that do not exist in the Planets table yet. The goal of our MERGE statement will be to update the name and description fields in the Planets table where the ID's match and to insert the records that have no corresponding ID:

   1:  MERGE Planets as target
   2:  USING( SELECT ID, Name, Comments FROM MorePlanets ) AS source
   3:  ON( target.ID = source.ID )
   4:  WHEN MATCHED THEN
   5:      UPDATE set target.Name = source.Name, target.Comments = source.Comments 
   6:  WHEN NOT MATCHED THEN
   7:      INSERT 
   8:          (ID, Name, Comments ) 
   9:      VALUES 
  10:          ( source.ID, source.Name, source.Comments );
  11:          
  12:  select * from Planets

Lets break down this sample MERGE statement line by line:

  • Line 1: We pick the Planets table as the merge target. This is where the merged results will end up
  • Line 2: We use a select statement as the source. In this example our tables have the exact same column names and data types so it makes things easy.
  • Line 3: We defined the matching criteria for the merge. In this case the IDs of the tables serve as the unique identifier.
  • Line 4-5: For each record in the MorePlanets table that has an ID that exists in the Planets table we will update the name and comments field.
  • Lines 6-10: For each record in the MorePlanets table that has an ID that does not exist in the Planets table we will insert a new row.

After we run the merge statement, here are the results:

image

Pretty cool eh? As you can see this command is very powerful. As a developer, I can think of a lot of uses for this command. Now that you understand the basic capabilities of the MERGE command spend some time reviewing Microsoft's examples. They show the advanced uses of the MERGE statement.

Concepts2Code - Your Source For Software Design, Development, and Consulting

MVC – Spark, The Alternative View Engine

One thing I always disliked about ASP.NET was the amount of angle brackets (<%'s) that I had to sprinkle all over my HTML. Especially when you are dealing with if statements that conditionally modify the HTML output. Wouldn't it be nice if you could achieve the same results without all the angle brackets and percent signs?

Fortunately there are alternative View Engines out there. One alternative view engine that has been getting a lot of buzz in the MVC community lately is Spark. According to their website, Spark is a view engine for ASP.Net MVC and Castle Project MonoRail frameworks. The idea is to allow the html to dominate the flow and the code to fit seamlessly. If you take the time to look at their documentation page you will probably agree with me on the fact that they have achieved their initiative.

Here is a simple example of how Spark differs from a traditional ASP.NET markup syntax:

   1: <% var names="new [] {'alpha', 'beta', 'gamma'}" %>
   2:  <% foreach( var name in names ) { %>
   3:      <% if( name == "beta" ) { %>
   4:         <p>beta is my favorite</p>
   5:      <% } else { %>
   6:         <p><%=name%> is okay too I suppose.
   7:      <% } %>
   8:  <% } >

Now, Here is the same code written with Spark

   1:  <var names="new [] {'alpha', 'beta', 'gamma'}"/>
   2:  <for each="var name in names">
   3:    <test if="name == 'beta'">
   4:      <p>beta is my favorite.</p>
   5:      <else/>
   6:      <p>${name} is okay too I suppose. 
   7:    </test>
   8:  </for>

If you ask me, the developers who created Spark are definitely on to something here. Since we can use tags instead of angle brackets it is easier to write and maintain the HTML. Especially when it comes to situations where you have a if statement nested inside a for loop. The standard ASP.NET markup can make it hard to match up the beginning and ending brackets in nested code blocks.

For me personally, I tend to shy away from using third party tools unless it is absolutely necessary. There is nothing worse then writing a large application based on a third party solution only to find out that the project died, is no longer supported or riddled with bugs that will never be fixed. Therefore, you probably won't find me replacing the standard view engine in my MVC apps anytime soon. Don't get me wrong, I am not saying that the Spark project will die or that you should be discouraged from using it. I just want to warn you that there are advantages and disadvantages from swaying from the norm.

In a perfect world, an employee from the Microsoft ASP.NET team will look at the Spark view engine and have an epiphany. Then they will take some of that Spark goodness and integrate into their code base. Until then I will continue to deal with the angle brackets until I have a better reason to make the transition. 

Concepts2Code - Your Source For Software Design, Development, and Consulting

Manipulating Blob Data in MSSQL with C#

Storing BLOB (Binary Large Objects) in a SQL database can be a very convenient way to tie documents with metadata. For example, if you are building a document management system it is very nice to be able to stuff a word document in a data row along with other information such as who created the document, when it was modified and etcetera.

The first step in accomplishing this task is to define a database table. The only real trick is to use a image column for the datatype of the column storing the BLOB data. For example consider this table creation script:

CREATE TABLE [dbo].[PurchaseOrderAttachment](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [PurchaseOrder] [nvarchar](50) NOT NULL,
    [Data] [image] NOT NULL,
    [Filename] [nvarchar](100) NOT NULL
)

To insert the data using C# you will need the following code (using LINQ)

MyDataContext db = new MyDataContext();
PurchaseOrderAttachment a = new PurchaseOrderAttachment();
a.Filename = System.IO.Path.GetFileName(attachment.Filename);
a.Data = System.IO.File.ReadAllBytes(attachment.Filename);
a.PurchaseOrder = "123";
db.PurchaseOrderAttachments.Add(a);
db.SubmitChanges();

Or if you are old school and want to do it with regular T-SQL:

INSERT INTO PurchaseOrderAttachment
    ( PurchaseOrder , Data, Filename )
VALUES
   ('123'
    ,( SELECT *  FROM OPENROWSET(BULK 'c:\windows\Santa Fe Stucco.bmp', SINGLE_BLOB) 
as x) ,'Santa Fe Stucco.bmp')

After we insert the image into the database we can take a quick look with SQL Server Management studio to see what it looks like in the table:

image

You can see that the Data column has binary data in it and it is not readable with the human eye. Therefore in order to view the data you will need to write a little code. Fortunately, for you I have written this code in two different ways. The first implementation was for a WinForms application. The second implementation was from a MVC application. Here is the Winforms code:

   1:  PurchaseOrderAttachment attachment = FindAttachment(1);
   2:  string filename = Path.Combine( System.IO.Path.GetTempPath(), attachment.Filename );
   3:  FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write);            
   4:  fs.Write(attachment.Data.ToArray(), 0, attachment.Data.Length);
   5:  fs.Close();
   6:   
   7:  if (File.Exists(filename) == true)
   8:      System.Diagnostics.Process.Start(filename);
   9:  else
  10:      MessageBox.Show(String.Format("Could not retreive file \"{0}\"", filename),
  11:                            "File not found", MessageBoxButtons.OK, MessageBoxIcon.Information);

Lets dissect this code. Line 1 retrieves the record from the database. Line 2 creates a temporary place to store the file on disk. Line 3,4 and 5 saves the file to disk using the FileStream object. Then on Line 8 I call System.Diagnostics.Process.Start(filename) to open the file. The reason I used this technique was because you can store any kind of file in the database. It could be excel, word, a jpeg, a TSQL script or even a text file. By saving the file to disk and calling the Process.Start method on it I can rely on windows to use the file extension and determine which program to launch. However, I can eliminate the need to write the file to disk completely if I build some more intelligence into my code and decide which application to launch based on the extension or content of the file. This is demonstrated in my next example.

In my MVC Web Application, I limited the kinds of files I allowed for upload. I stream the file to the client by using an HTTP Handler:

   1:  <%@ WebHandler Language="C#" Class="POAttachment" %>
   2:   
   3:  using System;
   4:  using System.Web;
   5:  using DBA.Common.DAL;
   6:  using System.Data.Linq;
   7:  using System.Linq;
   8:  using System.IO;
   9:   
  10:  public class POAttachment : IHttpHandler {
  11:      
  12:      public void ProcessRequest (HttpContext context) {
  13:          try {
  14:              int id = Convert.ToInt32(context.Request["id"]);
  15:              DBAInventoryDataContext db = new DBAInventoryDataContext();
  16:              PurchaseOrderAttachment attachment = FindAttachment(id);
  17:   
  18:              FileInfo file = new FileInfo(attachment.Filename);
  19:              context.Response.ContentType = GetContentType(file.Extension);
  20:              context.Response.AddHeader("Content-Disposition", " filename=" + attachment.Filename);
  21:              context.Response.AddHeader("Content-Length", attachment.Data.Length.ToString());
  22:              context.Response.BinaryWrite(attachment.Data.ToArray());
  23:              context.Response.Flush();            
  24:          }
  25:          catch {
  26:              context.Response.ContentType = "text/plain";
  27:              context.Response.Write("Error retreving attachment");
  28:          }
  29:      }
  30:      
  31:      private string GetContentType(string fileExtension)
  32:      {
  33:           switch (fileExtension) {
  34:               case ".xls":
  35:                   return "application/vnd.ms-excel";           
  38:               case ".pdf":
  39:                   return "application/pdf";             
  40:               default:
  41:                   return "application/octet-stream";
  42:           }
  43:      }
  44:  }

This code is somewhat simple. On line 16 I retrieve the row from the database. Then on lines 19-23 I set the Content Type, add some required headers and then I write the contents of the Data field to the Response object. When I want to use this Handler I create a hyperlink in my application that looks like this: http://<mysite>/POAttachment.ashx?id=1. When the user clicks on the link the handler will fetches the data from the database. The browser then will then launch the appropriate application based on the Content Type set in the Response headers.

Hopefully, this code will save you a little time. Although, it is very simple when you see the final code it takes a little bit of reading and experimenting to get something working for the first time. Happy Coding!

Concepts2Code - Your Source For Software Design, Development, and Consulting

VS 2010 Intellisense Improvements

image One of the reasons why it is so enjoyable to write .NET code is the IDE. If you started out writing code in notepad (like me) then I am sure that you truly appreciate all of the amazing things that Visual Studio does. Even though VS 2008 is amazing, VS 2010 is even better. One of the things they are upgrading for the next version is intellisense. 

In Visual Studio 2008, when you typed the first couple characters or a property or method, intellisense only returned items that started with those characters. In Visual Studio 2010 when you start typing text you get all the properties and methods start with and also contain those characters. For example, If you type “grid.Edit”. You will not only get the method EditIndex (as expected) but you will also get RowCancelingEdit, SetEditRow as well. The method EditIndex will be the default selection because it is a obvious match but you will also see the wildcard matches listed as well.

One other new feature has been titled “Pascal Case Intellisense”. Since the default naming guidelines in .NET use pascal casing, each word in a type or member should start with a capitalized letter. VS 2010’s intellisense filtering support now enables you to take advantage of this to quickly find and filter methods.  For example, if we typed “grid.PIC” VS 2010 will filter results that have PIC in their name, as well as those members which have a pascal cased name where the word segments start with that letter sequence. In our example, we would get two results which are PageIndexChanged and PageIndexChanging.

Additional Resources:

Concepts2Code - Your Source For Software Design, Development, and Consulting

How To Optimize the Speed of Your Website

With all the fancy scripts and tools available these days for web development it is easy to get carried away. Instead of writing custom solutions, we tend to download large, multi-purpose scripts to accomplish our goals. Although each library may not be big by itself, when you keep stacking one on top of the other you can end up with a bloated website. By bloated, I mean that your pages will become large in size which will increase download time and ultimately affect the user experience. So here are some tips that I came up with to help streamline your website.

  1. Include only the relevant scripts: If you are using ASP.NET, put a ContentPlaceHolder tag in the head of your master page which can be used for additional scripts and CSS. Instead of including every possible script file and CSS file in the master page you can now optionally include them on the pages that really need it.
    <head runat="server">       
        <link href="/Content/site.css" rel="stylesheet" type="text/css" />   
        <script src="/Scripts/jquery-1.3.2.min.js" type="text/javascript"></script>                  
         <asp:ContentPlaceHolder ID="AdditionalScripts" runat="server" />   
    </head>
    The HEAD on my master page is very simple. When I need to use additional libraries like JQuery UI, I include them on the content pages instead by using the ContentPlaceHolder.
  2. Optimize the size of your JavaScript and CSS: Removing the extra whitespace in your CSS and JavaScript files will decrease the size. There are some handy online tools you can use to accomplish this task.
    • The Online CSS compressor which offers three levels of compression which are Light, Normal and Super Compact. In addition it will also strip out comments.
    • The Online JavaScript compressor also offers three levels of compression. In my testing I was able to take a 25 KB script file and shrink it down to 5KB using the aggressive compression level.
    When you compress your files make sure you keep a copy of your original script and CSS files. Because, when you debug and develop it will be easier to read the files with the extra whitespace and comments.
  3. Be careful with images: A picture may paint a thousand words but it also can consume thousands of kilobytes! If you use images, try to follow these simple rules:
    • Use images sparingly. Ask yourself, do I really need to have an custom image for a button or can I use the standard HTML buttons with a little CSS styling.
    • Don't take a 800x600 dimension image and set the width and height on it to turn it into a icon. Shrink the image instead using Photoshop or GIMP.
    • Use small images for backgrounds instead of large full screen images. Often times, you can just tile the small image using CSS and accomplish the same goal.
    • Pick the proper image format. Sometimes just converting an image from one format to another can give you a large savings. Also reducing the resolution of the image can help. In most cases, you won't even notice the difference. Look at the example below from pingdom:
  4. Write smarter code: Often times you can shrink your website just by writing smarter code. Take this CSS class for example:
    .myClass { border-left: 0px; border-right: solid 1px #fff; border-top: solid 1px #ff, border-bottom: solid 1px #fff }

    Can be rewritten as:
    .myClass { border: solid 1px #fff; border-left: 0px; }

    For more tips like this, visit the CSS shorthand Guide...
  5. Consider using HTTP compression: Finally, if you followed all of these tips and your webpage is still too big then consider turning on compression:
    1. For IIS 6.0 follow this guide 
    2. For IIS 7.0 follow this guide.
    3. And of course, if you are using Apache follow these instructions.

Just keep in mind that turning on compression can increase the CPU utilization on your web server. So make sure you test out compression first to ensure that your web server can handle the increased load.

Concepts2Code - Your Source For Software Design, Development, and Consulting

 
Your Ad Here