Sunday, December 5, 2010

WCF Services Assisting ASP.NET Ajax

We love the Telerik ASP.NET Ajax Control set and often rely on it for our Ajax communication in ASP.NET. But sometimes the need for efficiency out weights the ease and maintenance of the plug and play solutions.

We needed a way to eliminate the page request that happens when interacting with one of our ajaxified controls. UpdatePanels and Telerik's AjaxManager still require the page to go through its entire lifecycle so that a portion of the page can be sent back to the browser. We ventured down a few paths that would allow us to use a usercontrol without loading the hosting page, but those solutions were a hack job at best.

It turns out that using WCF Services within ASP.NET is quite an elegant solution when having to roll your own AJAX solution. With the proper configuration, the services can run along the ASP.NET pipeline giving you access to authentication and authorization events and the user's session. The services can even handle writing your response to JSON.

Add a WCF Service to your ASP.NET Web Application. Then double check your web.config to make sure that it has the enableWebScript attribute declared and the aspNetCompatibilityEnabled is set to true. The enableWebScript and aspNetCompatibilityEnabled attributes make the WCF service able to participate in the ASP.NET pipeline.

<system.serviceModel>
  <behaviors>
    <endpointBehaviors>
      <behavior name="WcfServices.EndpointBehavior">
        <enableWebScript /> <!-- required for ASP.NET pipeline integration -->
      </behavior>
    </endpointBehaviors>
    <serviceBehaviors>
      <behavior name="WcfServices.ServiceBehavior">
        <serviceMetadata httpGetEnabled="true" /> <!-- required for RESTful services -->
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
    multipleSiteBindingsEnabled="true" />
  <services>
    <service name="Demo.WcfServices.NewsFeedService" behaviorConfiguration="WcfServices.ServiceBehavior">
      <endpoint behaviorConfiguration="WcfServices.EndpointBehavior"
        binding="webHttpBinding" contract="Demo.WcfServices.ServiceContracts.INewsFeedService" />
    </service>
  </services>
</system.serviceModel>

You can consume your WCF services by adding a ServiceReference on a page.

<asp:ScriptManagerProxy runat="server">
    <Services>
        <asp:ServiceReference Path="~/WcfServices/NewsFeedService.svc" />
    </Services>
</asp:ScriptManagerProxy>

By adding the ServiceReference, the page will add a javascript include to a proxy class that contains the service prototypes. It contains the prototypes that you will use when calling the service from javascript. To see the javascript proxy class, navigate to the SVC file URL and add either "/JS" or "/JSDEBUG" to the URL. Examine the output for the javascript function prototypes.

As you can see below, the javascript WCF call looks clean and is easy to follow. This approach has many advantages and it decouples us from the tedious page lifecycle that is induced through UpdatePanels and Telerik's Ajax framework.

function getLatestNews(lastNewsId) {
    Demo.INewsFeedService.GetLatestNews(lastNewsId, callbackSuccess, callbackFailed);
}
function callbackSuccess(result) {
    $("#newsFeed").prependTo(result);
}

In our case we used AJAX to WCF in order to get an updated XHTML fragment. Instead of using a usercontrol for presenting the dataset, we used XSLT to render the XHTML for the control. This makes it easy for the WCF AJAX call to return XHTML fragments that can be replaced on the page at the AJAX callback. So go ahead and trade in your UpdatePanels for WCF Services next time you have the need for a specialized AJAX call.

Links

Wednesday, November 10, 2010

Visual Studio 2010: ASP.NET ControlBuilder MakeGeneric Workaround

Working with enumerations is very code friendly and we use them often especially when we are referencing a lookup table.  The ORM that we use (LLBLGen Pro) released its latest version recently and they make it even easier for us to map an enumeration to an entity field.

Sometimes these lookup tables need to be presented to the end user for whatever reason - be it a drop down, radio button list or a check box list.  When working in VS 2008 I stumbled onto this blog that outlines a straight forward way to create a generic drop down that exposes the the selected value as the specified generic Enum. Using that approach, we centralized all of the enum parsing that goes on for the gets and sets when working with the selected items. We took that implementation and created a generic dropdown and checkbox list.  It worked great in our environment at the time which was Visual Studio 2008.

