# Wednesday, April 11, 2007
There may be a time you need access to the current executing ASP.NET handler's (page) view state without having a reference to the executing page.  I did today while creating a view state adapter that is injected into my page controllers.  I swapped out the Session adapter for this new ViewState adapter in my Spring.NET config.

Here's the code where I grab a view state reference in the adapter:

return ((PageBase)System.Web.HttpContext.Current.Handler).PageViewState;

Basically I grab the current handler and cast it to PageBase type.  In my application all pages inherit from PageBase, and PageBase exposes a public PageViewState property.  This allows me to grab the viewstate without having a direct reference to the page instance.  Some of this extra work is required because the System.Web.UI.Page ViewState property is not public, but protected.

Wednesday, April 11, 2007 8:53:47 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Friday, April 06, 2007
Before running NAnt that uses the P4Edit NAntContrib task ensure that you go into P4Win and from the menu select Settings | Use Current as Default.  Without doing this you may run into an error message that [some file] - must refer to client '[client name]'.

Ant
Friday, April 06, 2007 9:16:52 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
I'm not sure hardware currently supports this, but it would be really cool if there was a web browser GPS control, header - or something to that effect.  Basically the idea is to be able to perform a search through your web browser that uses your GPS location as one of the search parameters.  This would be incredibally useful for handheld internet devices.  My navi in my truck already does this when I need to find a gas station, bank, or restaurant nearby.  Handheld browsers should be able to do the same without the user needing to manually input a GPS coordinate.  Think realestate listings.  You're driving down a nice neighborhood and you want to search for all listed houses within 3 miles, with GPS coords this would be a snap.  What if you are standing inside a store?  You could go directly to the store's web site just because you are physically standing inside it.  I think people would really dig this - I know I would.  What about location aware internet advertising?  This is just the tip of the iceberg, especially once everyone has a phone that also acts as an internet connectivity device.

Friday, April 06, 2007 5:46:32 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Tuesday, March 27, 2007
I have finally come to the realization that my mindset, beliefs, programming methodology, and above all my spirit are incompatable with my current employer.  I need to work for IT company again.

WTF
Tuesday, March 27, 2007 9:59:20 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [1]  | 
# Thursday, March 22, 2007
"ctl00___ctl00___ctl00_ctl00_bcr_ctl00___Comments___Comments_ctl06_NameLink" Why do you mock me?!  Why do you make something so blatantly easy, so cumbersome and impossibly difficult?  Its for my own protection you say?  I can't be trusted you say?

Clearly when MS brought out ASP.NET they never  really intended for programmers to do client side programming, because its too difficult for Mort to even understand the difference between client side code versus server side code.  I've explained the difference to Mort several times, however I still don't think he truly understands the difference.  I blame the leaky abstraction that is ASP.NET for making it too difficult for Mort to truly understand what's really happening under the hood of ASP.NET.

Thursday, March 22, 2007 4:36:28 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, March 19, 2007
For no real good reason I decided to implement another reusable IComparer, this time using .NET 2.0 and generics.  My focus this time was on:
  • Better API using generics.
  • SQL compliant ordering clause.
  • Multi-property sort ordering.

GenericComparer<Address> comparer = new GenericComparer<Address>()
   .OrderBy("FirstName, LastName DESC");

As you might guess this IComparer will sort a collection of Address objects by FirstName and then by LastName descending.  The code could still use some cleaning up and I plan on replacing all of the reflection calls using lightweight code generation, but nonetheless it does pass my current set of tests.

public class GenericComparer<T> : IComparer<T>
{
    Type declaringClassType = typeof(T);
    IList<PropertyOrderBy> properties = new List<PropertyOrderBy>();

    /// <summary>
    /// Default ctor instantiates a new GenericComparer instance.
    /// </summary>
    public GenericComparer() { }

    public GenericComparer<T> OrderBy(string sqlOrderBy)
    {
        string[] parts = sqlOrderBy.Split(',');
        foreach (string part in parts)
        {
            string[] orderbyParts = part.Trim().Split(' ');
            string fieldName = orderbyParts[0].Trim();
            string direction = string.Empty;

            if (orderbyParts.Length > 1)
                direction = orderbyParts[1].Trim();

            properties.Add(new PropertyOrderBy(fieldName, direction));
        }

        return this;
    }

    private IComparable GetPropertyValue(object instance, string propertyName)
    {
        // This won't work if the property is overloaded by type
        PropertyInfo info = declaringClassType.GetProperty(propertyName);
        object val = info.GetValue(instance, null);

        IComparable retVal = val as IComparable;
        if (retVal == null)
        {
            throw new ApplicationException(
             propertyName +
             " Type must implement IComparable to be able to use a PropertyComparer.");
        }

        return retVal;
    }

