Battle of the Third Party Controls

For those of you local to the Southwest Florida area and don’t know, there is a nice little .NET user group for developers, hobbyists, managers/owners, and various other IT-centric folks. The developer group is very welcoming to new visitors, always provide a great array of topics, and if you’re lucky, you might even get a bit of “swag” for attending one of the free meeetings.

On March 18th, 2009, the Naples .NET Usergroup put on a Battle of the Third Party Controls session which consisted of developers presenting on third party development controls they’ve used in their own projects. In the ring – Telerik, Infragistics, DevExpress, and FarPoint.

Here’s a quick recap:

Infragistics

Round 1: Infragistics

Well, Infragistics was battered, bloody, and bruised from the start. John Dunagan took his best shots at a set of developer controls that really didn’t want to play nice. Obviously, sometimes things happen with systems, user error, and Murphy’s Law; but too much went wrong to make Infragistics even a contender in this battle.

John pointed out difficulties while installing, references not automatically being added properly when dragging controls from the Visual Studio toolbox, and gasp, Visual Studio crashed while importing Infragistic Themes into the project during the demonstration. John had a lot to contend with.

Grade: D-

It was over when…

Unfortunately, before it began – and if you want more specifics, I pronounced it dead when we viewed source and saw a mindnumbing, mouse scrolling nightmare of Viewstate, excessive inline styles, and what looked like poor auto-generated HTML throughout a very basic UI sample.

Telerik

Round 2: Telerik

