Monday, March 30, 2009

Adding UnitTests to CruiseControl

In this post I’ll describe how to add these tests to the Continuous Integration Server.  The process is very simple really, only a matter of a adding a new task in the Control Control Config Console (CCNETConfig) for that project.

Adding your Test to Continuous Integration

Assuming you have already added your UnitTest project and added it to the solution. Logon onto your build server and open CCNetConfig.  this is available from the start/all programs menu.

image

Click File/Open from the menu and browse to the servers configuration file (ccnet.config), which should be found in D:\Program Files\CruiseControl.NET\server folder on the local server and click Open.

Expand the project on which you have added the Unit tests and right click the Tasks node on the tree, in this case it’s MSS.

image

Choose Add Task and NUnitTask from the menu, you should then see the new node added to the tree.  Select this node and add the relevant information to the detail pane on the right.

image

(Assemblies) should point to the UnitTest DDL that is built during compilation.

Output File; should point to the logs folder on the build server

Path; points to the location of the NUnit Console application

timeout; Default to 100 seconds for timeout, however increase this for long running test projects.

Save your changes and you should be fine.

Viewing the results

Now every time the project builds your tests will be run against all the code.  This will highlight any places where the new code has adversely affected other areas of the solution.

image

From the CCNet control website (http://<server>/CCNet/) you can browse to your project and select “Latest Build” then select the “NUnit Details” link on the right of the screen.  Here you can see that 1 test was completed successfully.  Clicking the NUNIT Timings link will tell you how long it took to run the tests which have be very useful for performance testing.

Saturday, March 28, 2009

Blogging via Windows Live Writer

Its important to let everyone know what your doing, so here is an easy way to interact with a blog. I’d encourage everyone to post once or twice a week anything they’ve learned; be it technical or organisational, important or trivial. Once it has been entered into the blog others will benefit from that knowledge and we’ll forever have a record.

This is software is also ideal for documenting systems and procedures “as-you-go”, as all you need to do is take screen shots and paste them directly into your documentation.

To start I’ll show you how to download and use Microsoft Windows Live Writer and connect it to a SharePoint Blog.

Getting Live Writer

The software is freely available from the Microsoft Website, simply click the Download button, Click Run and Run again when prompted.

lr1

Uncheck everything except “Writer” and click Install.

lr2

You’ll be asked to close Internet Explorer, do this and click Continue.

lr3

You’ll see the Progress bar during the installation.

lr4

Deselect everything from the additional installation window as you don’t need it and click Continue.

lr5

Finally click close and you should be ready to start using the software.

Starting Live Writer and connecting to the IT Blog.

Once installed you should get a new item in your Start/Programs/Windows Live menu called Windows LiveWriter. It should open first time only asking you to connect to a default Blogging site.

image

Click Add..

image

Select SharePoint Blog and click Next.

image

In the Blog URL you need to type the following: /sites/it/blog/default.aspx, then">http://<server>/sites/it/blog/default.aspx, then click Next.

image

You should see a progress window as Live Writer searches for the Blog details.

image

If successful you will be asked for a Blog Name, I’d recommend choosing the same name as the blog, as you can add additional blogs to the application if needed.

image

You should now be able to access the Blog each time the application starts. If this is your only blog, it will open be default, however if you want to use multiple blogs be sure to check the upper right of the screen which should say “Blog”.

Publishing a post

Live writer is much like Word simply type in the work space and cut/paste images and screen shots. When you are finished click the Publish button in the upper left hand corner of the tool bar.

image

You should see the progress bar and finally a new Browser window will open and you can see your blog entry.

Wednesday, March 25, 2009

ASP.NET Mobile Browser

I’ve been investigating doing a port of my iPhone application to other platforms and discovered an interesting tool called Mobile Browser Definition File.  The file contains capability definitions for about 70% of the main mobile devices and browsers in use today.  Your ASP.NET project can use the .browser file, along with the information in the HTTP request header, to determine what type of device/browser has made the request and what the capabilities of that device are.

Check it out, it’s very useful if anyone wants to do work on mobile devices.  Did you know for example that many of the new Nokia phones don’t support the PNG graphic format!? 

Monday, March 23, 2009

Enable ASP debugging in an ASP.NET 3.5 Site

Today I got this error on an ASP running under Visual Studio 2008.

image

The error text was as follows:

The type of page you have requested is not served because it has been explicitly forbidden.  The extension '.asp' may be incorrect.   Please review the URL below and make sure that it is spelled correctly.

It turns out that ASP is not supported under Visual Studio 2008 unless you make 2 additions to the local Web.Config file. 

<compilation debug="false">
      <buildProviders>
        <add extension=".asp" type="System.Web.Compilation.PageBuildProvider" />
      </buildProviders>

</compilation>

 

<httpHandlers>
<add path="*.asp" verb="*" type="System.Web.UI.PageHandlerFactory" validate="true" />
</httpHandlers>

This will enable ASP to work in the debug browser however if you have relative paths it won’t work as you’re running only one project under a specific port number.

i.e You will get this type of error.

image

Alternatively you can just set the server in the project properties to run under local IIS, which is just as good.

image

Saturday, March 21, 2009

The Developer Disk Image could not be mounted

I got the following error when trying to deploy the latest version of my iPhone application to my iPod Touch.

The Developer Disk Image could not be mounted.
Xcode could not find an appropriate Developer Disk Image to mount on XXXXX iPod. Please contact Apple for the 2.2.1 (5H11) device support package.

Going onto the Apple Developers site and getting the latest copy of the SDK fixed the issue.

Friday, March 20, 2009

Working with Azure

I’m back in action after a long time away from my iPhone application development.  It took a very interesting weekend conference called BizCamp to get me back into doing some development again.  Thanks should also go to Stephen and Joel for getting me off the sofa, watching BSG until 1am at the weekend.

The basic idea here is to add some bug tracking functionality to my iPhone applications site.  This request came from one of my beta testers who commented that this type of functionality gives a better feeling of community.

I’m assuming that the developer reading this has all the various software downloads needed to run Azure.  I’m also assuming that you’ve gone through the pain and suffering required to obtain your invitation code to the CTP.

The Basic Design

Due to the limited data types available in Azure I’m going to keep the implementation very simple, just one table to hold all the data.

Picture 2

Each field is defined as a string however during implementation I’ll add 3 extra fields to support Azure.  ID which is a simple INT used as a Primary Key, PartitionKey a GUID used by Azure storage and RowKey which will match the Primary Key.

The User will simply fill in the error description via the website or within the iPhone application.  These records will be given a status of “Open”.

image

Later the records are reviewed within the Administration section of the website where each bug is given a status of “In Progress”, “On Hold” or “Closed”; however I’ll leave that functionality outside this posting as it’s basically using the same techniques.

Class Development

First we create 3 main classes; the first being the query definition class which encapsulates a simple list.

The context class

public class CloudContext : TableStorageDataServiceContext
{
    internal CloudContext(StorageAccountInfo accountInfo)
        : base(accountInfo)
    {  }

  // table name
internal const string BugsTableName = "BugTable";

// Query Definition
public IQueryable<UserDataModel> BugTable
      { get
           { return this.CreateQuery<BugDataModel>(BugTableName); }

     }

}

The TableStorageDataServiceContext class handles the authentication process into the Table Storage service.

The Data model

public class BugDataModel : TableStorageEntity
{
    public BugDataModel(string partitionKey, string rowKey)
        : base(partitionKey, rowKey)
    {
    }

    public BugDataModel()
        : base()
    {
        PartitionKey = Guid.NewGuid().ToString();
        RowKey = String.Empty;
    }

    public int ID
    {
        get;
        set;
    }

    public string Name
    {
        get;
        set;
    }

    public string Email
    {
        get;
        set;
    }

    public string ErrorDescription
    {
        get;
        set;
    }

    public string ErrorResolution
    {
        get;
        set;
    }

    public string Status
    {
        get;
        set;
    }
}

Which is a definition of the actual storage structure.

The Data Source

    public class BugDataSource
    {
        private CloudContext _ServiceContext = null;

        public BugDataSource()
        {
            // Get the settings from the Service Configuration file
            StorageAccountInfo account =
                StorageAccountInfo.GetDefaultTableStorageAccountFromConfiguration();

            // Create the service context we'll query against
            _ServiceContext = new CloudContext(account);
            _ServiceContext.RetryPolicy = RetryPolicies.RetryN(3, TimeSpan.FromSeconds(1));
        }

        public IEnumerable<BugDataModel> Select()
        {
            var results = from b in _ServiceContext.BugTable
                          select b;

            TableStorageDataServiceQuery<BugDataModel> query =
            new TableStorageDataServiceQuery<BugDataModel>(results as DataServiceQuery<BugDataModel>);
            IEnumerable<BugDataModel> queryResults = query.ExecuteAllWithRetries();
            return queryResults;
        }

        public IEnumerable<BugDataModel> SelectByID(int id)
        {
            var results = from c in _ServiceContext.BugTable
                          where c.ID == id
                          select c;
            TableStorageDataServiceQuery<BugDataModel> query =
            new TableStorageDataServiceQuery<BugDataModel>(results as DataServiceQuery<BugDataModel>);
            IEnumerable<BugDataModel> queryResults = query.ExecuteAllWithRetries();
            return queryResults;
        }

        public void Delete(BugDataModel itemToDelete)
        {
            IEnumerable<BugDataModel> bugs = this.SelectByID(itemToDelete.ID);
            foreach (BugDataModel bug in bugs)
            {
                _ServiceContext.DeleteObject(bug);
                _ServiceContext.SaveChanges();
            }
        }

        public void Update(BugDataModel itemToUpdate)
        {
            IEnumerable<BugDataModel> bugs = this.SelectByID(itemToUpdate.ID);
            foreach (BugDataModel bug in bugs)
            {
                bug.Name = itemToUpdate.Name;
                bug.Email = itemToUpdate.Email;
                bug.ErrorDescription = itemToUpdate.ErrorDescription;
                bug.ErrorResolution = itemToUpdate.ErrorResolution;
                bug.Status = itemToUpdate.Status;

                _ServiceContext.UpdateObject(bug);
                _ServiceContext.SaveChanges();
            }
        }

        public void Insert(BugDataModel newItem)
        {
            // we must overwrite the supplied ID with the MAX as Azure does not yet support Identity columns
            // with simple storeage tables.

            int Id = this.GetNextID();
            newItem.ID = Id;
            newItem.RowKey = Id.ToString();
            _ServiceContext.AddObject(CloudContext.BugTableName, newItem);
            _ServiceContext.SaveChanges();
        }

        #region  -- Private methods --
        /// <summary>
        /// Simple function to find the max results from a recordset of IDs and add one.
        /// </summary>
        /// <returns>the next id number</returns>
        private int GetNextID()
        {
            int maxID=0;
            IEnumerable<BugDataModel> categories = this.Select();
            foreach (BugDataModel Entry in categories)
                if (Entry.ID > maxID) maxID = Entry.ID;
            maxID++;
            return maxID;
        }

        #endregion

    }

The DataSource class is the real work horse of the application.  It’s the controller for getting information in and out of the storage structure.

You’ll notice that I’ve added a GetNextID private method which I’m not really happy about, but it’s the only way I could see to duplicate the functionality needed to mimic a normal database structure.

Web Interface Development

Next we create the web interface code.  In my project I separated the web code into another project and added a reference to the Model classes, but it’s not essential.

Create a new ASPX page to allow a Bug to be registered (Inserted) by the Users.

    <h2>Report your problems here</h2>
    <asp:FormView id="frmAdd" DataSourceId="bugData" DefaultMode="Insert"
            Runat="server" OnItemInserted="frmAdd_ItemInserted" >
        <InsertItemTemplate>
        <table>
        <tr>
            <td><asp:Label id="nameLabel" Text="*Name:" AssociatedControlID="nameBox" Runat="server" /></td>
            <td><asp:TextBox id="nameBox" Text='<%# Bind("Name") %>' Runat="server" />
                <asp:RequiredFieldValidator ID="reqName" ControlToValidate="nameBox" runat="server" ErrorMessage="Required"/>
            </td>
         </tr>
         <tr>
            <td><asp:Label id="emailLabel" Text="Email:" AssociatedControlID="emailBox" Runat="server" /></td>
            <td><asp:TextBox id="emailBox" Text='<%# Bind("Email") %>' Runat="server" />
            <asp:RegularExpressionValidator
                ID="RegularExpressionValidator1" runat="server"
                ControlToValidate="emailBox"
                ErrorMessage="email format"
                ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"/>
            </td>
         </tr>
         <tr>
            <td><asp:Label id="errorDescriptionLabel" Text="*Description:" AssociatedControlID="errorDescriptionBox" Runat="server" /></td>
            <td><asp:TextBox id="errorDescriptionBox" Rows="10" TextMode="MultiLine" Text='<%# Bind("ErrorDescription") %>' Runat="server" />
                <asp:RequiredFieldValidator ID="reqRescription" ControlToValidate="errorDescriptionBox" runat="server" ErrorMessage="Required"/>
                <asp:TextBox id="errorResolution" Visible="false" Text='<%# Bind("ErrorResolution") %>' Runat="server" />
            </td>
         </tr>
         <tr>
            <td><asp:Label id="statusLabel" Text="Status:" AssociatedControlID="statusBox" Runat="server" /></td>
            <td><asp:TextBox id="statusBox" Value="Open" Text='<%# Bind("Status") %>' Runat="server" /></td>
         </tr>
         <tr>
            <td colspan="2"><label>* indicates a manditory field.</label>
         </tr>
         <tr align="center">
            <td colspan="2"><asp:Button id="insertButton" Text="Report" CommandName="Insert" Runat="server"/></td>
         </tr>
         </table>
        </InsertItemTemplate>
    </asp:FormView>

    <%-- Confirmation message --%>
    <asp:Panel id="frmMessage" Visible="false" runat="server">
        <p><b>Your error has been received and will be actioned as soon as possible.</b></p>
    <p><a href="Default.aspx">Return to Home</a></p>
    </asp:Panel>

    <%-- Data Sources --%>
    <asp:ObjectDataSource runat="server" ID="bugData"
        TypeName="BusinessObjects.BugDataSource" InsertMethod="Insert"
            DataObjectTypeName="BusinessObjects.BugDataModel"
            SelectMethod="Select">   
    </asp:ObjectDataSource>

As you can see I’ve created 3 main functional areas.  An ASP.FormView which has a field for each record to be inserted.  Each of these is linked to the asp:ObjectDataSource called “bugData”.  An finally an ASP:Panel which is displayed when the “insertButton” is pressed.

protected void frmAdd_ItemInserted(object sender, FormViewInsertedEventArgs e)
{
    frmAdd.Visible = false;
    frmMessage.Visible = true;
}

Above it the only code in the code behind to implement the OnPressed event.

Build everything and you should be almost ready to run a test.

Generating the local storage and running locally

Next we need to run the program locally to ensure it’s working.

First off we need to generate the database structure into the local development storage.  At this point I’m assuming that you’ve got it up an running as it worked ‘”all most” out of the box for me.  I did have to tweek the configurations as I was running under a SQL2008 database under a named instance.

I created a simple generate batch file so I could run it a few times.

echo -- Generate the Azure Storeage database
set EXE_PATH=C:\Program Files\Windows Azure SDK\v1.0\bin\
set SOURCE_PATH=C:\Development\AzureProj\Interfaces\

"%EXE_PATH%DevtableGen.exe" /forceCreate "/server:(local)" "/database:Service_Azure" "%SOURCE_PATH%bin\BusinessObjects.dll;%SOURCE_PATH%bin\ServerAzure.dll"

As you can see the DevtableGen application uses reflection to read the contents your application and will create the tables in the local storage account.

As a side note you can check the DevelopmentStorage.exe.config file and hunt out the code below to find the location of your local database.

<connectionStrings>
  <add name="DevelopmentStorageDbConnectionString"
       connectionString="Data Source=(local);Initial Catalog=DevelopmentStorageDb;Integrated Security=True"
       providerName="System.Data.SqlClient" />
</connectionStrings>

You can check that everything worked by opening SQL Management Studio.

When you run the application now you should be able to see everything in action.  I’ll admit I’ve omitted the basic instructions for creating the various projects, but Azure is not for the faint hearted so it’s fair enough to say if you need that information you really won’t be getting the most out of this posting.

Azure Deployment

Assuming you’ve got your CTP invitation you should create a Storage Project and a hosted project.

image

When you select each you’ll get the information you need to place in the configuration files.

image 

The Storage project will provide you with the Access keys and http address for accessing the hosted data. 

image

Within the Hosted application you’ll get the Application ID.

Now it’s time up update your Azure project, which is automatically created when you create the solution.  Here you’ll find 2 configuration files; ServiceConfiguration.cscfg and ServiceDefinition.csdef.

ServiceConfiguration.cscfg

<?xml version="1.0"?>
<ServiceConfiguration serviceName="Service" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">
  <Role name="Web">
    <Instances count="1" />
    <ConfigurationSettings>
      <Setting name="AccountName" value="xxxxxxxxxxxxx"/>
      <Setting name="AccountSharedKey"    value="xxxxxxxxxxxx"/>
      <Setting name="TableStorageEndpoint" value="http://127.0.0.1:10002/"/>

    </ConfigurationSettings>
  </Role>
</ServiceConfiguration>

This file holds the configuration setting for accessing the service.  Above is only the local setting, once you deploy you’ll need to update these values to those generated in Azure.

ServiceDefinition.cscfg

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="xxxxxxxxxxx" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WebRole name="Web">
    <InputEndpoints>
      <InputEndpoint name="HttpIn" protocol="http" port="80" />
    </InputEndpoints>
    <ConfigurationSettings>
      <Setting name="AccountName"/>
      <Setting name="AccountSharedKey"/>
      <Setting name="TableStorageEndpoint"/>
    </ConfigurationSettings>
  </WebRole>
</ServiceDefinition>

Project properties

image

Right clicking the Azure project and selecting the Portal tab and enter the Application ID which is available on the Azure site.

 

At this stage you’re ready to run everything, however I’m not going to describe that as you should be able to figure it out from here.

Wednesday, March 11, 2009

Creating your SharePoint Development Environment

The SharePoint Development Environment is an all in one platform for the correct development and deployment of features.  It is designed to run on a development machine locally, package up all necessary code and deploy customisations to the Test and Production servers.

Setup of the Virtual Machine

Get a pre-built copy of the latest VM image from your server.  Copy both the VHD and VMC files to your D:\VMs directory.  Note: My  build contained Windows 2003 server with SQLServer 2000 and Visual Studio 2008, SharePoint MOSS Dev Tools and some site content and was about 18Gb in size therefore you should have at least 20Gb free.

Install the Virtual PC software onto your host machine by running setup.exe on the CD or Download from Microsoft.  When the software has been installed double click the Microsoft Virtual PC icon on your desktop.

image

Click “New” then “Next”

image

Select the “Add existing virtual machine” check box and click “Next”.  click “Browse” and select the location where the “SharePoint MOSS Dev Tools and Content.vmc” ws copied to on your local machine.  Then click “Next”.

image

Ensure that “When I click Finish, open Settings” is checked and click “Finish”.

image

Select Memory from the left had side and use the slider to set the amount allocated to the VM.  If you have 2Gb of RAM on the host set it to 1GB, if you have 4Gb of RAM set it to 2Gb, etc.

image

Select “Hard Disk 1” from the left had side and Click “Browse” on the right.  Select the “SharePoint MOSS Dev Tools and Content.vhd” file which you have stored on your local pc.

image

Select Shared folders” from the left had side and ensure that your Host PC’s D drive is mapped to the VM’s D drive.  We do this to ensure that SourceSafe and Visual Studio will operate correctly on the VM server.  Finally click OK and you should see that the Virtual machine has been added to your dialogue.

image

Select the SharePoint MOSS VM and click “Start”. 

Starting up and Configuring your of the Virtual Machine

Each VM needs to be configured for each developer to ensure that there is no IP Addressing or deployment issues that could conflict with correct operation.  Once the VM starts on your PC you should disable the network connection.

image

Right click the network icon (last on the right, next to the folder) and select Network Settings.

image

Select “Not Connected” from the drop down next to Adapter 1 and click “OK”.

Login to the VM using the local Administration account by holding down <Alt Gr> and clicking “Delete”, this is equivalent to sending <CTRL><ALT><DEL> on your VM. 

image

DUBWSDEV010 is the name of the original template used to create the VM, we will be changing this to match your VM name.  The Password can be obtained from your SharePoint Administrator.

When loaded you should be presented with a standard desktop.  Follow the instructions below to rename the MOSS installation.

image 

1) Go to the Sharepoint Central Administration > Operations > Services on Server  and stop all running services e.g. "Windows SharePoint Services Search", “Windows SharePoint Services Search “, etc.

