Monday, 19 November 2007

I've moved from SSW to Oakton!

Just had my last day at SSW after 8.5 years! We had a great time at Strike Bowling @ Fox Studios in Centennial Park - and an even better dinner at Cine Italian restaurant down the street. After the obligatory speeches and hugs, that was it!!!

I started at Oakton this morning with 5 others (the equal largest weekly intake) with a lovely woman called Fiona Begg. I was blown away by her enthusiasm for the company and it has definitely rubbed off on me. I am very much looking forward to starting on a new Lend Lease project through Oakton (Stock Code 'OKN') which will deal with Property Funds Management.

After the induction, I was given a runthrough of the work to be done at Lend Lease. I also had some spare time which I used to look into the Property funds management business in general. I found some very illuminating articles on the software currently available at http://www.avo.gov.au/PropertyFundManagementDiscountedCashFlow.htm. Looking through some of the articles linked to on this site gave me a good overview of some of the tools used. It appears that there is an industry leading application called "DYNA" which is currently used by Lend Lease - along with Excel.

It seems that what ever the company, there is always a worrying number of spreadsheets lurking around where business-critical applications should be. I don't want to go into the many problems there are with spreadsheets - but I would hope that you don't want millions of dollars going awry when you incorrectly cut and paste a formula! After all the spreadsheets I've seen at Queensland Water Infrastructure and Pfizer, my thoughts were .... "Oh no! Not again!"

I also noticed that (like most companies), that Timesheets are not kept at the task level, but rather X number of hours is just allocated to a client. This is a lot simpler than the level of detail we had to provide at SSW. Perhaps our clients were just more demanding.

One of the things that Mark Liu mentioned when I gave him a lift to the bowling was that his brother John Liu (with whom I worked with for a year at SmartSalary) is also at Oakton. This means that I will potentially be working with 4 people I have worked with previously :

  1. Ryan Macnamee will be my supervisor (with whom I worked on the Qantas meal forecasting application at his old company Datacom)
  2. Andrew Weaver (who used to work at SSW) who now works at Lend Lease directly
  3. Tristan Kurniawan (who is contracting to Lend Lease via SSW)
  4. John Liu (if he moves to the Lend Lease project).

So it is really more like changing clients at SSW than moving company... Only bad thing is that I have to start 7:45am tomorrow!

Friday, 16 November 2007

The old schema refresh problem with SQL Server 7.0 still exists in SQL 2005

The metadata in SQL Server 2000 and 2005 databases do not automatically update for views when you add to or remove a column to the underlying table. They will only show the original columns from when the view is updated. For example, if you run the following script, you will only be shown the first 2 columns even though there are now 3 columns in the table:



if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Client]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[Client]
GO


CREATE TABLE [dbo].[Client] (
[ClientID] [int] IDENTITY (1, 1) NOT NULL ,
[Name] [varchar] (50) NULL ,
) ON [PRIMARY]
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[vwClient]') and OBJECTPROPERTY(id, N'IsView') = 1)
drop view [dbo].[vwClient]
GO

SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO

CREATE VIEW dbo.vwClient
AS
SELECT dbo.Client.*
FROM dbo.Client


GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT
BEGIN TRANSACTION
ALTER TABLE dbo.Client ADD
DateUpdated datetime NULL
GO
COMMIT


SELECT * FROM vwClient


Even worse, if you remove a column, you will get runtime errors guaranteed if you don't recreate all dependant views. To fix this, you should perform an sp_refreshview 'viewname' on the views whose underlying tables have been updated - or you can manually trigger this by going to design view of the view, adding a space to the query definition and clicking OK.

The simplest way to do this is for all views is to use the following stored procedure to detect any changes or problems:



if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[procRefreshAllViews]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[procRefreshAllViews]
GO

SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS OFF
GO



CREATE PROC dbo.procRefreshAllViews
AS

SET quoted_identifier off

DECLARE @ObjectName varchar (255)
DECLARE @ObjectName_header varchar (255)