Telerik on the other hand, came out with gold trimmed, bright white trunks and a mean look ready for a fight. Philip Lippard had a pretty cushy job with Telerik providing a great source of pre-built, fully interactive examples publicly available on their website (http://www.telerik.com). Phil did a nice job demonstrating the controls, and with Telerik’s one-two punch of very attractive, and much lighter (relatively speaking) controls, Telerik was the star of the show and the favorite among developers.

Grade: B+, Though Telerik is a strong control suite, and a personal preference in most cases, I do hold criticism of some of the controls. For example, the Charting controls I strongly dislike and favor Component Art, a control company not featured in this battle.

It was over when…

Phil typed http://www.telerik.com into the address bar. Telerik does a great job with their website as well as their controls, and the marketing really shows through (in a good way).

DeveloperExpress

Round 3: DevExpress

Presented by Amy Hatfield, a darkhorse for me personally, DevExpress really impressed. Like a middleweight fighter moving up in the ranks, DevExpress was not as robust as Telerik, but certainly showed a lot and is a solid choice for a control suite. Amy did a very nice job showing off a lot of code-less (minimal) wiring of the grid control; featuring Ajax, paging, and detail template binding with little to no codebehind required.

I certainly intend on looking a little harder at DevExpress in the future.

Grade: B-

It was over when…

If this was a fight, it would have ended in a judges decision. Nothing knocked out DevExpress by any means, and is a viable scrapper through and through.

Farpoint Spread

Round 4: Farpoint

If the other three suites were fighters brawling it out, Farpoint would have been the bookie – involved, but not quite in the competition.

Farpoint was ready to go, energetic, and full of zest; but competing in a completely different arena than the other controls. Farpoint, as it turns out, is a Spreadsheet control that does one thing and does it well. Opening existing excel documents (regardless of version) seems to be a breeze, working interactively with bound or empty workbooks is smooth, and you should have no problem importing, exporting, or printing spreadsheet objects.

We were fortunate enough to have a presenter all the way from Raleigh, North Carolina, and I applaud his effort in presenting.

Grade: Not eligible. Unfortunately it’s just not a sound comparison with the other controls. Certainly I see some benefit to the control, but at the same time, I personally was not blown away by the product. For Office-heavy users who need an online spreadsheet solution, it sounds like the way to go.

It was over when…

For me, it was over when I realized it was just a spreadsheet. I have not come across a lot of client requests where they simply want an excel document or template online to do calculations/forms etc. For me, I would much rather relegate that to an HTML form with calculations occuring in code and presented with more of a “face” to it, rather than an older, spreadsheet-style UI.

It was a fun night, a good battle, and great people to share knowledge and viewpoints with. If you happen to be in the area, check out http://www.naplesdot.net for more information on future meetings.

Channel 9

P.S. – interviews with the presenters will be featured on Channel 9 by Russ Fustino from Microsoft. Check it out here!

Common Reusable Business Entities Part 3

This is part two of an ongoing series on common reusable business entities. This series does not need to be read in order, but here are the preceeding posts if you would like to catch up:

This will be the final article in a three part series regarding Common Business Entities. The original hope was to provide a a few common business objects that are used across most business-style applications. Additionally, I hoped to demonstrate at least a skeleton of business entitities that those unfamiliar with custom object creation could begin playing with.

In this article, we’ll cover a few important numbers; such as the CreditCardNumber, SocialSecurityNumber, and PhoneNumber objects. The structures do not have a lot of business logic (such as validation), because I am attempting to demonstrate very lean entities (as recently seen in WCF-style applications) and leave the logic up to the business layer to be called upon. If you chose to use these objects in your own application, please ensure you add validation as you could see unexpected results.

So let’s get to it:

CreditCardNumber Structure

public struct CreditCardNumber
{
    #region "PROPERTIES"

    /// <summary>
    /// The type of credit card (VISA, Mastercard, etc.).
    /// </summary>
    public enum CreditCardType
    {
        VISA,
        Mastercard,
        AmericanExpress,
        Discover
    }

    /// <summary>
    /// The credit card number without any formatting.
    /// </summary>
    public string Value { get; set; }

    /// <summary>
    /// The type of credit card (VISA, Mastercard, etc.).
    /// </summary>
    public CreditCardType Type { get; set; }

    /// <summary>
    /// The secure credit card CVV number.
    /// </summary>
    public int? CVV { get; set; }  //123

    /// <summary>
    /// The expiration month of the credit card as a two digit integer.
    /// </summary>
    public int ExpirationMonth { get; set; }  //12    //NOTE: This could be an enum or it's own object (Month), but for simplicity I am just showing an integer

    /// <summary>
    /// The expiration year of the credit card as a four digit integer.
    /// </summary>
    public int ExpirationYear { get; set; }  //2009

    /// <summary>
    /// The masked credit card number.
    /// </summary>
    public string Masked  //XXXXXXXXXXXX1234
    {
        get
        {
            string result = string.Empty;

            if (!string.IsNullOrEmpty(Value))
            {
                int length = Value.Length;
                string suffix = Value.Substring(length - 4, 4);
                return string.Format("XXXXXXXXXXXX{0}", suffix);
            }

            return result;
        }
    }

    #endregion

    #region "CONSTRUCTORS"

    /// <summary>
    /// Create a new credit card number.
    /// </summary>
    /// <param name="value"></param>
    /// <param name="expirationMonth"></param>
    /// <param name="expirationYear"></param>
    /// <param name="creditCardType"></param>
    public CreditCardNumber(string value, int expirationMonth, int expirationYear, CreditCardType creditCardType) : this()
    {
        Value = value;
        ExpirationMonth = expirationMonth;
        ExpirationYear = expirationYear;
        this.Type = creditCardType;
    }

    /// <summary>
    /// Create a new credit card number.
    /// </summary>
    /// <param name="value"></param>
    /// <param name="expirationMonth"></param>
    /// <param name="expirationYear"></param>
    /// <param name="creditCardType"></param>
    /// <param name="cvv"></param>
    public CreditCardNumber(string value, int expirationMonth, int expirationYear, CreditCardType creditCardType, int? cvv) : this()
    {
        Value = value;
        ExpirationMonth = expirationMonth;
        ExpirationYear = expirationYear;
        this.Type = creditCardType;
        CVV = cvv;
    }

    #endregion
}

SocialSecurityNumber Structure

public struct SocialSecurityNumber
{
    #region "PROPERTIES"

    /// <summary>
    /// The social security number without any formatting.
    /// </summary>
    public string Value { get; set; } //123121234

    /// <summary>
    /// A masked version of the social security number.
    /// </summary>
    public string Masked  //XXX-XX-1234
    {
        get
        {
            string result = string.Empty;

            if(Value.Length == 9)
            {
                result = string.Format("XXX-XX-{0}", Value.Substring(4, 4));
            }

            return result;
        }
    }

    #endregion

    #region "CONSTRUCTORS"

    /// <summary>
    /// Create a new social security number.
    /// </summary>
    /// <param name="value"></param>
    public SocialSecurityNumber(string value)
    {
        Value = value;
    }

    #endregion
}

PhoneNumber Structure

public struct PhoneNumber
{
    #region "PROPERTIES"

    /// <summary>
    /// The phone number without any formatting.
    /// </summary>
    public string Value { get; set; } //2395551234

    /// <summary>
    /// The areacode of the phone number.
    /// </summary>
    public string Areacode //239
    {
        get
        {
            string result = string.Empty;

            if(Value.Length == 10)
            {
                result = Value.Substring(0, 3);
            }

            return result;
        }
    }

    /// <summary>
    /// The prefix of the phone number.
    /// </summary>
    public string Prefix //555
    {
        get
        {
            string result = string.Empty;

            if(Value.Length == 10)
            {
                result = Value.Substring(3, 3);
            }

            return result;
        }
    } 

    /// <summary>
    /// The suffix of the phone number.
    /// </summary>
    public string Suffix //1234
    {
        get
        {
            string result = string.Empty;

            if(Value.Length == 10)
            {
                result = Value.Substring(6, 4);
            }

            return result;
        }
    } 

    /// <summary>
    /// The masked phone number.
    /// </summary>
    public string Masked //(XXX)XXX-1234
    {
        get
        {
            string result = string.Empty;

            if(Value.Length == 10)
            {
                result = string.Format("(XXX)XXX-{0}", Suffix);
            }

            return result;
        }
    }

    #endregion

    #region "CONSTRUCTORS"

    public PhoneNumber(string value) : this()
    {
        Value = value;
    }

    #endregion

    #region "METHODS"

    public override string ToString()
    {
        return string.Format("({0}){1}-{2}", Areacode, Prefix, Suffix);
    }

    #endregion
}

One Very Important Note

I’ve mentioned it already, but please note that in an effort to make these lean, I have not really added any validation to the entities. This is most certainly something you will need to add, perhaps in the entity itself, a business layer, or wherever you see fit to add this validation. You will certainly get unexpected results without validating your entities. Also, it may be appropriate to throw exceptions when attempting to retrieve properties that are dependent on another property that is not filled out. Here, I’ve simply returned an empty string in most cases, so it depends on your preferential usage.

As always, please feel free to add any comments about entities, best practices, code standards, structure, pro’s and con’s of logic within entities, and any other goodies you would like to contribute. Good luck and happy coding!

Common Reusable Business Entities Part 2

This is part two of an ongoing series on common reusable business entities. This series does not need to be read in order, but here are the preceeding posts if you would like to catch up:

In this post, we’re going to examine a short but useful structure that can be used in almost every business-style application you will write; the Address structure.

Address Structure

The Address structure specifies information regarding a physical mailing address. Obviously, this is used all over the place, but a few examples include E-Commerce shipping and billing addresses, user profile addresses, and a foundation for geocoding property/locations. Overall, all of the entities to be demonstrated in this short series are meant to be thin, without a lot of functionality or business logic.

public struct Address
{
    #region "PROPERTIES"

    /// <summary>
    /// The prefix of the street address.  (Ex: 1400 Pennsylvania Ave.)
    /// </summary>
    public string Address1 { get; set; }

    /// <summary>
    /// The suffix of the street address.  (Ex: Apt. 14)
    /// </summary>
    public string Address2 { get; set; }

    /// <summary>
    /// The city of the address.
    /// </summary>
    public string City { get; set; }

    /// <summary>
    /// The state of the address (2 character string - could be entity).
    /// </summary>
    public string State { get; set; }  //NOTE: it may be more appropriate to have this as an object

    /// <summary>
    /// The country of the address (3 character representation: USA, CHN, JPN)
    /// </summary>
    public string Country { get; set; }  //NOTE: it may be more appropriate to have this as an object

    /// <summary>
    /// The postal code of the address.
    /// </summary>
    public string Zip { get; set; }  

    #endregion

    #region "CONSTRUCTORS"

    /// <summary>
    /// Constructor for address.
    /// </summary>
    /// <param name="address1"></param>
    /// <param name="address2"></param>
    /// <param name="city"></param>
    /// <param name="state"></param>
    /// <param name="country"></param>
    /// <param name="zip"></param>
    public Address(string address1, string address2, string city, string state, string country, string zip)
        : this()
    {
        this.Address1 = address1;
        this.Address2 = address2;
        this.City = city;
        this.State = state;
        this.Country = country;
        this.Zip = zip;
    }

    #endregion

    #region "METHODS"

    /// <summary>
    /// Overrides ToString() to display default address format (Address1 Address2 City, State Zip).
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        return (this.Address1 + " " + this.Address2 + " " + this.City + ", " + this.State + " " + this.PostalCode);
    }

    #endregion
}