image

Click Stop on anything started.

2) Double click the Newsid.exe icon.

image

Click Agree and Click Next.

image

Select Random SID and Click Next

image

Click “Rename the computer to:” and the new name of the development VM.  This should be in the format DUBWSDEV### where ### is a number from 010 to 019.  See the SharePoint Administrator for your assigned computer name and IP Address.  Click “Next”.

image

Be sure to uncheck the Automatically reboot when SID is applied box and click “Next”.

After some time processing the new SID should have been applied to your server.

image

Click “Finish”.

3) Now we need to rename your SQL Server installation.

Start Query Analyser from the “Start/Programs/Microsoft SQL Server” menu.

image

Set the SQL Server to be “.” and Connection to Windows authentication and click OK.

image

From the Query window type “EXEC sp_dropserver '<old server>'” and click the Play icon.

Next enter the command in the window “EXEC sp_addserver '<new_name>', 'local' “ and click the Play icon.

Exit Query Analyser.

4) From the command prompt type “stsadm -o renameserver -oldservername <old server name> -newservername <new server name>”

image

Type Exit and hit <Return> to close the command window.

5) Open the Registry using REGEDIT command and search for any occurrence of the old server name (i.e. DUBWSDEV010) rename each of these to the new server name (e.g. DUBWSDEV012).

Play close attention to the key located as “SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\12.0\Secure\ConfigDB” this should be set to the new server name.

Restart the server and login as the local Administrator.

6) Reset the central configuration of sharepoint to the new server by using the administration command: “psconfig -cmd adminvs -provision -windowsauthprovider onlyusentlm”

7) Reset the external access mappings for the server using Central Administration/Operations/Alternate Access Mappings.

image

Edit each public URL in your site to match your new server name (e.g. DUBWSDEV012)

8) In My computer/Properties remove the server from the domain by clicking on Workgroup.

image

Enter “Workgroup” into the field and click OK and restart the server.

Change the IP address of the server to the one required.  Contact the SharePoint Administrator for your assigned number.

image

Click OK.

image

Re-enable the network card by right clicking the network icon (last on the right, next to the folder) and select Network Settings.

image

Select the Intel card that exists on your host PC and click OK.

 

Rejoin the domain under the new name by click on My Computer/Properties and select the Domain radio button and enter entirl.com into the field.

image

When prompted for a Username and password, this must be someone with enough permissions to allow this.

image

All going well you should see the following prompt.

Restart your server, you should now be able to have full access to the local SharePoint environment.