DECLARE tnames_cursor CURSOR FOR SELECT name FROM sysobjects
WHERE type = 'V' AND uid = 1 Order By Name
OPEN tnames_cursor
FETCH NEXT FROM tnames_cursor INTO @Objectname
WHILE (@@fetch_status <> -1)
BEGIN
IF (@@fetch_status <> -2)
BEGIN
SELECT @ObjectName_header = 'Refreshing ' + @ObjectName
PRINT @ObjectName_header
EXEC('sp_refreshview ' + @ObjectName)
END
FETCH NEXT FROM tnames_cursor INTO @ObjectName
END
PRINT ' '
SELECT @ObjectName_header = 'ALL VIEWS HAVE BEEN REFRESHED'

PRINT @ObjectName_header
DEALLOCATE tnames_cursor
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO


This problem can be detected easily with SSW SQL Auditor which you can get from http://www.ssw.com.au/ssw/sqlauditor/

The simple way to parse strings to GUIDs in C# or VB.NET

While the religious debate rages between whether we should be using GUIDs as primary keys rages, you can use the Parse method of the System.SqlTypes.SQLGuid class to parse query strings to GUIDs. Here's an example:

return System.Data.SqlTypes.SqlGuid.Parse(Request.QueryString["ParticipantGuid"]).Value;

Thursday, 15 November 2007

Unit Test I created to force the load of Referenced DLLs

One of the issues with .NET applications is that they will load references Just In Time (JIT). I did this code about 2.5 years ago - but it is still useful to force the load of referenced DLLs in case your setup package doesn't deploy DLLs correctly. This code allows you to do a "sanity check" on your application if a user reports bugs when the install your Service/Windows Forms application.



   1:  using System;

   2:  using System.Collections;

   3:  using System.Reflection;

   4:  using NUnit.Framework;

   5:   

   6:  namespace UnitTest

   7:  {

   8:         /// <summary>

   9:         /// Forces load of all assemblies to stop any problems when the Setup Package doesn't have all the correct DLLs 

  10:         ///(we had this specific problem when DLLs were added to SSW.Framework.WindowsUI.WizardForms  FuelAdvance.Components.Windows.Wizards.dll)

  11:         /// (davidklein@ssw.com.au 17/06/2005)

  12:         /// </summary>

  13:         /// 

  14:         [TestFixture()]

  15:         public class DLLDependencyTest

  16:         {

  17:                private static ArrayList m_LoadedDLLs;

  18:   

  19:                public DLLDependencyTest()

  20:                {

  21:                       m_LoadedDLLs = new ArrayList();

  22:                       TestLoadReferencedDLLs();

  23:                }

  24:   

  25:                [Test()]

  26:                public void TestLoadReferencedDLLs()

  27:                {

  28:                       Assembly applicationToTest = AppDomain.CurrentDomain.Load("SSWeXtremeEmails");

  29:                       LoadDLL(applicationToTest);

  30:                }

  31:   

  32:                private static void LoadDLL(Assembly assembly)

  33:                {

  34:                       AssemblyName[] referencedAssemblies = assembly.GetReferencedAssemblies();

  35:                       foreach (AssemblyName referencedAssemblyName in referencedAssemblies)

  36:                       {

  37:                             Assembly currentReferencedAssembly = Assembly.Load(referencedAssemblyName);

  38:                             if (!m_LoadedDLLs.Contains(currentReferencedAssembly.FullName))

  39:                             {

  40:                                    m_LoadedDLLs.Add(currentReferencedAssembly.FullName);

  41:                                    LoadDLL(currentReferencedAssembly);

  42:                             }

  43:                       }

  44:                }

  45:         }

  46:  }

Wednesday, 14 November 2007

What is Project Management Anyway? Different clients have different expectations

The below is a snippet of an email I sent to resolve an issue we had with "Project Management" one of the problems was that our client thought it was one thing (based on their experiences), and we had a different notion of what "Project Management" was.

==========
<Email Snippet>
==========

Marlon,

I think some of our clients such as Westlink and Pfizer have a set idea of what Project Management is – they believe it implies “Dedicated Project Management and Oversight”. We have typically not been providing this as part of our other “Project Management” tasks in many projects, and this could lead to disappointment. In the extremely successful Maximus project, Peter McKeown from Symmetric was doing the project management by:
a. constantly planning
b. suggesting alternative options
c. talking to the clients
d. dictating our work for us
e. giving the project a high level of visibility by organising Friday demos and making sure all stakeholders were present and gave input.

This worked very well. He added real value as a Project Manager.

Project Management

