Friday, October 29, 2010

Getting Jiggy with Ajax and fields

I was asked to do something that I’ve done a million times before the other day, link a drop down field to a radio button so depending on what is selected the list of option should reduce. I decided to use Ajax and JQuery to do all the hard work and also because its really good at what it does.

The first requirement was to have a number of radio buttons on screen each showing the name of a department. These were very simple at first, not even taken from the database. So I created a very simple bit of HTML…

<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Ajax Demo</title>
</head>
<body>
Corporate <input type="radio" id="radio1" name="BU" value="Corporate" /><br />
Food <input type="radio" id="radio2" name="BU" value="Food" /><br />
Investment <input type="radio" id="radio3" name="BU" value="Investment" /><br />
<select id="department">
<option value="0">-- No business unit selected --</option>
</select>
</body>
</html>

Nothing crazy here, just your basic setup, with three radio buttons and a select box with no values. It would look like this.

image

Adding the Ajax bit

Now we need to implement JQuery onto the page, and we do this by including the latest script file. You can get this on the internet, via a content delivery network or just download it. I’ve taken a copy as its quicker.

<script type="text/javascript" src="template/scripts/jquery-1.4.2.min.js"></script>

The line above adds the JQuery script into the page your are working with.

Below is the script to do all the wor, I’ve highlighted the interesting bit.

<script type="text/javascript" language="javascript">
$(document).ready(function() {
$("input[name*='BU']").click(function() {
callAjax($(this).val());
});
});

function callAjax(val) {

var selectedValue = val;
var servletUrl = '/OrganisationChartDataService.svc/Departments/'">http://<server>/OrganisationChartDataService.svc/Departments/' + val + '/JSon';

$.getJSON(servletUrl, function(options) {
var department = $('#department');
$('>option', department).remove(); // Clean old options first.
$.each(options, function(index) {
department.append($('<option/>').val(options[index].Id).text(options[index].Name));
});
});

}

</script>

The first section binds a “click” function to any element on the page with the name “BU”. This is handy because we don’t have to write a separate function for each element and if we add more we don’t have to do anything, it will just work.

The second highlighted bit is teh call to a WCF web service that takes in the name of a business Unit as being part of the URL, what’s returned can then be added to the select list.

The results can be see below, its fast simple and very powerful.

image

clicking another item shows this:

image

Wednesday, October 20, 2010

Old school CSV without creating a temp file

I had a request to export a report out to Excel today, which is something I’ve done a bunch of times before but always by producing a temporary file.  I figured I’d try something different, find a way to give me the same functions without having to worry about permissions on the file system, or deleting the files afterwards.

It turned out to be very easy… use a TextStream that writes out  a HTML header so Explorer does all the hard work.

On the ASPX page…

On the click event for the export button we turn off the usual ASP.NET view state stuff and make a new header with the content type of "application/vnd.ms-excel", this tells IE to start Excel regardless of the details sent.  Another interesting thing to see here is the Response.End call, you’ll need this to prevent ASP.NET sending the page refresh information along with your data.

protected void ExportButton_Click(object sender, EventArgs e)
{
      this.EnableViewState = false;
        Response.Clear();
        Response.Buffer = true;
        Response.ContentType = "application/vnd.ms-excel";
        Response.AddHeader("Content-Disposition", "inline;filename=TeamExport.csv");
        team.ExportToExcel(Response.Output, true);
        Response.Charset = "";
        Response.End();
}

On the Business Object its a simple matter of building up an array of results, making sure to wrap them in “” for each field.  Then added this to the HTML TextStream …..

/// <summary>
/// Export Global Team lists to Excel
/// </summary>
/// <param name="httpStream">The HTTP stream.</param>
public void ExportToExcel(TextWriter httpStream)
{
     // find all associated applications
     IList<Application> applications =  (from application in Application.FindAllByProperty("GlobalTeam", Id)
             select application).ToList<Application>();

     foreach(Application app in applications)
     { 
         string[] dataArr = new string[]
             {
                 WriteableValue(app.Id),
                 WriteableValue(app.GlobalTeamName),
                 WriteableValue(app.Title),
                 WriteableValue(app.SupportManager),
                 WriteableValue(app.ProjectManager),
                 WriteableValue(app.StartDate.ToShortDateString()),
                 WriteableValue(app.EndDate.ToShortDateString())
             };
         httpStream.WriteLine(string.Join(",", dataArr));
     }
}