    #region IComparer<T> Members

    public int Compare(T lhsObj, T rhsObj)
    {
        int result = 0;

        foreach (PropertyOrderBy orderBy in properties)
        {
            IComparable lhs = GetPropertyValue(lhsObj, orderBy.PropertyName);
            IComparable rhs = GetPropertyValue(rhsObj, orderBy.PropertyName);

            if (orderBy.SortDirection == "DESC")
                result = rhs.CompareTo(lhs);
            else
                result = lhs.CompareTo(rhs);

            if (result != 0)
                return result;
        }

        return result;
    }

    #endregion    

    private class PropertyOrderBy
    {
        public string PropertyName;
        public string SortDirection = "ASC";

        public PropertyOrderBy(string propertyName, string direction)
        {
            direction = direction.ToUpper();
            if (direction != "ASC" && direction != "DESC" && direction != string.Empty)
                throw new ArgumentOutOfRangeException("direction", "Unknown order by direction: " + direction + ", expected either ASC or DESC.");

            if (direction != string.Empty)
                this.SortDirection = direction;

            this.PropertyName = propertyName;
        }
    }
}

Monday, March 19, 2007 6:37:10 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Sunday, March 18, 2007
For whatever reason when I cut and paste code into DasBlog using the "Insert Code" feature, my tabs always end up missing - even though they initially show up right after clicking Parse Code.  I ended up having to find and replace all blocks of four spaces with a tab before pasting into DasBlog.

The missing tab problem and the fact the syntax highlighting isn't as nice as Visual Studio 2005 makes me think I should start looking for another tool to format my code snippets that appear in this blog.

Sunday, March 18, 2007 7:02:58 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Saturday, March 17, 2007

To get my WatiN tests working in predictable and repeatable manner I ended up integrating Spring.NET into my application so that I could swap out the data service implementation through configuration.  When running under WatiN the stubbed data service is used instead of the production web service.  Having a predictable set of test data is critically important, especially since some of my test assertions are looking for specific data values in the rendered HTML, which would be impossible if I was hitting the actual web service for data.