I see project management as separate from creating release plans and the day-to-day meetings that developers have with clients. I don’t believe we are doing project management just by having developers send release plans and sending clarification emails. Project management is not what developers do. Consequently, I have separated this dedicated function out as an optional extra in the SSW Proposal Template. It is like an insurance policy and really needs to be done by a dedicated person that will perform the following function (as per Wikipedia):

"The planning, monitoring and control of all aspects of the project and the motivation of all those involved in it to achieve the project objectives on time and to the specified cost, quality and performance."

“Project management is quite often the province and responsibility of an individual project manager. This individual seldom participates directly in the activities that produce the end result, but rather strives to maintain the progress and productive mutual interaction of various parties in such a way that overall risk of failure is reduced.”

I believe the project manager should take on the role of and liaising with the client about problems and potential problems with the project. It is the project managers role to do the following:

1. Helping to make sure the customer is informed of budget overruns and technical issues and given the earliest possible opportunity to:
a. stop the project
b. remove tasks
c. reassign tasks to cheaper resources (e.g. China).
d. Reassign the tasks to more effective resources

This is assisted by generating Timing, ETAs and financial reports that developers don’t normally generate:
a. Financial Reporting e.g. using Microsoft Project via TFS WorkItems (based on our proposal spreadsheet) to show baseline estimates against actual figures and predict problems and deadlines.
b. Updating TimePRO projects and the project status and sending out the Project Progress report.

2. Keeping a close eye on developers/team leaders to see if there are any problems that have simpler solutions or would be better done by an expert/outside resource or using a 3rd party tool.

3. Helping to make sure that the ongoing quality of the product is good standards are enforced. E.g.
a. Identify opportunities for automated testing and unit tests.
b. Running code auditor/SQL auditor/FXCop.
c. The most effective tools are used such as using code generators for repetitive work.
d. Suggesting refactoring possibilities if there is repetitive code.

4. Helping to Removing any client-side or developer-side bottlenecks to development (e.g. following up questions that the client is not answering/developers just waiting on response or are stuck with a product bug or coding issue)

5. Helping developers to determine what should go into the current release and what should go later (scoping – ie what is in scope/out of scope)

6. Updating the scope documents (this is not done in normal projects; we typically create the document and leave it to gather dust)

2 main tasks are performed by developers which were previously classified as “Project Management” by us are:
1. “Project Administration” which includes creating releases and sending out release plans and done emails & timesheets
2. “Requirements Gathering, Client Meetings and Clarifications”

With this in mind.... I believe that dedicated project management (e.g. what I’ve been doing with Westlink) should be an optional extra in the template. I have updated the template accordingly as follows:

Updates to Template:
1. Changed “Project Management” to “Project Administration” (I think it reflects our activities more accurately for that task). I kept Project Administration (primarily Timesheets and Invoicing) at 2 hours per 40 work hours.

2. Added new task type – “Requirements Gathering, Client Meetings and Clarification.” I put this down as 4 hours per 40 work hours.

3. Added an “Optional Extra Items” Group to clearly indicate that a dedicated Project Manager is not normally used on our projects. Our normal task system (eXtreme Emails) provides basic project management capabilities.
4. Includes the “Weeks” calculation

Figure 1 - Updates to the SSW Proposal Template







Figure 2 - Added Dedicated Project Management as an optional Extra for the whole project


==========
</Email Snippet>
==========

Tuesday, 13 November 2007

Automating Deployment between Development, Test and Production Environments for ASP.NET applications

This is a great way of automating the deployment of the correct settings to your Dev, Test and Production Environments - courtesy of Scott Gu:

Use ASP.NET Web Application Projects (which have MSBuild based project files) Open the VS Configuration Manager and create new "Dev", "QA", "Staging" build configurations for your project and solution Add new "web.config.dev", "web.config.qa", and "web.config.staging" files in your project and customize them to contain the app's mode specific configuration settings Add a new "pre-build event" command to your project file that can automatically copy over the web.config file in your project with the appropriate mode specific version each time you build the project (for example: if your solution was in the "Dev" configuration, it would copy the web.config.dev settings to the main web.config file).Once you follow these steps, you can then just pick the mode your solution is in using the configuration drop-down in the VS standard toolbar

For more, see:
http://weblogs.asp.net/scottgu/archive/2007/09/21/tip-trick-automating-dev-qa-staging-and-production-web-config-settings-with-vs-2005.aspx