Here is the checkbox list control that we were using:
public class EnumCheckBoxList<T> : EnumCheckBoxList where T : struct
{
    public List<T> SelectedEnumValues
    {
        get
        {
            List<T> items = new List<T>(base.Items.Count);
            foreach (ListItem item in base.Items)
            {
                if (item.Selected)
                    items.Add((T)Enum.Parse(typeof(T), item.Value));
            }
            return items;
        }
        set
        {
            base.ClearSelection();

            foreach (T item in value)
                base.Items.FindByValue(Enum.GetName(typeof(T), item)).Selected = true;
        }
    }

    /// <summary>
    /// Only valid for use on bit flagged enumerations
    /// </summary>
    public T? SelectedFlaggedEnumValues
    {
        get
        {
            ulong values = 0;
            foreach (ListItem item in base.Items)
            {
                if (item.Selected)
                    values = values | Convert.ToUInt64((T)Enum.Parse(typeof(T), item.Value));
            }
            return values == 0 ? null : (T?)Enum.Parse(typeof(T), values.ToString());
        }
        set
        {
            if (value != null)
            {
                Type enumType = typeof(T);
                foreach (Enum e in Enum.GetValues(enumType))
                {
                    ulong singleEnumFromFlagResult = 0;
                    if ((singleEnumFromFlagResult = (Convert.ToUInt64(e) & Convert.ToUInt64(value.Value))) > 0)
                    {
                        Enum singleEnumFromFlag = (Enum)Enum.Parse(enumType, singleEnumFromFlagResult.ToString());
                        base.Items.FindByValue(Enum.GetName(enumType, singleEnumFromFlag)).Selected = true;
                    }
                }
            }
        }
    }
}

[ControlBuilder(typeof(EnumCheckBoxListControlBuilder))]
public partial class EnumCheckBoxList : CheckBoxList
{
    public string EnumTypeName { get; set; }
}

public class EnumCheckBoxListControlBuilder : ControlBuilder
{
    public override void Init(TemplateParser parser, ControlBuilder parentBuilder, Type type, string tagName, string id,
                              System.Collections.IDictionary attribs)
    {

        string enumTypeName = (string)attribs["EnumTypeName"];
        Type enumType = Type.GetType(enumTypeName);
        if (enumType == null)
            throw new Exception(string.Format("Type for enum {0} can not be created", enumTypeName));

        Type dropDownType = typeof(EnumCheckBoxList<>).MakeGenericType(enumType);
        base.Init(parser, parentBuilder, dropDownType, tagName, id, attribs);
    }
}

Today our new development is done primarily in Visual Studio 2010.  We needed the same functionality for the generic dropdown and checkbox list for our Nucleus project but upon bringing over our implementation we discovered that the ControlBuilder wasn't updating the .designer file - so our controls weren't being converted to their generic implementations. After a little Googling, I found a bug entered in Microsoft Connect that described the issue we are having with VS 2010 and it seems that there isn't any workaround currently available for our situation using the ControlBuilder implementation.

The generic control was nice because programmers wouldn't have to go back and forth suppling plumbing to parse string values into enumerations and vise versa. We took the same idea and instead created a few extension methods.  The extension methods shown below have a get and set defined for a list of enumeration values and also a get and set for enumerations that are decorated with the FlagsAttribute.

/// <summary>
/// Get the selected enumeration values in the listbox
/// </summary>
/// <typeparam name="T">The type of enumeration represented in the listbox</typeparam>
/// <param name="list">The listbox</param>
/// <returns>Returns the list of selected enumeration values in the listbox</returns>
public static List<T> GetSelectedEnumValues<T>(this ListControl list) where T : struct
{
    List<T> items = new List<T>(list.Items.Count);
    foreach (ListItem item in list.Items)
    {
        if (item.Selected)
            items.Add((T)Enum.Parse(typeof(T), item.Value));
    }
    return items;
}

/// <summary>
/// Select the values in the given list of enums in the listbox.
/// </summary>
/// <typeparam name="T">The type of enumeration represented in the listbox</typeparam>
/// <param name="list">The listbox</param>
/// <param name="valuesToSelect">The enum values to select in the listbox</param>
public static void SetSelectedEnumValues<T>(this ListControl list, List<T> valuesToSelect) where T : struct
{
    list.ClearSelection();

    foreach (T item in valuesToSelect)
        list.Items.FindByValue(Enum.GetName(typeof(T), item)).Selected = true;
}