public static string WriteableValue(object o)
{
    if (o == null || o == null)
        return "";
    else
        return "\"" + o.ToString() + "\"";
}

Its good to rediscover something old, simple but yet works so well ….

Thursday, October 14, 2010

More pain with Windows x64 migration

You have no idea how hard it was to get this to work!.  following on from my last posting I moved over from Windows XP to Windows 7 on x64 for development.  Although there was some pain in setting up the VB COMs it was nothing to the suffering when it comes to Registry settings.

Some applications were continually giving Generic 500 errors with little or nothing in the Event Logs, so I presumed it was a security problem.  Following many hours of messing about, it turns out I was on the wrong track altogether.  The specific COMs were failing because they were looking for Registry settings…

All the settings had been imported into their usual position under HKEY_LOCAL_MACHINE\SOFTWARE\SomeApplication, but no matter what I did the values always came back null. 

The solution

The answer was hidden away in a MS support article (http://support.microsoft.com/kb/896459)

32-bit programs and 64-bit programs that are running on an x64-based version of Windows operate in different modes and use the following sections in the registry:

  • Native mode 64-bit programs run in Native mode and access keys and values that are stored in the following registry sub key:
    HKEY_LOCAL_MACHINE\Software
  • 32-bit programs run in WOW64 mode and access keys and values that are stored in the following registry sub key:
    HKEY_LOCAL_MACHINE\Software\WOW6432node

So adding a copy of the REG settings to this second location made everything work again.

Tuesday, October 5, 2010

Setting up Windows 7 for Classic ASP

I’ve decided to move over from using the slow VPN to local development so decided to setup some old classic ASP applications on my local machine which is running IIS 7.5.  Unfortunately this process was far from easy.

Setting up IIS

First you need to be able to activate all the required roles.  You do this by selecting Control Panel/Programs/Turn Windows features on or off.  from the list you select the following:

Parallels Picture

Doing some configuration

Next you should create an Admin Console for you to work with.  Type  “MMC” into the start menu and select File/Add Remove Snap in from the menu.  Select IIS Manager and IIS Manager Ver.6, Also choose Event Log for the current machine.

Parallels Picture 1

File Save this to your desktop for later.

Getting it working

When I got a local copy of the application all the .NET code worked first time without any issue, however when I tried to run any Class ASP code I got a standard Error: This error (HTTP 500 Internal Server Error) means that the website you are visiting had a server problem which prevented the webpage from displaying.

After some checking about on the net it turns out that you can turn on messages for ASP code using a configuration setting in IIS Manager,

Within IIS Manager Browse to the Virtual Directory you need and Double click click on the ASP icon and expand the “Debugging Properties” tree.

Parallels Picture 2

Turn on “Log Errors to NT Log” and “Send Errors to Browser”.

Unfortunately after doing this I still did not get an error messages displayed so had to do some more hunting down of the error.

Tracing requests

You also need to turn on Tracing which can be done by reading the instructions on the following link: http://learn.iis.net/page.aspx/565/using-failed-request-tracing-to-troubleshoot-classic-asp-errors/

Following this you actually get to see a correct error!

Parallels Picture 3

LineNumber:21
ErrorCode:800a01ad
Description: ActiveX component can't create object

Checking the global.asa gave me the following line:
Set GetDirectory = Server.CreateObject("EnterpriseIreland.BusinessObjects.HumanResources.Directory")

Checking Permissions

This error is usually down to permissions or the worker process can’t find the DLL.  Setting “Everyone” with Full permissions on the D:\Applications\ folder did not work, neither did giving “Everyone” access to the D:\Development folder.

Next I checked to make sure the DLLs were correctly registered by re-running  the “register_assembly.bat” command file located with in the D:\Applications\Common folder.

Next open Regedit.exe and browse to the DLL that was failing:
HKEY_CLASSES_ROOT\EnterpriseIreland.BusinessObjects.HumanResources.Directory open the CLSID key and you find a GUID.  In my case it was “{C940B037-A429-303E-8B2E-162E4E19AC91}” search the registry for this GUID.

You should find it somewhere in the HKEY_CLASSES_ROOT\Wow6432Node\CLSID\ key group; in my case it was here: HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{C940B037-A429-303E-8B2E-162E4E19AC91} click into the “InprocServer32” key and look for the value key “Codebase” this should show the file location of the DLL.  If it’s not pointing to the correct location change it.

Moving to App Pool

None of these fancy changes seemed to make any difference so I decided to look into the AppPool configuration.  The DefaultAppPool was working fine with .Net, so I created a second one based on this called “ASP”.

Parallels Picture

Next in the Advanced settings you need to make one minor but VERY IMPORTANT change;

Parallels Picture 1

You must set “Enable 32-bit Applications” to “True”

Finally assign your website to use this application pool by selecting it in IIS Manager and click Basic Settings in the Action Menu.

Parallels Picture 2

Click Select and choose “ASP” or whatever your AppPool name is called.

Success At last!!

After all this, we finally have a working legacy ASP site working on Windows 7.

Monday, April 5, 2010

Adventures when moving to GIT

I’ve been on Git hub a few times but never actually set this up on my laptop.  Following some Open Source work with a group of friends I finally got the point to the solution.  Distribution of the repositories and the ability to pick and choose the areas you want to take into your own, was such a simple and effective way to share code I decided to adopt it for most of my local development.

I am however not the best with the command line interface so when I heard that the tortoise group had got together and created an integrated windows version, I had to give it a go.

Setup

Download the software from Google code and run the MSI file worked first time without any issue.  You will however need to restart your PC/Laptop following the installation.

image

Following an reset you’ll get some extra options in your stadard windows explorer.

Connection to an existing repository

I already had an existing GitHub repository so it was a simple matter of connecting up to this.  First create a folder on your drive (in my case I called it Development) on your C Drive.  Right click on the empty directory and select Git Clone.

Select the URL on git hub of the git file and project you want to connect too, in this case the NXMPP code project.

image

Click Ok.

image

All going well you can click Close and have a copy of the code on your PC.

image

Pushing a change

So supposing you’ve a change to make to this code, how can you get it back to the cloud repository?  I’ve made a very simple change to one of the tests to illustrate the changes in one file.

image

Here in explorer you can see that Git has realised that there was a change to one of the files.

image

Right click the file and select “Diff” will bring up the a description of the changes.

Now lets say that change needs to be placed back into the repository, simply right click the file or directory and select Git Commit.  This will place the code back into the repository.

I’m looking forward to getting more into this very simple and interesting product.

Thursday, April 1, 2010

Resetting the Admin password on a Windows 2003 VM when you forget what it was.

Today I had a problem remembering the Administrator password on a VM I had created a long time ago.  Although I had created another Admin level account for myself, the VM was not on the domain so I could not even use my own account.  Luckily some came to my rescue with his magic Linux boot file which can remove the password.

Step 1

Download the Offline NT Password & Registry Editor from their website.  I used version v080526 as v080802 did not seem to be working correctly.  Unzip the ISO file 080526.iso to your local drive.

Open up the instructions page on their site.

Step 2

Start your VM and mount the ISO file by right clicking on the icon on the bottom left of the screen and selecting “Capture" ISO Image”.

Now shut down and restart the VM.

Step 3

At this point you can follow the instructions page on the site or just do the following for a quick overview.

  1. Press <Enter> to select the first disk partition
  2. Press <Enter> to select the default configuration directory
  3. Press <Enter> to select option 1; “Password Reset”.
  4. Press <Enter> to select “Edit user data and password”
  5. Press <Enter> to select the “Administrator” username or enter which ever you wish
  6. Enter “1” for option “Clear (blank) user password” and press Enter.  There are other options available if needed.
  7. Enter “!” to quit
  8. enter “q” to quit
  9. enter “y” to write the changes to the disk
  10. Enter “n” to try again option.

Now unmount the ISO by right clicking the icon and selecting “Release 080526.iso” and restart the VM.  You should now be able to login without having a password.

Monday, March 29, 2010

Playing about with REST on .Net

I’ve been planning to do more work with Azure and iPhone and figured I should embrace the new wave of developments with REST.  This seems to be the way in which the technology is going even for Microsoft.  I’m a bit of a fan of WCF, so figured I’ve give it a go implementing this using everything I’ve learned from that and with a little help from “RESTful .Net” by Jon Flanders it worked out well.

The Server

First step is to create standard VS2010 WCF Service Application project called WCFServer.

image

This will give you all the basic items needed to run an IIS Hosted WCF Service.  I won’t go into the details of these to much as I’ll assume you understand the requirement for deleting or renaming the various classes to fit those I’ve used.

The project will consist of 4 files:

  1. IRESTService.cs; which is an Interface definition for the server contracts.
  2. RESTService.svc; the web service definition file
  3. RESTService.svc.cs; the code behind for the service
  4. web.config; the application configuration and definition file.

IRESTService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WCFServer
{
    [ServiceContract]
    public interface IRESTService
    {
        [OperationContract()]
        [WebGet(UriTemplate="/GetRestData/{value}")]
        string GetRestData(string value);

        [WebGet(UriTemplate = "/")]
        [OperationContract]
        string GetRestDataRoot();
    }
}

My Service has 2 very simple HTTP GET methods, GetRestDataRoot which will return a simple text string and GetRestData which takes a string and then responds with a string.  These are hardly high tech and are only to illustrate hte process.

RESTService.svc

<%@ ServiceHost Language="C#" Debug="true" Service="WCFServer.RESTService" CodeBehind="RESTService.svc.cs" %>

This defines the service file and the link to the code behind.

RESTService.svc

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.ServiceModel.Activation;

namespace WCFServer
{
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class RESTService : IRESTService
    {
        public string GetRestData(string value)
        {
            return string.Format("You entered: {0}", value);
        }

        public string GetRestDataRoot()
        {
            return string.Format("Root");
        }
    }
}

As you can see from the code behind I’m not really doing anything complicated at this stage, just returning strings.

web.config

<?xml version="1.0"?>
<configuration>

…………… < sniped > ………

<httpModules>
   <add name="NoMoreSVC" type="WCFServer.RestModule, WCFServer"/>
   <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>

…………… < sniped > ………

  <system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
    <services>
      <service name="WCFServer.RESTService" behaviorConfiguration="WCFServer.RESTServiceBehavior">
        <!-- Service Endpoints -->
        <endpoint address="" binding="webHttpBinding" contract="WCFServer.IRESTService" behaviorConfiguration="web">
          <identity>
            <dns value="localhost"/>
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="WCFServer.RESTServiceBehavior">
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true"/>
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="web">
          <webHttp/>
        </behavior>
      </endpointBehaviors>

    </behaviors>
  </system.serviceModel>
</configuration>

In the web.config I’ve highlighted some of the more interesting elements for implementing REST.  The endpoint definition has defined the binding as “webHttpBinding" which is needed and we set the behaviorConfiguration to “web".  The endpointBehaviors is then defined to use webHttp.

You’ll also notice that I’ve added another called called RestModule and registered this as a httpModule, this was only done to remove the need to access the URL with a .svc on the end.  It’s probably over the top for what I need but it just looked better.

restmodule.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace WCFServer
{
    public class RestModule : IHttpModule
    {
        public void Dispose()
        {
        }

        public void Init(HttpApplication app)
        {
            app.BeginRequest += delegate
            {
                HttpContext ctx = HttpContext.Current;
                string path = ctx.Request.AppRelativeCurrentExecutionFilePath;
                int i = path.IndexOf('/', 2);
                if (i > 0)
                {
                    string svc = path.Substring(0, i) + ".svc";
                    string rest = path.Substring(i, path.Length - i);
                    string qs = ctx.Request.QueryString.ToString();
                    ctx.RewritePath(svc, rest, qs, false);
                }
            };
        }
    }
}

Once you have all this up and running we can test it by running the manipulating the URL in my case (http://localhost:1286/Service1.svc).  If you try to access it via the REST interface you‘ll get the following http://localhost:1286/Service1/

image

and http://localhost:1286/RESTService/GetRestData/Testing123 should return the following:

image

The Client

OK, now we need a client to access the REST server, for this I’ll use a simple UnitTest project.

ServiceTest.cs

using System;
using System.Text;
using NUnit.Framework;
using NMock2;
using System.Data;

namespace UnitTests
{
    [TestFixture]
    public class CompanyObjectTests
    {
        private static readonly log4net.ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodInfo.GetCurrentMethod().DeclaringType);
        private Mockery _mockSQLDatabse;

        [SetUp]
        public void TestSetup()
        {
            log4net.Config.XmlConfigurator.Configure();
            _log.Info("Starting up for testing");
        }

        [TearDown]
        public void TestShutDown()
        {
            _log.Info("Shutting down test");
        }

        [Test(Description = "Test the service")]
        public void ServiceTest()
        {
            IClientContract client = new ClientContract();
            string result = client.GetRestDataRoot();
            Assert.IsTrue(result == "Root", "The result should be 'Root'");
            _log.DebugFormat("result={0}", result);

            result = client.GetRestData("Testing");
            Assert.IsTrue(result == "You entered: Testing", "The result should be 'You entered: Testing'");
            _log.DebugFormat("result={0}", result);
        }
    }
}

Here I’ve created an interface (IClientContract) and a concrete class (ClientContract).

IClientContract

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace UnitTests
{

    [ServiceContract]
    public interface IClientContract
    {
        [OperationContract]
        [WebGet(
            BodyStyle = WebMessageBodyStyle.Bare,
            ResponseFormat = WebMessageFormat.Xml,
            UriTemplate = ""
            )]
        string GetRestDataRoot();

        [OperationContract]
        [WebGet(
            BodyStyle = WebMessageBodyStyle.Bare,
            ResponseFormat = WebMessageFormat.Xml,
            UriTemplate = "/GetRestData/{value}"
            )]
        string GetRestData(string value);
    }
}

ClientContract

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace UnitTests
{

    public class ClientContract : ClientBase<IClientContract>, IClientContract
    {
        public string GetRestDataRoot()
        {
            return this.Channel.GetRestDataRoot();
        }
        public string GetRestData(string symbol)
        {
            return this.Channel.GetRestData(symbol);
        }
    }
}

App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
  </configSections>
  <appSettings>
    <add key="ApplicationName" value="" />
  </appSettings>

  <log4net>
    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
      <layout type="log4net.Layout.PatternLayout">
        <param name="Header" value="[Header]\r\n"/>
        <param name="Footer" value="[Footer]\r\n"/>
        <param name="ConversionPattern" value="%d [%t] %-5p %c %m%n"/>
      </layout>
    </appender>
    <root>
      <level value="ALL"/>
      <appender-ref ref="ConsoleAppender"/>
    </root>
  </log4net>

  <system.serviceModel>
    <bindings />
    <client>
      <endpoint address="http://localhost:1286/RESTService.svc"
                behaviorConfiguration="rest"
                binding="webHttpBinding"
                contract="UnitTests.IClientContract"/>
    </client>
    <behaviors>
      <endpointBehaviors>
        <behavior name="rest">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors> 
  </system.serviceModel>
</configuration>

Running this in NUnitGUI will give you green lights all the way.

image

Source Code

All the source code for this project can be found here.