Friday, 9 November 2007

Important Tips for budding Software Developers & Computer Science Students

Some of the most important things you should learn before you get out into the big bad world of software development:
  1. Learn about money and accounting. Debit & Credits, Balance Sheets and Profit & Loss Statements. Pretty much everything you do in the real world (unless you are a low-level system or driver developer) deals with business concepts - so you should learn it as soon as possible. It will give you a head start against all the other technically proficient programmers. A combined accounting/IT degree is ideal.
  2. Learn one of the things they don't teach you at uni - to talk to customers. Just because you can code in your sleep doesn't make you a good software developer. I have seen this time and time again. The "soft skills" are what set you apart from the other job candidates. Make sure this comes through in your job interview. Confidence will get your 50% of the way to setting you apart from the pack.

Wednesday, 7 November 2007

The power of Lambda Functions

(Tip thanks to my mate Eric Phan):

Lambda functions are essentially anonymous functions (in-line functions) that you can write instead of creating a new method to perform this once off operation.

A good example is filtering a list

I have a list of customers and I want to know who is more than 25 years old and female.
Traditionally (using a filtering method):


   1:  static void Main(string[] args)

   2:  {

   3:     List<Customer> customers = GetAListOfCustomer();

   4:     List<Customer> matureFemaleCustomers = FindMatureFemaleCustomers(customers);

   5:  } 

   6:   

   7:  // Find female customers over 25 years old 

   8:  public List<Customer> FindMatureFemaleCustomers(List<Customer> customers)

   9:  {

  10:     List<Customer> matureFemaleCustomers =new List<Customer>();

  11:     foreach (Customer c in customers)

  12:     {

  13:        if (c.Age > 25 && c.Gender == Gender.Female)

  14:        {

  15:           matureFemaleCustomers.Add(c);

  16:        }

  17:     }

  18:     return matureFemaleCustomers;

  19:  } 


In C# 2.0 with generics:



   1:  static void Main(string[] args){ List<customer> customers = GetAListOfCustomer();

   2:  List<customer> matureFemaleCustomers = customers.FindAll( delegate(Customer c) { return c.Age > 25 && c.Gender == Gender.Female; } );}



In C# 3.0 with Lambda Functions



   1:  static void Main(string[] args) 

   2:  { List customers = GetAListOfCustomer(); 

   3:    List matureFemaleCustomers = customers.Where(c => c.Age > 25 && c.Gender == Gender.Female);

   4:  } 





Simple and elegant!

Dynamic Queries in LINQ - and LINQPad

If you are creating a keyword-style search in LINQ, you can use this wrapper to make your life easier
http://www.albahari.com/expressions/

You can also use LINQPad to test out your LINQ to SQL queries: http://www.albahari.com/linqpad.html. One of the drawbacks I found is that it doesn't currently support views.

Labelling VS Branching in TFS - Source Control Best Practices

TFS provides both branching and labelling. The question is: when should I use branching and when should I use labelling?.
As a general rule:

If you just want to mark a set of code as a milestone, then label. Otherwise use branching - you never know when you will need to patch your code!

See http://blogs.msdn.com/team_foundation/archive/2005/02/23/379179.aspx
for a more detailed explanation. Buck Hodges says:


"For anything that you don't want to service or patch, labeling is sufficient. For a release that you do want to service or patch, branching is the way to go. By having a separate branch, you'll have a place to check in your changes and maintain the code while continuing development on the main branch. Using merge, you can move changes between branches."

Issues with LINQ Order By Queries when using Distinct

Encountered an unusual issue today whereby the .Distinct() extension method call on a LINQ query will actually mean that the output SQL will NOT have an order by clause in it. Distinct basically forces an ignore on any orderby statements that you have:

- This will NOT generate the Order By


   1:  var test = (from c in db.CreditRequests

   2:  where c.DateRejected != null

   3:  orderby c.DateRejected

   4:  select c).Distinct();



- This WILL generate the Order By


   1:  var test = (from c in db.CreditRequests

   2:  where c.DateRejected != null

   3:  orderby c.DateRejected

   4:  select c);





A way around this is to use Lamda Expressions



   1:  var test = (from c in db.CreditRequests

   2:                          where c.DateRejected != null

   3:                          select c).Distinct().OrderBy(d => d.DateRejected).OrderBy(d => d.CreditRequestID);