What’s Next?

I’ll cover three short entities in the next part; SocialSecurityNumber, CreditCardNumber and PhoneNumber, so feel free to come back and check it out. Also, please add any requests for specific common entities or add your own in the comments below.

Common Reusable Business Entities Part 1

I noticed that several of my projects both for work and hobby tend to reuse certain common business entities. A Name structure is pretty much always the same (A name is a name is a name… maybe that’s a horse – nevermind the sidetrack.) In most cases, these entities were needed without change or very little change and therefore made for a great base entity to build off of.

Here are a few common entities that you may like to begin building a library with. You never know, it may save some time and build more consistency throughout several of your applications.

PersonName Structure: What’s in a Name?

The PersonName structure specifies a person’s name, including first, middle, and last as well as common prefix and suffix values. It has built in formatting options that you may like to expand on. Overall, all of the entities to be demonstrated in this short series are meant to be thin, without a lot of functionality or business logic.

public struct PersonName
{
    #region "ENUMERATIONS"
    /// <summary>
    /// The prefix to a persons name.
    /// </summary>
    public enum PrefixType
    {
        [Description("~")]
        NONE,
        [Description("Dr.")]
        DR,
        [Description("Father")]
        FATHER,
        [Description("Miss")]
        MISS,
        [Description("Mr.")]
        MR,
        [Description("Mrs.")]
        MRS,
        [Description("Ms.")]
        MS,
        [Description("Prof.")]
        PROF,
        [Description("Rev.")]
        REV
    }