/// <summary>
/// Get the selected enumeration values in the listbox. This method should be called if the enumeration is decorated with the Flags attribute.
/// </summary>
/// <typeparam name="T">The type of enumeration represented in the listbox. This enumeration must be decorated with the Flags attribute</typeparam>
/// <param name="list">The listbox</param>
/// <returns>Returns the selected enumeration values in the listbox as a flagged enumeration. If no values are selected, null is returned</returns>
public static T? GetSelectedFlaggedEnumValues<T>(this ListControl list) where T : struct
{
    ulong values = 0;
    foreach (ListItem item in list.Items)
    {
        if (item.Selected)
            values = values | Convert.ToUInt64((T)Enum.Parse(typeof(T), item.Value));
    }
    return values == 0 ? null : (T?)Enum.Parse(typeof(T), values.ToString());
}

/// <summary>
/// Select the values in the given flag enumeration in the listbox.
/// </summary>
/// <typeparam name="T">The type of enumeration represented in the listbox. This enumeration must be decorated with the Flags attribute</typeparam>
/// <param name="list">The listbox</param>
/// <param name="valuesToSelect">The enum values (represented as Flags) to select in the listbox</param>
public static void SetSelectedFlaggedEnumValues<T>(this ListControl list, T valuesToSelect) where T : struct
{
    list.ClearSelection();

    Type enumType = typeof(T);
    foreach (Enum e in Enum.GetValues(enumType))
    {
        ulong singleEnumFromFlagResult = 0;
        if ((singleEnumFromFlagResult = (Convert.ToUInt64(e) & Convert.ToUInt64(valuesToSelect))) > 0)
        {
            Enum singleEnumFromFlag = (Enum)Enum.Parse(enumType, singleEnumFromFlagResult.ToString());
            list.Items.FindByValue(Enum.GetName(enumType, singleEnumFromFlag)).Selected = true;
        }
    }
}

Hopefully Microsoft responds to the Connect bug with a fix in an upcoming service pack.  In the mean time these extension methods will do the trick.

Links

Friday, November 5, 2010

First Sprint in Review

Wow it is hard to believe that it has been two weeks and we have finished our first Sprint.  Here is how it came out.

Committed Tasks
We completed all but a couple of the tasks we committed in the Sprint.  We under estimated the few learning curves that were introduced by our new environment which caused delays early in the Sprint.

Review (Demo)
Everyone demonstrated the features that they worked on during the Sprint.  We all knew what to expect because we were all involved in the planning process.

Retrospective
We noticed a few things that we would change in the next Sprint.

  1. Use the Work Remaining field on the TFS Tasks to help us better gauge progress and future planning.
  2. When UI elements are "done", gather the team for a quick demo.
    • We want to collect input early to provide a better polished product at the end of the Sprint.

The formal structure and Daily Scrum is welcomed by the team.  If you are not already using an agile process like Scrum, give it a try - you will be surprised with the results.

Monday, October 25, 2010

New Project - Welcome Scrum

Before today, we were working in an informal agile process with fairly short development cycles. We work as a team but often developers would be unaware of new features that others were working on. A big problem with this is that it makes it difficult for another team member to pick up where someone left off.

Enter our new project, code name Nucleus.  Dave and I have been planning for a few months the vision and features of the project and we formally kicked it off today - Hooray!  We were introduced to a new project process called Scrum through Visual Studio 2010 and Telerik's Team Pulse product.  Scrum is an agile practice that gets the entire team involved in nearly the entire process from planning to tasks to conducting demos of the work completed.

With our first stab of Scrum, we will start with the following 2 week Sprint Schedule.
  • Day 1: Sprint Planning Meetings
    • Part I (4 hours max): Participants include Product Owner and Scrum Master
      • Determine focus of the Sprint and flag possible Sprint Backlog items
    • Part I (4 hours max): All Team Members participate
      • Discuss in detail each flagged Sprint Backlog item
        • Use Scrum Poker to estimate effort
      • Determine actual Sprint Backlog and assign work to team members
  • Day 2-10Daily Sprint Meeting (15 minute max)
    • Team members answer:
      • What have you done since the last meeting?
      • What will you do from now until the next meeting?
      • Is there anything holding up your work?
  • Day 6Requirements Planning (4 hour max)
      • Participants include Product Owner and Scrum Master
        • Add and edit the Product backlog
        • Additional stakeholders may be invited
  • Day 10: Sprint Review and Retrospective (4 hour max)
    • Demo functionality added from the Sprint.
    • Reflect on the Sprint to determine what worked, what didn't work and what would we change in the next Sprint.
Additionally we sprinkled into Sprint a few formal code reviews.

We had an outstanding Day 1 in our first ever Sprint.  We played a little Scrum Poker, talked about backlog items and by the end of the day we all had our tasks outlined for the rest of the Sprint.

Links