How to concatenate strings from mutliple rows into one in SQL Server 2000 or 2005

"How to concatenate (aka join) strings from mutliple rows into one in SQL Server 2000 or 2005". This is a very common question, so here is a simple answer:


   1:   

   2:  CREATE TABLE Colors 

   3:  ( 

   4:  Color VARCHAR(32) 

   5:  ) 

   6:  GO 

   7:  SET NOCOUNT ON 

   8:  INSERT Colors SELECT 'red' 

   9:  INSERT Colors SELECT 'orange' 

  10:  INSERT Colors SELECT 'blue' 

  11:  INSERT Colors SELECT 'green' 

  12:  GO 

  13:  DECLARE @colors VARCHAR(1024) 

  14:  SELECT 

  15:  @colors = COALESCE(@colors + ',', '') + Color 

  16:  FROM 

  17:  Colors 

  18:  SELECT Colors = @colors 

  19:  GO 

  20:  DROP TABLE Colors 

  21:  GO


Thanks to http://databases.aspfaq.com/general/how-do-i-concatenate-strings-from-a-column-into-a-single-row.html

Monday, 5 November 2007

Keeping all your connection strings in the Web.Config when using library classes (aka simplified deployment and configuration model)

One of the problems with using multiple projects e.g. DataAccess, BusinessLogic, etc classes is that the Data Access Classes need to be able to read from the Web.Config in the Web.UI layer. However, we sometimes cannot Reference the WebUI layer directly because (apart from being a bad design), it would create a circular reference.

There are a few possible solutions to this. One is to use the VS 2008 Client Application services as per http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2007/06/03/9463.aspx
However, this is dependent upon the service name being consistent. This is a problem on a local Cassini development machine which can change port numbers. If you want a low maintenance approach to reading connection strings from your associated web project:
  1. Add A Reference to System.Web in your library project. This will give you access to the WebConfigurationManager class.
  2. Go to your data access project e.g. "Data", and go to the settings section.
  3. Create your connection string in the settings section.
  4. Add the following code so that your values are set when the settings are initialised.




   1:  using System.Web.Configuration;

   2:   

   3:  namespace Pfizer.CreditReturns.Data.Properties {

   4:      

   5:      

   6:      // This class allows you to handle specific events on the settings class:

   7:      //  The SettingChanging event is raised before a setting's value is changed.

   8:      //  The PropertyChanged event is raised after a setting's value is changed.

   9:      //  The SettingsLoaded event is raised after the setting values are loaded.

  10:      //  The SettingsSaving event is raised before the setting values are saved.

  11:      public sealed partial class Settings {

  12:          

  13:          public Settings() {

  14:   

  15:              //

  16:              this["CreditReturnsConnectionString"] = WebConfigurationManager.ConnectionStrings["PfizerCreditReturnsConnectionString"].ConnectionString;

  17:              this["DWHS_CreditReturnsConnectionString"] = WebConfigurationManager.ConnectionStrings["PfizerCreditReturnsDataWarehouseConnectionString"].ConnectionString;

  18:   

  19:              //System.Console.WriteLine(this.CreditReturnsConnectionString);

  20:              // // To add event handlers for saving and changing settings, uncomment the lines below:

  21:              //

  22:              // this.SettingChanging += this.SettingChangingEventHandler;

  23:              //

  24:              // this.SettingsSaving += this.SettingsSavingEventHandler;

  25:              //

  26:                          

  27:   

  28:          }

  29:          

  30:          private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) {

  31:              // Add code to handle the SettingChangingEvent event here.

  32:          }

  33:          

  34:          private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) {

  35:              // Add code to handle the SettingsSaving event here.

  36:          }


This example will load your configuration settings from the Web.Config at runtime.

Databinding in Page_Load VS LINQ Data Source Selecting Event

While it is nice to have all your databinding code in Page_Load (e.g. in a LoadData() method), it is preferable to use the DataSource Selecting Event. e.g.


   1:  linqDSProducts_Selecting(object sender, LinqDataSourceSelectEventArgs e)


This is because you retain full support for Data source pagination and sorting - which equals less code (declarative or programmatic). For more details on using the Selecting event with LINQ datasources, see
http://www.visualbuilder.com/viewdetail.php?type=1&id=1591&group_id=11