    /// <summary>
    /// The suffix to a persons name.
    /// </summary>
    public enum SuffixType
    {
        [Description("~")]
        NONE,
        [Description("I")]
        I,
        [Description("II")]
        II,
        [Description("III")]
        III,
        [Description("IV")]
        IV,
        [Description("Jr.")]
        JR,
        [Description("Sr.")]
        SR,
        [Description("1st")]
        FIRST,
        [Description("2nd")]
        SECOND,
        [Description("3rd")]
        THIRD,
        [Description("4th")]
        FOURTH,
        [Description("D.D.S.")]
        DDS,
        [Description("M.D.")]
        MD,
        [Description("Ph.D.")]
        PHD
    }

    /// <summary>
    /// The display format for a persons name.
    /// </summary>
    public enum DisplayMode
    {
        //TODO: add any additional formats needed
        LastFirstMiddle,
        LastFirstMiddleInit,
        LastFirst,
        FirstMiddleLast,
        FirstMiddleInitLast,
        FirstLast
    }
    #endregion

    #region "PROPERTIES"
    /// <summary>
    /// Persons first name.
    /// </summary>
    public string First { get; set; }

    /// <summary>
    /// Persons middle name.
    /// </summary>
    public string Middle { get; set; }

    /// <summary>
    /// Persons last name.
    /// </summary>
    public string Last { get; set; }

    private string _MiddleInit;
    /// <summary>
    /// Persons middle initial.
    /// </summary>
    public string MiddleInit
    {
        get
        {
            if (string.IsNullOrEmpty(this._MiddleInit) == true)
            {
                if (string.IsNullOrEmpty(this.Middle) == false)
                {
                    return this.Middle.Substring(0, 1);
                }
                else
                {
                    return string.Empty;
                }
            }
            else
            {
                return this._MiddleInit;
            }
        }
        set
        {
            _MiddleInit = value.Substring(0, 1);
        }
    }


    /// <summary>
    /// Persons name prefix (Mr., Mrs., Ms., etc.)
    /// </summary>
    public PrefixType? Prefix { get; set; }

    /// <summary>
    /// Persons name suffix (Jr., Sr., etc.)
    /// </summary>
    public SuffixType? Suffix { get; set; }

    #endregion

    #region "CONSTRUCTORS"

    /// <summary>
    /// PersonName constructor.
    /// </summary>
    public PersonName(string first, string last)
        : this()
    {
        First = first;
        Last = last;
    }

    public PersonName(PrefixType? prefix, string first, string middle, string last, SuffixType? suffix)
        : this()
    {
        Prefix = prefix;
        First = first;
        Middle = middle;
        Last = last;
        Suffix = suffix;
    }

    #endregion

    #region "METHODS"
    /// <summary>
    /// Overrides ToString() to display default name format (Last, First Middle).
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
        return String.Format("{0}, {1} {2}", this.Last, this.First, this.Middle);
    }

    /// <summary>
    /// Additional ToString() to display name by specified format.
    /// </summary>
    /// <param name="dm"></param>
    /// <returns></returns>
    public string ToString(DisplayMode dm)
    {
        //TODO: add all the rest of the formats needed here
        switch (dm)
        {
            case DisplayMode.LastFirstMiddleInit:
                return string.Format("{0}, {1} {2}", this.Last, this.First, this.MiddleInit);
                break;
            case DisplayMode.LastFirst:
                return string.Format("{0}, {1}", this.Last, this.First);
                break;
            case DisplayMode.FirstMiddleLast:
                return string.Format("{0} {1} {2}", this.First, this.Middle, this.Last);
                break;
            case DisplayMode.FirstMiddleInitLast:
                return string.Format("{0} {1} {2}", this.First, this.MiddleInit, this.Last);
                break;
            case DisplayMode.FirstLast:
                return string.Format("{0} {1}", this.First, this.Last);
                break;
            default:
                return ToString();
                break;
        }
    }

    #endregion
}

