matthew

Elmah and Azure, Using Sql Azure

by Matthew Hintzen on 22. January 2013 10:16

If you haven’t heard of or used ELMAH (Error Logging Modules and Handlers for ASP.NET) by Aziz Atif,  in your Asp.net application (any flavour, MVC, forms, Web.api) then you owe it to yourself to investigate and add it to your project Forthwith!  It will save you hours off troubleshooting.

But If you want to use it on Azure, you have few choices, Nuget has a Elmah for Azure Tables.  Now if you are like me, you are using Windows Azure to host your Asp.net Web API app (and MVC app as well), with the newest edition of Sql Azure as your database backend.  Now there is a Nuget package for Elmah using Sql Server, but the script that is provided with the package to provision the SQL database with the necessary tables and stored procedures is marked up for Sql 8 and SQL 8 compatibility… which doesn’t work with SQL Azure database functionality.

Here is the solution I came up with, the modified SQL script that can be used to provision a SQL Azure database for Elmah support.

 
 
CREATE TABLE [dbo].[ELMAH_Error]
(
    [ErrorId]     UNIQUEIDENTIFIER NOT NULL,
    [Application] NVARCHAR(60)  COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [Host]        NVARCHAR(50)  COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [Type]        NVARCHAR(100) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [Source]      NVARCHAR(60)  COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [Message]     NVARCHAR(500) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [User]        NVARCHAR(50)  COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [StatusCode]  INT NOT NULL,
    [TimeUtc]     DATETIME NOT NULL,
    [Sequence]    INT IDENTITY (1, 1) NOT NULL,
    [AllXml]      NTEXT COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL 
) 
 
GO
 
ALTER TABLE [dbo].[ELMAH_Error] WITH NOCHECK ADD 
    CONSTRAINT [PK_ELMAH_Error] PRIMARY KEY ([ErrorId])
GO
 
ALTER TABLE [dbo].[ELMAH_Error] ADD 
    CONSTRAINT [DF_ELMAH_Error_ErrorId] DEFAULT (NEWID()) FOR [ErrorId]
GO
 
CREATE NONCLUSTERED INDEX [IX_ELMAH_Error_App_Time_Seq] ON [dbo].[ELMAH_Error] 
(
    [Application]   ASC,
    [TimeUtc]       DESC,
    [Sequence]      DESC
) 
GO
 
/* ------------------------------------------------------------------------ 
        STORED PROCEDURES                                                      
   ------------------------------------------------------------------------ */
 
SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS ON 
GO
 
CREATE PROCEDURE [dbo].[ELMAH_GetErrorXml]
(
    @Application NVARCHAR(60),
    @ErrorId UNIQUEIDENTIFIER
)
AS
 
    SET NOCOUNT ON
 
    SELECT 
        [AllXml]
    FROM 
        [ELMAH_Error]
    WHERE
        [ErrorId] = @ErrorId
    AND
        [Application] = @Application
 
GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO
 
SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS ON 
GO
 