I integrated Spring.NET into the ASP.NET 1.1* application I'm working on and so far I'm very happy with it.  What I like about it:

  • XML configuration almost exactly the same as Spring in Java (which I've been using pretty heavily for the last 3 months).
  • Tight ASP.NET integration, so I have no code dependencies on any of the Spring namespaces in my code.  None.  Zero.
  • There are data validation libraries and other features which may be useful in the future.
  • It just works.

So check it, here's my code behind for the Search.ascx, notice how the controller instance is set via the Controller property by Spring.  Then the view instance (the Search user control instance itself) is set into the controller in the Initialize method call on page load.  The one other thing that might seem strange is that I'm creating the SearchCriteria instance directly from the user control's Request collection rather than using declared ASP.Net controls; sometimes straight HTML controls are just what we need.

public class Search : ControlBase, ISearchView
{
   protected System.Web.UI.WebControls.LinkButton m_btnSearch;
   protected System.Web.UI.WebControls.DataGrid m_resultsGrid;
   private SearchController m_controller;

   private void Page_Load(object sender, System.EventArgs e)
   {
      Controller.Initialize(this, !Page.IsPostBack);
   }

   public void SortSearchResults(object sender, DataGridSortCommandEventArgs e)
   {
      Controller.Sort(e.SortExpression);
   }

   public void SearchClick(object sender, EventArgs e)
   {
      Controller.Search(Criteria);
   }

   public SearchCriteria Criteria
   {
      get
      {
         SearchCriteria criteria = new SearchCriteria();
         criteria.BorrowerId     = Request.Form["txtBorrowerId"];
         criteria.BorrowerName   = Request.Form["txtBorrowerName"];
         criteria.BusinessLine   = Request.Form["txtBusinessLine"];
         criteria.LoanNumber     = Request.Form["txtLoanNumber"];
         criteria.UnderwriterUid = Request.Form["txtUnderwriterUid"];

         return criteria;
      }
   }

   /// <summary>
   /// Injected by Spring.NET.
   /// </summary>
   public SearchController Controller
   {
      get { return m_controller; }
      set { m_controller = value; }
   }

   #region ISearchView Members

   public void setResults(IList results)
   {
      m_resultsGrid.DataSource = results;
      m_resultsGrid.DataBind();
   }

   #endregion

}

And here's the Spring.NET specific configuration for the Search page, this does all the wiring via configuration of the different objects.

<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net">

    <object type="Search.ascx">
        <property name="Controller" ref="SearchController" />
    </object>

    <object id="SearchController" type="CRR.Web.Controllers.SearchController, CRR.Web">
        <constructor-arg name="dataService" ref="DataService" />
        <constructor-arg name="stateProvider" ref="SessionStateProviderAdapter" />
    </object>
   
    <object id="SessionStateProviderAdapter"
            type="CRR.Web.Controllers.SessionStateProviderAdapter, CRR.Web"/>


    <!-- Production data service implmentation -->
    <!-- <object id="DataService" type="CRR.Domain.DataService, CRR.Domain"/> -->
   
    <!-- Stubbed data service implmentation for testing -->

    <object id="DataService" type="CRR.Domain.DataServiceStub, CRR.Domain"/>   
   
</objects>

There is also a Spring provided HttpHandler and an HttpModule for Spring in the web.config.  This is the important part.  By registering Spring web as the http handler for aspx pages, I'm now telling Spring to construct my aspx (and thus ascx) page instances rather than ASP.NET.  With Spring now constructing my pages, its now possible to inject dependencies directly into my pages upon construction.

<httpModules>
   <add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
</httpModules>
<httpHandlers>
   <add verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web"/>
   <add verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/>
   <add verb="*" path="ContextMonitor.ashx" type="Spring.Web.Support.ContextMonitor, Spring.Web"/>
</httpHandlers>

The search controller uses a state service, specifically ASP.NET session state via a custom session state adapter, and a data service which are injected into the controller by Spring - the view (code behind page) has NO knowledge of the services being injected into the controller, as the controller instance itself is injected into the view.

Here's the relevant parts of the controller used for wiring, the constructor and Initialize().  Note that the constructor is called by Spring and that the Initialize method is called by the search view.

/// <summary>
/// Controller logic for search view.
/// </summary>
public class SearchController
{
   public static readonly string ResultsKey = "SearchController.ResultsKey";

   private ISearchView m_view;
   private IDataService m_dataService;
   private IStateProvider m_stateProvider;

   public SearchController(IDataService dataService, IStateProvider stateProvider)
   {
      m_dataService = dataService;
      m_stateProvider = stateProvider;
   }

   public SearchController Initialize(ISearchView view, bool firstTime)
   {
      m_view = view;

      if (firstTime)
         DefaultSearchForCurrentUser();

      return this;

   }
   // snip //
}

The plan is to have two different Spring configurations.  One for production and integration testing and the other one used for WatiN tests.  That way when WatiN drives Internet Explorer to verify the UI is working, it does so without ever touching the data service, only testing specific stubs are being used for the data service.  This is partly for speed but also because our data service implementation is not yet complete.

*I know… very sad.  My last ASP.NET project was using .NET 2.0, and that was a year and a half ago when that was started.

ASP.NET | IoC
Saturday, March 17, 2007 5:29:16 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, March 14, 2007

Now that I’m finally back in ASP.NET user land, I can finally scratch an itch of mine to use WatiN on a real project.  I’m thoroughly impressed with the product, even though the documentation is fairly light; the thing is the API is so straight forward you don’t need a bunch of documentation which allows you to ramp up and use it very quickly.  How nice is that?

The one problem I had was that Session state and general page state was being shared between each of my tests initially which would cause some tests to break because they expected the page to be in one state, but another test had changed the state to something else.  I had originally scoped my IE instance at the fixture level, which caused some tests to break because of the order they were being run by NUnit.  I ended up having to change my IE scope from fixture level, to test level which was quite a bit slower.  Here’s one of my tests:

[Test]
public void SearchResultsAreDisplayedOnSearchClick()
{
   using (IE ie = CreateIE())
   {
      ie.Link(Find.ById(new Regex(@"m_btnSearch\b"))).Click();
      string borrowerName = GetResultTable(ie).TableRows[1].TableCells[1].Text;

      Assert.AreEqual("George Washington", borrowerName,
          "Search results did not display as expected.");
   }
}

private Table GetResultTable(IE ie)
{
   return ie.Table(Find.ById(new Regex(@"m_resultsGrid\b")));
}

private IE CreateIE()
{
   IE ie = new IE(SearchUrl);
   ie.Refresh();
   return ie;
}

What I need to do is to make my tests less granular, less like a unit test and more like a functional integration test.  I would rather not do that, but I think for more complicated testing scenarios I will have to do this anyway.  I should stop trying to fight what should be natural here.  Basically I need to combine what are now several tests into a single test.  An end user would likely perform several actions on the page before moving on to anther page, and so should my WatiN tests.  After all, these aren’t unit tests – those of course are already all green.

Wednesday, March 14, 2007 6:21:41 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [1]  |