What’s Next?

I’ll cover an Address structure in the next part, so feel free to come back and check it out. Also, please feel free to add any requests for specific common entities or add your own in the comments below.

Converting an ASP.NET 2.0 project to ASP.NET 2.0 AJAX project

This is nothing new and certainly something you can find all over the internet, however, I wanted to have a local record of it for times when I need to remember all of the steps involved for converting (relatively) older web-applications.
I’ve used this set of instructions recently to convert an older application to use AJAX (but a requirement indicated that it remain ASP.NET 2.0 and not fully upgraded to the latest version of ASP.NET (current 3.5 at the time of this posting). So the instructions should be pretty solid at this point. Please feel free to add any additional “gotcha’s” or information on converting ASP.NET applications in the comments below if you would like.

In case you’re just skimming this article, please be aware this is geared for ASP.NET 2.0, which is NOT the most recent version of ASP.NET, Ajax, or the Ajax Control Toolkit. So if you’re intending to upgrade to 3.5 (or anything later), please make sure to pick up the latest files and, if necessary, instructions.

Step 1: Software

First, we need to install ASP.NET 2.0 Ajax 1.0 Extensions. This file can be downloaded from Microsoft here. No big deal here, simply download and install the package.

Note: You may also want to download the AJAX Control Toolkit for ASP.NET 2.0 for additional controls, this can be found here.

Step 2: Project References

In your web application project, add the following references to your project:

  • System.Web.Extensions
  • optionally: AjaxControlToolkit.dll – I generally pull this from the SampleWebSite\bin directory included in the Ajax Control Toolkit download.

You will also need to add the following page reference when using Ajax Script Manager and Ajax-specific controls on each page containing these controls (or a masterpage):

<%@Register Assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
    Namespace="System.Web.UI" TagPrefix="asp" %>

Step 3: Web.config

This is where the bulk of the work is, but don’t worry – it’s all cut and paste.

Inside <configSections></configSections>:

<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
      <sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
        <section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication"/>
        <sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
          <section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="Everywhere"/>
          <section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication"/>
          <section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" allowDefinition="MachineToApplication"/>
        </sectionGroup>
      </sectionGroup>
</sectionGroup>

Add below <compilation></compilation:

<httpHandlers>
      <remove verb="*" path="*.asmx"/>
      <add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      <add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/>
</httpHandlers>

Add inside <httpModules></httpModules>:

<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>

Add below </system.web>:

<system.web.extensions>
    <scripting>
      <webServices>
        <!-- Uncomment this line to customize maxJsonLength and add a custom converter -->
        <!--
<jsonSerialization maxJsonLength="500">
<converters>
<add name="ConvertMe" type="Acme.SubAcme.ConvertMeTypeConverter"/>
</converters>
</jsonSerialization>
-->
        <!-- Uncomment this line to enable the authentication service. Include requireSSL="true" if appropriate. -->
        <!--
<authenticationService enabled="true" requireSSL = "true|false"/>
-->
        <!-- Uncomment these lines to enable the profile service. To allow profile properties to be retrieved
and modified in ASP.NET AJAX applications, you need to add each property name to the readAccessProperties and
writeAccessProperties attributes. -->
        <!--
<profileService enabled="true"
readAccessProperties="propertyname1,propertyname2"
writeAccessProperties="propertyname1,propertyname2" />
-->
      </webServices>
      <!--
<scriptResourceHandler enableCompression="true" enableCaching="true" />
-->
    </scripting>
  </system.web.extensions>

Add below </system.web.extensions>:

<system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <modules>
      <add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    </modules>
    <handlers>
      <remove name="WebServiceHandlerFactory-Integrated"/>
      <add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      <add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      <add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    </handlers>
  </system.webServer>

At Least One Gotcha

Whenever you deal with web.config settings, especially for pre-existing websites/webapplications, there could
be settings that may conflict with new functionality provided with AJAX; one that I ran into was the following tag:

<xhtmlConformance mode=”Legacy”/>

This tag needs to be commented out or set to a different mode (Transitional, Strict). You can read more about it from Scott Guthrie’s post, or simply comment it out and move on.

Note: Scott Guthrie has another post on gotcha’s for Ajax as well as Visual Studio, UI, and other general Microsoft Developer gotcha’s, it’s certainly worth a few minutes to check out.