CREATE PROCEDURE [dbo].[ELMAH_GetErrorsXml]
(
    @Application NVARCHAR(60),
    @PageIndex INT = 0,
    @PageSize INT = 15,
    @TotalCount INT OUTPUT
)
AS 
 
    SET NOCOUNT ON
 
    DECLARE @FirstTimeUTC DATETIME
    DECLARE @FirstSequence INT
    DECLARE @StartRow INT
    DECLARE @StartRowIndex INT
 
    SELECT 
        @TotalCount = COUNT(1) 
    FROM 
        [ELMAH_Error]
    WHERE 
        [Application] = @Application
 
    -- Get the ID of the first error for the requested page
 
    SET @StartRowIndex = @PageIndex * @PageSize + 1
 
    IF @StartRowIndex <= @TotalCount
    BEGIN
 
        SET ROWCOUNT @StartRowIndex
 
        SELECT  
            @FirstTimeUTC = [TimeUtc],
            @FirstSequence = [Sequence]
        FROM 
            [ELMAH_Error]
        WHERE   
            [Application] = @Application
        ORDER BY 
            [TimeUtc] DESC, 
            [Sequence] DESC
 
    END
    ELSE
    BEGIN
 
        SET @PageSize = 0
 
    END
 
    -- Now set the row count to the requested page size and get
    -- all records below it for the pertaining application.
 
    SET ROWCOUNT @PageSize
 
    SELECT 
        errorId     = [ErrorId], 
        application = [Application],
        host        = [Host], 
        type        = [Type],
        source      = [Source],
        message     = [Message],
        [user]      = [User],
        statusCode  = [StatusCode], 
        time        = CONVERT(VARCHAR(50), [TimeUtc], 126) + 'Z'
    FROM 
        [ELMAH_Error] error
    WHERE
        [Application] = @Application
    AND
        [TimeUtc] <= @FirstTimeUTC
    AND 
        [Sequence] <= @FirstSequence
    ORDER BY
        [TimeUtc] DESC, 
        [Sequence] DESC
    FOR
        XML AUTO
 
GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO
 
SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS ON 
GO
 
CREATE PROCEDURE [dbo].[ELMAH_LogError]
(
    @ErrorId UNIQUEIDENTIFIER,
    @Application NVARCHAR(60),
    @Host NVARCHAR(30),
    @Type NVARCHAR(100),
    @Source NVARCHAR(60),
    @Message NVARCHAR(500),
    @User NVARCHAR(50),
    @AllXml NTEXT,
    @StatusCode INT,
    @TimeUtc DATETIME
)
AS
 
    SET NOCOUNT ON
 
    INSERT
    INTO
        [ELMAH_Error]
        (
            [ErrorId],
            [Application],
            [Host],
            [Type],
            [Source],
            [Message],
            [User],
            [AllXml],
            [StatusCode],
            [TimeUtc]
        )
    VALUES
        (
            @ErrorId,
            @Application,
            @Host,
            @Type,
            @Source,
            @Message,
            @User,
            @AllXml,
            @StatusCode,
            @TimeUtc
        )
 
GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO
 

Matthew

Sql Azure to Azure Website with Entity Framework 5 (EF) Model First – Doesn’t work

by Matthew Hintzen on 17. December 2012 16:19

So this is a little helper for those of you out there who, with me, are attempting to using the Azure Framework and Infrastructure for the next GREAT project.  It’s tough being a pioneer, so let me spare you some arrows.

So here is the setup You have a SQL Azure Database, and you want to use it with an Azure Website that you will have configured to run MVC or maybe WebAPI.  Now let’s say you have a legacy database, before we all learned to Code First.  Or maybe you are like me and you actually don’t really like Code First; Database is Database, and should be designed as such; Business layer and Models are different, there is an impedance mismatch yes, but if you want your code to be scalable, robust and responsive, sometimes you just have to accept that you are going to need to write code…

So for what ever reason, you decided to use the new Entity Framework 5 and you want to do a Model First approach; as of 17th of December 2012, you will have a problem, because the Model First approach requires a “special” type of Database Connection String that as well as having information about the Database connection also has information about the Metadata used by the EF5 Model First.  I won’t get into the whole discussion of whether this was a good idea to put create a connection string that is really Metadata and has the “real” connection string hidden in the Metadata, that’s a thought for another day (but yes, I think this was a bad decision on the EF teams part).  So if you don’t know exactly what I mean let me show you.

 1:   <connectionStrings>
 2:       
 3:       <add name="Rincon" 
 4:             connectionString="data source=tcp:sqlazureUrl.database.windows.net,1433;
 5:  initial catalog=[dbname];User ID=[AzureLogin];Password=[AzurePassword];
 6:  Trusted_Connection=False;Encrypt=True;Connection Timeout=30;" 
 7:             providerName="System.Data.Sqlclient" />
 8:       
 9:      <add name="RinconEntities" 
 10:             connectionString="metadata=res://*/Dal.RinconDb.csdl|res://*/Dal.RinconDb.ssdl|res://*/Dal.RinconDb.msl;
 11:  provider=System.Data.SqlClient;
 12:   provider connection string=
 13:   &quot;
 14:  data source=tcp:sqlazureUrl.database.windows.net,1433;
 15:  initial catalog=[dbname];User ID=[AzureLogin];Password=[AzurePassword];
 16:  Trusted_Connection=False;Encrypt=True;Connection Timeout=30;
 17:  &quot;" 
 18:       providerName="System.Data.EntityClient" />
 19:   
 20:   </connectionStrings>

Ok, so the first connection string is the one we have been using since ASP.NET 1.0 for our Data needs, but that won’t work with a EF Model First approach, it requires the second  more complicated string, where the “real” connection string is embedded within the Model First connection string.

So what’s the problem you ask? Well currently You can’t configure the connection strings in Azure Websites to be anything but the “normal” real ones, like the first example

image

So I can’t show you (without giving away the secret sauce), but I have entered two Connection Strings in the Configure section for my Azure Website, and the “Custom” is suppose allow you specify something other then the defaults

image

You’ll notice that the Model First requires a ProviderName of “System.Data.EntityClient” but as you can see that is not provided for in the drop down list, so you’re supposed to be able to select “Custom” and specify whatever type of connection string you want, but trust me as of the 17th of December, that second one if it is a Model First Connection string WILL NOT be propagated out to the Web.config for the web site, even though you are told it should work.

So I had to solve the problem.  Mind you I also needed to support developing locally, and running Unit Tests (This project is TDD) with full gated checkin and Unit Test run and continous integration from MS hosted TFS…. Yeah in for penny in for a pound!  Let’s really put all the new stuff to the test, My goal in life is to keep @ScottGu busy trying to keep up with the promises Winking smile

Anyways, you probably have come to this page looking for the fix…. well turns out the fix wasn’t easy to figure out, but should be easy for you to implement, thanks to my work. In order to pull this off we need to change how we make the DBContext for the Model First connection, and what’s worse the only way to do that is to modify the T4 file, luckily the fix is easy, once you know what to do.

I’ve submitted it as a bug to the EF team (should probably also submit it to the Azure Team), with my suggested fix, and see about putting in a pull request to update the T4 template for Model First DBContext.  You can follow my submission here As currently configured SQL Azure will not work with Model First EF DBContext.

Down at the bottom you will find a comparison of the modifications you or you could just download my Context.tt, and replace the contents of your file with mine.

My EF T4 modfication Context.tt replacement

Then to use it in code you would start a new context using the following syntax

var dal = <EntitiesContainerName>.NewContext(
"<StandardSqlClientConnectionName>",
"<QualifiedMetadataInformation>"))

the <QualifiedMetadataInformation> string comes from the first entries on the Entity Frameworks Model First connection string

metadata=res://*/Dal.RinconDb.csdl|res://*/Dal.RinconDb.ssdl|res://*/Dal.RinconDb.msl;

In this case we would use the “Dal.RinconDb” as the substitute for “<QualifiedMetadataInformation>”

Here is an example of actually using the new functionality, this will work on both the local machine and on an Azure Website

 1: public ActionResult Test()
 2: {
 3:     using ( var dal = RinconEntities.NewContext("Rincon", "Dal.RinconDb"))
 4:     {
 5:         //do something
 6:     }
 7: }

The first parameter is the “name” of the normal standard Database connection string in the .config file, and the second is just the Metadata information we need to pragmatically build up Model First MetaData data connection.

I sure hope this can help someone else out there.

and as promised here is the difference in the original and my modified Context.tt


So here we go, if you do a model first in EF 5, the <ModelName>.Context.tt will start like this:

 1: <#@ template language="C#" debug="false" hostspecific="true"#>
 2: <#@ include file="EF.Utility.CS.ttinclude"#><#@
 3:  output extension=".cs"#><#
 4:  
 5: const string inputFile = @"RinconDb.edmx";
 6: var textTransform = DynamicTextTransformation.Create(this);
 7: var code = new CodeGenerationTools(this);
 8: var ef = new MetadataTools(this);
 9: var typeMapper = new TypeMapper(code, ef, textTransform.Errors);
 10: var loader = new EdmMetadataLoader(textTransform.Host, textTransform.Errors);
 11: var itemCollection = loader.CreateEdmItemCollection(inputFile);
 12: var modelNamespace = loader.GetModelNamespace(inputFile);
 13: var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef);
 14:  
 15: var container = itemCollection.OfType<EntityContainer>().FirstOrDefault();
 16: if (container == null)
 17: {
 18:     return string.Empty;
 19: }
 20: #>
 21: //------------------------------------------------------------------------------
 22: // <auto-generated>
 23: // <#=GetResourceString("Template_GeneratedCodeCommentLine1")#>
 24: //
 25: // <#=GetResourceString("Template_GeneratedCodeCommentLine2")#>
 26: // <#=GetResourceString("Template_GeneratedCodeCommentLine3")#>
 27: // </auto-generated>
 28: //------------------------------------------------------------------------------
 29:  
 30: <#
 31:  
 32: var codeNamespace = code.VsNamespaceSuggestion();
 33: if (!String.IsNullOrEmpty(codeNamespace))
 34: {
 35: #>
 36: namespace <#=code.EscapeNamespace(codeNamespace)#>
 37: {
 38: <#
 39:     PushIndent(" ");
 40: }
 41:  
 42: #>
 43: using System;
 44: using System.Data.Entity;
 45: using System.Data.Entity.Infrastructure;
 46: <#
 47: if (container.FunctionImports.Any())
 48: {
 49: #>
 50: using System.Data.Objects;
 51: using System.Data.Objects.DataClasses;
 52: using System.Linq;
 53: <#
 54: }
 55: #>
 56:  
 57: <#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
 58: {
 59:     public <#=code.Escape(container)#>()
 60:         : base("name=<#=container.Name#>")
 61:     {
 62: <#
 63: if (!loader.IsLazyLoadingEnabled(container))
 64: {
 65: #>
 66:         this.Configuration.LazyLoadingEnabled = false;
 67: <#
 68: }
 69: #>
 70:     }
 71:  
 72:     protected override void OnModelCreating(DbModelBuilder modelBuilder)
 73:     {
 74:         throw new UnintentionalCodeFirstException();
 75:     }

 

All you got to do is replace the start like this

 1: <#@ template language="C#" debug="false" hostspecific="true"#>
 2: <#@ include file="EF.Utility.CS.ttinclude"#><#@
 3:  output extension=".cs"#><#
 4:  
 5: const string inputFile = @"RinconDb.edmx";
 6: var textTransform = DynamicTextTransformation.Create(this);
 7: var code = new CodeGenerationTools(this);
 8: var ef = new MetadataTools(this);
 9: var typeMapper = new TypeMapper(code, ef, textTransform.Errors);
 10: var loader = new EdmMetadataLoader(textTransform.Host, textTransform.Errors);
 11: var itemCollection = loader.CreateEdmItemCollection(inputFile);
 12: var modelNamespace = loader.GetModelNamespace(inputFile);
 13: var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef);
 14:  
 15: var container = itemCollection.OfType<EntityContainer>().FirstOrDefault();
 16: if (container == null)
 17: {
 18:      return string.Empty;
 19: }
 20: #>
 21: //------------------------------------------------------------------------------
 22: // <auto-generated>
 23: // <#=GetResourceString("Template_GeneratedCodeCommentLine1")#>
 24: //
 25: // <#=GetResourceString("Template_GeneratedCodeCommentLine2")#>
 26: // <#=GetResourceString("Template_GeneratedCodeCommentLine3")#>
 27: // </auto-generated>
 28: //------------------------------------------------------------------------------
 29:  
 30: <#
 31:  
 32: var codeNamespace = code.VsNamespaceSuggestion();
 33: if (!String.IsNullOrEmpty(codeNamespace))
 34: {
 35: #>
 36: namespace <#=code.EscapeNamespace(codeNamespace)#>
 37: {
 38: <#
 39:      PushIndent(" ");
 40: }
 41:  
 42: #>
 43: using System;
 44: using System.Configuration;
 45: using System.Data.Entity;
 46: using System.Data.Entity.Infrastructure;
 47: using System.Data.EntityClient;
 48: using System.Data.SqlClient;
 49: <#
 50: if (container.FunctionImports.Any())
 51: {
 52: #>
 53: using System.Data.Objects;
 54: using System.Data.Objects.DataClasses;
 55: using System.Linq;
 56: <#
 57: }
 58: #>
 59:  
 60: <#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
 61: {
 62:      
 63:     public static <#=code.Escape(container)#> NewContext(string sqlConnectionName, string metadataSpecification)
 64:     {
 65:         var sqlConnection = ConfigurationManager.ConnectionStrings[ sqlConnectionName ];
 66:         var entityBuilder = new EntityConnectionStringBuilder();
 67:  
 68:         entityBuilder.ProviderConnectionString = sqlConnection.ConnectionString;
 69:         entityBuilder.Provider = sqlConnection.ProviderName;
 70:  
 71:         entityBuilder.Metadata = string.Format( @"res://*/{0}.csdl|
 72:                                                                 res://*/{0}.ssdl|
 73:                                                                 res://*/{0}.msl" , metadataSpecification );
 74:      
 75:         return new <#=code.Escape(container)#>( entityBuilder.ConnectionString );
 76:     }
 77:  
 78:     public <#=code.Escape(container)#>()
 79:         : base("name=<#=container.Name#>")
 80:     {
 81: <#
 82: if (!loader.IsLazyLoadingEnabled(container))
 83: {
 84: #>
 85:         this.Configuration.LazyLoadingEnabled = false;
 86: <#
 87: }
 88: #>
 89:     }
 90:  
 91:     public <#=code.Escape(container)#>(string nameOrConnectionString)
 92:         : base(nameOrConnectionString)
 93:     {
 94: <#
 95: if (!loader.IsLazyLoadingEnabled(container))
 96: {
 97: #>
 98:           this.Configuration.LazyLoadingEnabled = false;
 99: <#
 100: }
 101: #>
 102:     }
 103:  
 104:  

Admin

Red Jungle at TechEd 2012

by Phil on 4. September 2012 09:30

Red Jungle will once again be attending this year’s Microsoft TechEd conference. But we are especially excited this year to have the opportunity to present on some very interesting new topics. Our very own resident Windows Phone expert (read fanboy!) Matthew Hintzen has been chosen to speak and share his experiences of developing for the new Windows Phone platform.

Over the recent months Matthew has spent countless hours diving into the depths of DataBinding, weaving his way around Live Tiles, and coming to grips with how the whole life-cycle of push-notification services fits together. He’s put together two great presentations for you - in which he takes you through his learning's, triumphs, and of course the mistakes he made along the way too.

So if you’re attending the biggest tech conference of the year this week. Pop along to these great sessions to learn some Windows Phone magic (or just to be entertained) – it would be great to see you there!

APP306: WP7 Silverlight DataBinding: How exactly do you do that?
Friday, September 7 11:55 - 12:55 Marlborough Room
Speaker(s): Matthew Hintzen

We have all seen how Databinding works in WPF and in Silverlight, you work in Expression Blend, you do some binding... but what REALLY is going on. How does this binding work, how are contexts set up? I set out to pull back the curtain on the magic and help you to fully understand the significant amount of plumbing that is taken care of for you with {binding Name} an why sometimes it seems to work and other times... We will be concentrating on just how the binding works / what the notation means. Attendants will need to be very familiar with Entity Frameworks, SQL CE 3.5 local storage, Linq and Lambda notation to follow the demos. A cursory understanding of MVVM pattern concepts is also recommended.

APP307: Live Tiles, The Good, The Great and The Ugly
Friday, September 7 13:55 - 14:55 Marlborough Room
Speaker(s): Matthew Hintzen

Live Tiles are all the new rage and talk of the world in WP7 and with Windows 8 release just around the corner they are going to become even more important to your software. Your users are going to come to expect your live tile to actually be alive and meaningful. To do a Live Tile correctly however takes planning and an understanding of the Metro Experience Live Tile update architecture and life cycle. I'm hoping to help you to make sure your Live Tile is Great and plays well with Metro. In this session you will discover what makes for a good Live Tile, a bad Live Tile, and a "just don't do that". We'll walk thru: determining what should be on a Live Tile (and what shouldn't); How to make the tile engaging and informative without being distracting; Creating a Web Service that feeds the Live Tile in the way that best works with the Metro UI life cycle. Audience Participation will be greatly appreciated! Many of the Live Tile examples that are currently out there that show you "how" to do Live Tiles are not Best Practice! We look thru some not so good examples and really understand WHY they are should be avoided. For Live Tiles to be useful to the End Users we must use our great power with great responsibility.


Matthew

Red Jungle has a Speaker at TechEd NZ Conference - September 4 - 7

by Matthew Hintzen on 18. July 2012 12:01

And that would be Me.  I submitted two talk submissions, and they picked them both up.

The first one is

Live Tiles; The Good, The Great and The Ugly

Track: Building Metro Style Apps: Windows 8 and Windows Phone

Session Type: Breakout Session

Live Tiles are all the new rage and talk of the world in WP7 and with Windows 8 release just around the corner they are going to become even more important to your software. Your users are going to come to expect your live tile to actually be alive and meaningful. To do a Live Tile correctly however takes planning and an understanding of the Metro Experience Live Tile update architecture and life cycle. I'm hoping to help you to make sure your Live Tile is Great and plays well with Metro. In this session you will discover what makes for a good Live Tile, a bad Live Tile, and a "just don't do that". We'll walk thru

  • determining what should be on a Live Tile (and what shouldn't)
  • How to make the tile engaging and informative without being distracting.
  • Creating a Web Service that feeds the Live Tile in the way that best works with the Metro UI life cycle.
  • Audience Participation will be greatly appreciated!

 

Many of the Live Tile examples that are currently out there that show you "how" to do Live Tiles are not Best Practice! We look thru some not so good examples and really understand WHY they are should be avoided. For Live Tiles to be useful to the End Users we must use our great power with great responsibility


And the next one is:

WP7 Silverlight DataBinding:

How exactly do you do that?

Track: Building Metro Style Apps: Windows 8 and Windows Phone

Session Type: Breakout Session

We have all seen how Databinding works in WPF and in Silverlight, you work in Expression Blend, you do some binding... but what REALLY is going on. How does this binding work, how are contexts set up?

I set out to pull back the curtain on the magic and help you to fully understand the significant amount of plumbing that is taken care of for you with {binding Name} an why sometimes it seems to work and other times...

We will be concentrating on just how the binding works / what the notation means. Attendants will need to be very familiar with Entity Frameworks, SQL CE 3.5 local storage, Linq and Lambda notation to follow the demos. A cursory understanding of MVVM pattern concepts is also recommended.


I hope to see you all there.


Matthew

Where Firebug beats IE dev tools

by Matthew Hintzen on 6. March 2012 14:34

Microsoft is known for making great developer tools, Hell that’s why I’m a self confessed Microsoft FanBoi, it’s just EASY AS to program using their tools… with one exception, Ajax calls.  The built in tools in Visual Studio / IE are great for a lot of things but Asynchronous Javascript calls is not one of those things.

This post is for @tobint and @HumanCompiler on twitter.  I’ve been trying to defend using IE as my primary browser to my fellow worker @redjungle (Phil Gale to his friends), but as we have done more and more advanced Browser based applications with lots of jQuery and javascript and Ajax calls I finally realised that ultimately he was right.  For that job the correct tool is Firefox.  I complained about it to @HumanCompiler (suggesting that MS buy up company that makes firebug and make it the tool for IE Ajax debugging) I was referred to @tobint, who admitted he wasn’t a fan of FireBug, but wanted to know why I like Firebug, how it is “better” then the built in IE tools.

Trust me @tobint as a microsoft fanboi, it takes a lot to make me use someone else’s tools, (hell I used TFS 2005… so that shows I have a high pain threshold), but I just can’t disagree anymore.  If a tools job is to get in, get the info I need, and get out (and I believe that is what a tool should do) than for Ajax Firebug wins.

Let me show you with screen shots.

I have a jQuery Modal that asks for some input information, the user clicks it and then clicks save and an ajax call is made. Now of course my code wouldn’t have ANY bugs in it, so it will all work, but let’s imagine that maybe someday I don’t code up my

[HttpPost]
public ActionResult

correctly (yeah, I know, like that would ever happen) like maybe I put in a throw new NotImplementedException( ); by accident

So here is what I have to do to see the problem in IE
I load up the page and get my model and all I see is this

And it just sits there doing nothing (except spinning), no problem let’s fire up the Dev Tools by pressing F12… Whoops for some reason the F12 isn’t working (maybe that modal is interfering, not sure, but that’s ok, I’ll just go get it from the tool tips at the top)

It’s just two clicks, and here is what I get

Ok, so without looking at the help files, where do I go?, gee I’m not sure, maybe I need to refresh my page, and it will show me something.

so I do that and … Nope still nothing “obviously” wrong… Now at this point I happen to know (cause @tobint told me) that I can use the network tab, so I’ll go there.

Hmmm, nothing, I have to click “Start Capturing” I guess, hang on a moment didn’t I have to click on the Open Developer tools, doesn’t it seem like if I am asking for development assistance with my web page, that it should start capturing by default, I mean isn’t that really the logical thing to have happen?  Oh well, enough whinging, click the button and refresh the page again.

Now I get

It actually took me putting in this screen shot before I visually located the problem line, can you find it in one glance?  It’s the one that has the Red 500 (not exactly obvious, is it?).  Ok so click on that row and you get… a highlighted row… Ok, so double click on it

Ok this looks a little more useful, so let’s go tot he Response header and see what the return was

Yeah… that’s not too helpful, Let’s go look at the Response Body tab, maybe that will have more information.

Well there is the exception detail, inside a bunch of HTML, now I can extract the information I need from this page, but it isn’t going to happen in a glance, I’m going to have to do a little visual parsing.

Keep in mind when you are doing Web 2.0 development this is the most common action you will have to debug

Now let’s see the steps necessary in Firefox using Firebug.

I open Firefox and follow exactly the same steps as I did in IE (no pre-prepping the browser) to get to the problem screen

I don’t know the keyboard shortcut for Firebug, so I will just go up and click on the icon for it (only one click)

And I have

I don’t know about you, but I’m pretty sure I KNOW EXACTLY where thing broke after just one click. Let’s make sure, let me click on that bit row that is RED, and after a single click I have.

Well that’s good, here is my response html that I can parse thru and find the error, but wait, what’s that “HTML” tab? let’s click on it and see…

That sure seems a lot easier to read / debug now wouldn’t you agree @tobint?

 

So with IE, even if I did everything as quickly as I could it would take 8 clicks to find the information I want, and even then I would have to parse the raw HTML error page.

Firebug in Firefox took 3 clicks all in one window.

Gentlemen of the Jury… I rest my case!

Please feel free to let me know what you think of my comparison at @matthewhintzen on twitter.