Wednesday 31 October 2007

Where can I get the Visual Studio 2008 Runtime installer?

You can get the Visual Studio 2008 runtime installer from X:\WCU\dotNetFramework\dotNetFx35setup.exe where X: is the virtual drive path of your Visual Studio 2008 Setup. Note that for Vista-based installs you must run it in Admin mode for the setup to work correctly - otherwise the setup will fail.

Friday 26 October 2007

Picture of the day!

Don't say we didn't warn you!


Creating a root Virtual Path usng Cassini

If you ever wondered how to create a root Virtual path using the new ASP.NET2.0 Web Development Server (Casini)? It’s easy!

At the moment our application is having problems mapping images correctly because it will not accept http://localhost:8080/WebUI/default.aspx
Even if I create an IIS virtual directory http://localhost/WebUI/default.aspx it will still not be good enough.
(I know I know it is this stupid third party CMS and I have no control over its relative mappings)

I need something like the following:

http://localhost:8080/default.aspx

Create an external tool from “Tools” > “External Tools…”
Add a new external tool and call it “WebServer on port 8080 (or whatever port you want)
In the command point to casini which is located at C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\WebDev.WebServer.EXE
Have the argument as the following so that you assign the port and the path is directly in the Project Directory as follow:/port:8080 /path:$(ProjectDir)
Click ok
Then run the tool by going “Tools” > “Web Server on Port 8080”
Cassini will now run in the task bar with http://localhost:8080/
In the Properties Page of your web project, select “Start Options” and select “Use custom server” radio button
Type the Base URL as http://localhost:8080/
OK

Wednesday 24 October 2007

LINQ and the DataContext Attach() method

I just found out today that it is possible (rather than passing a DataContext object around by reference or refetching the objects), to "re-attach" objects to another data context. You can then perform updates on the object and related collection. See this article for more details:
http://blogs.msdn.com/dinesh.kulkarni/archive/2007/10/08/attach-if-you-have-something-detached.aspx

Tuesday 23 October 2007

C# code format for Blogs

This is just a simple formatting tool that you can use online to format your code using "pre" tags

http://www.manoli.net/csharpformat/

My code samples in previous posts use this tool for formatting. Also supports XML, HTML, VB, SQL and Powershell scripts (aka Monad).

C# Coalesce

The ?? coalesce operator checks whether the value on the left hand side of the expression is null, if so it returns the value on the right hand side. If the value on the left hand side is not null, then the left hand side value is returned:

string message = "Hi Devs";
string result = message ?? "It was null!";

// Outcome: result == "Hi Devs"


string message = null;
string result = message ?? "It was null!";

// Outcome: result == "Message was null"


Note that this is a short-circuited operator. For example:

a = b ?? c;
translates to:
a = ( b == null ? c : b);
Which means the expression c is only evaluated if b is null. There is no equivalent in VB (short of performing an iif())

LINQ - Best Practices with Anonymous Types

Anonymous types allow you to return data from multiple LINQ objects which are strongly typed, determined at runtime. However, anonymous types only have local scope. I believe that best practice with LINQ objects is to NOT cast the anonymous type to an object and consume it (because designers e.g. datagrid cannot determine the properties of the type).

Instead of casting to objects and consuming, you should really create a wrapper class which defines the standard contract that can be returned from your data access layer.

Example:



   1:  using System;

   2:  using System.Collections.Generic;

   3:  using System.Text;

   4:  using System.Linq;

   5:  using System.Configuration;

   6:  using LendLease.InvestmentManagement.AssetMaintenance.Entities;

   7:   

   8:  namespace LendLease.InvestmentManagement.AssetMaintenance.Services

   9:  {

  10:      public class CityName

  11:      {

  12:          public string DisplayValue {get; set;}

  13:      }

  14:      class AddressService : IAddressService 

  15:      {

  16:   

  17:          static AddressService()

  18:          {

  19:          }

  20:   

  21:          public IList<CityName> GetAllCities()

  22:          {

  23:              InvestmentManagementDbDataContext context =

  24:                   new InvestmentManagementDbDataContext();

  25:   

  26:              var query = (from ps in context.Addresses

  27:                          select new CityName { DisplayValue = ps.City }).Distinct();

  28:              return query.ToList();

  29:          }

  30:      }

  31:  }





For more info, see http://weblogs.asp.net/scottgu/archive/2007/05/15/new-orcas-language-feature-anonymous-types.aspx?CommentPosted=true
and http://blogs.msdn.com/miah/archive/2007/09/07/outsmarting-the-limitations-of-anonymous-types.aspx for some more comments on this.

Changes to System.Data.Linq.ChangeSet and System.Data.Linq.Table from VS 2008 Beta 2 to RTM

Just installed VS 2008 release candidate, and there were some compile issues related to the System.Data.Linq.ChangeSet class. One of the changes that affected me in the upgrade was the move to move "Database mindset" language. I found this move from "ModifiedEntity" to "Updates" unusual because it is terminology brought from the database field. There is also a change to System.Data.Linq.Table where the "Add" method is now "InsertOnSubmit". The "Remove" method is now "DeleteOnSubmit". Seems like a throwback to me!

Another issue is that my LINQ dbml files had conversion issues, showing the error:
"There is no Unicode byte order mark. Cannot switch to Unicode." The fix was just to remove the encoding attibute in the XML document description:

<?xml version="1.0" encoding="utf-16"?>



public bool Run(ChangeSet changedObjects)
{
List<trackedobject> tracked = new List<trackedobject>();
foreach (object o in changedObjects.AddedEntities)
tracked.Add(new TrackedObject { Current = o, IsNew = true });
foreach (object o in changedObjects.ModifiedEntities)
tracked.Add(new TrackedObject { Current = o, IsChanged = true });
foreach (object o in changedObjects.RemovedEntities)
tracked.Add(new TrackedObject { Current = o, IsDeleted = true });
return Run(tracked);
}

needs to change to:


public bool Run(ChangeSet changedObjects)
{
List<TrackedObject> tracked = new List<TrackedObject>();

foreach (object o in changedObjects.Inserts)
tracked.Add(new TrackedObject { Current = o, IsNew = true });
foreach (object o in changedObjects.Updates)
tracked.Add(new TrackedObject { Current = o, IsChanged = true });
foreach (object o in changedObjects.Deletes)
tracked.Add(new TrackedObject { Current = o, IsDeleted = true });

return Run(tracked);
}

Monday 15 October 2007

Enabling or Disabling All controls in an ASP.NET Page using Reflection

Every ASP.NET developer should know that the “Enabled” property doesn’t exist in the base System.Web.UI.Control class. Consequently, it makes it a little difficult looping through the controls collection because then you have to cast each control into its relevant type e.g. "(Button)myControlName" to be able to change this property. Typically, you would have code like this:


if (control.GetType() == typeof(Button))
{
Button button = (Button)control;
button.Visible = (bool)item.IsVisible;
button.Enabled = (bool)item.IsEnabled;
}
else if (control.GetType == typeof(TextBox))
{
TextBox textBox = (TextBox)control;
textBox.Visible = (bool)item.IsVisible;
textBox.Enabled = (bool)item.IsEnabled;
}



.... (n number times) for each and every required control type. This solution above could have 100s of lines for all the control types that require support.

See a simplified and more elegant alternative below that I came up with, using reflection - it ends up with just 2 lines rather than 10s or hundreds as seen in the previous approach...



///
/// Populate the secured items listing, and apply visible/disabled properties on each.
///
public void SecureControls()
{
ManagerContext db = new ManagerContext();

//Grab list of controls for current page
Control contentControl = this.Master.FindControl("BodyPlaceHolder");
List securedItems = db.Manager.SecuredItem.GetSecuredItemByPageName(this.PageName);
Control control = null;

//Set visible and enabled property; enabled property set based on reflection to control type
foreach (SecuredItem item in securedItems)
{
control = contentControl.FindControl(item.ControlName);
control.Visible = (bool)item.IsVisible;

if (control.GetType().GetProperty("Enabled") != null)
{
control.GetType().GetProperty("Enabled").SetValue(control,(bool)item.IsEnabled,null) ;
}

}

Another alternative to this approach is to custom controls with a standard interface e.g. ISecuredItem but this requires more work because all controls have to have a custom implementation.

New language features in .NET 3.5

I am currently using Beta 2 for development of a new Credit Returns system for Pfizer. There are some very handy features in 3.5 that make life a lot easier and less code intensive! My favourites are
  1. Automatic properties (so I don't have to manually create private variables to hold the values of my properties but still have the benefits of properties over public fields).
  2. Collection initializers (so I can set properties inline with the call to the constructor).
For more information on these new features, see Scott Guthrie's blog:

http://weblogs.asp.net/scottgu/archive/2007/03/08/new-c-orcas-language-features-automatic-properties-object-initializers-and-collection-initializers.aspx

Debugging CodeSmith Templates in Windows Vista

I had some problems with Windows Vista trying to debug the PLINQO (Professional Linq) CodeSmith templates. To get around the problems of crashing and restarting ( As described by Paul Welter,
http://community.codesmithtools.com/blogs/pwelter/archive/2007/06/09/tips-amp-tricks-debugging.aspx)

"There are some problems with using the Just-In-Time debugger in Windows Vista. First you need to make sure you have all the latest service packs installed. Next, the debugger in Vista will cause CodeSmith to hang when you finish debugging. You can work around this issue by updating the Just-In-Time debugger setting DbgJITDebugLaunchSetting. The setting is found in the registry at [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework]. Change the value of DbgJITDebugLaunchSetting to 2. This will cause the debugger dialog to be displayed immediately when your code hits a breakpoint. This will also allow control to return to CodeSmith when you continue the execution of the template from the debugger."

Note that the original value on my Vista machine was 10 Hex (16).

Sunday 14 October 2007

How to change default printers in .NET

Apparently, the System.Management namespace gives access to the WMI classes. However, that only gives me a configurable Win32_Printer object and a readonly Win32_PrinterConfiguration object...

There is a way around this one:

public static void SetDefaultPrinter(string name)
{
try {
Type t = Type.GetTypeFromProgID("WScript.Network");
Object o = Activator.CreateInstance(t);
t.InvokeMember("SetDefaultPrinter", System.Reflection.BindingFlags.InvokeMethod, null, o, new object[] {name});
}
catch (Exception)
{ throw new Exception("Unable to set a default printer."); }
}

Wednesday 10 October 2007

PLINQO - Professional LINQ Objects

Working on the Credit Returns system for Pfizer, I have started using the PLINQO CodeSmtih templates for generation of LINQ supporting classes. They have a few issues, but they generate separate files for all entities and give you some of the most common queries out of the box (e.g. get GetByForeignKey()) so you don't have to do the monkey work. They also give you absolute control over the output of your dbml files (e.g. it creates separate partial classes for each LINQ entity).

See:
http://community.codesmithtools.com/blogs/pwelter/archive/2007/08/08/plinqo.aspx

Contextual Autocomplete - aka AutoCompleteExtender that allows multiple input values

This handy package allows you to have 2 sources for your AutocompleteExtender. E.g you can use 2 textboxes to provide filters for the one autocomplete.

For more info and the download, see:
http://www.codeplex.com/websf/Wiki/View.aspx?title=Autocomplete_landing_page

Tuesday 9 October 2007

Converting Arrays without for...loop construct

.NET has built-in functionality to convert one collection or array of one type (e.g. List<typename>) to another collection or array of another type. You don't need a for loop to do this at all. Here's how:

(from http://gsharper.blogspot.com/2007/04/convert-integer-array-to-string-array.html)

private void TestMethod(int[] intArray)
{
string[] stringArray = Array.ConvertAll (intArray,new Converter (ConvertIntToString)); string result = string.Join(",", stringArray);
}

private string ConvertIntToString(int intParameter)
{
return intParameter.ToString();
}

Saturday 6 October 2007

Cost Effective UML Modelling - Altova UModel 2008 Released

UModel is one of the cheaper UML Modelling alternatives out there. It Supports UML 2.1.1 and Generates code to C# and Java. One of the nice features is that the 2008 version is now integrated with the VS 2005 IDE and Eclipse development environments.

A review of the 2007 version can be found here:
http://redmondmag.com/reviews/article.asp?editorialsid=554

BPCS IBM DB2 iSeries Integration at Pfizer and Zimmer

2 of my recent clients - who have large American parents (Pfizer and Zimmer) - still use ancient (1960s and 1970s technology) systems for their ERP systems. I constantly surprised to see dinosaur "Green Screen" applications like this running the core of businesses. It is a testament to the motto - "if it ain't broke, don't fix it". The commonality is that both clients are insistent that they will move to SQL 2005 - but it just never eventuates as they are always busy fighting fires. I am also amazed that our .NET applications can actually use these systems through the magic of the IBM driver for DB 2 iSeries editions. I have taken 2 different approaches on each of these clients:
  1. Zimmer required updates to the database, so I used the driver to perform direct updates (a SQL linked server doesn't work with updates).
  2. The Pfizer Application only required readonly use of Invoice data - so we used SQL Server SSIS to cache the data we needed to a data warehouse on the nightly loads. Our application draws data from views based on the data.

I also provided recommendations to use BizTalk for integration - but the clients felt that the additional expense was not worth it.

Microsoft Releases .NET Base Class Library Source Code

According to an email from Scott Guthrie and announced Wednesday AM - http://weblogs.asp.net/scottgu, the .NET framework 2.0 source code has been released under a limited license.

According to Scott:

"As part of our first source release we'll be including the source for the BCL (base class libraries), ASP.NET, Windows Forms, WPF, and ADO.NET. We'll then be enabling more libraries next year. All source will be released under a reference license (which means the source is for reference use only - not for modification, rebuilding, rebranding, copying, etc)."

So it is nice to now have the source code in an easily accessible fashion (Java has had source code opened up for years). However, we could already use .NET Reflector (one of my favourite .NET tools) to see the source code. So what does it mean to have access to the source code?

  1. The big plus is for use in debugging when you need to step through and see what a framework is class is doing. An awesome feature in the final release of 2008 will be the option to download the framework source code while you are debugging! - http://weblogs.asp.net/scottgu/archive/2007/10/03/releasing-the-source-code-for-the-net-framework-libraries.aspx
  2. It is good to learn how things are written so you can write better code in a consistent manner,such as the Provider model when Microsoft opened that up for users to create their own.

Friday 5 October 2007

"No Battle Plan ever survives contact with the enemy"

"No Battle Plan ever survives contact with the enemy" - Helmuth von Moltke the Elder
Recently, I was asked a question about my estimate for fixing and rewriting a property valuation system in Melbourne. I created the estimates after a 5 day specification review of their 26 applications (some of the apps had 300+ aspx pages and 200 user controls) and 20 SQL 2000 databases.

The client was concerned that one of the releases in the project was going to go 30% over the original baseline figure (of we warned the client before this happened - that is why he raised his concerns). He reasoned to me: surely after 5 days of doing the original spec review, I should have been able to give them an accurate estimate for the 6 month project.

This project has highlited some of the hazards that go with estimation - in particular - projects that deal with code written by other developers. The truth of the matter is that it is MUCH more difficult than for estimating a new application. In particular, every line of code can potentially be a shock or a nightmare.

This brings me to offer the quote "No Battle Plan ever survives contact with the enemy". In my case, this skirmish was against buggy code from a stream of developers that had "contributed" their expertise (or lack thereof) to the group of applications. Project Managers be warned! For normal projects, you take the your/the developer's estimate and double it. When you are dealing with other peoples code, I would say it is safer to triple it.

Another point that was also raised that "builders and construction companies give accurate estimates. If they can give a fixed cost for this project, then why can't SSW?"

I believe there is a fallacy amongst the general population (especially within the IT industry) that somehow builders and construction workers give accurate estimates. Nothing could be further from the truth. My boss was given an estimate by a builder for home renovations - that it would take $20K to complete the work on his house at Coogee. $200K later, they were about half way there. All the construction work I have had done (e.g. bathroom renovations) has had "unforeseen problems" and has costed more than what I expected. In fact, there a people paid in the construction industry purely to say whether work is "additional" and will cost extra or not. This is a testament to the fact that the scoping of building projects is innacurate - and often deceptively so. I wouldn't be surprised if a construction company found out a way to make a roof an additional item for a building!

The best thing that a Project Manager can do (in IT, construction or otherwise) in giving estimates is to be transparent and truthful with the client and to break down the project into as many finely grained tasks as possible. Price each component individually. This forces the estimator to examine the project with a fine-tooth comb before one line of code is written. If estimates are broken down an substantiated, then they can be more accurately analysed so we can learn from our triumphs and mistakes (aka underestimates).

Microsoft AJAX Toolkit - using the AutoCompleteExtender without a web service

The AJAX Control Toolkit sample site instructions (http://www.asp.net/ajax/ajaxcontroltoolkit/samples/) do not make it clear that you can use the AutoCompleteExtender without a web service. This is possible without any additional coding. Here's the trick:
  1. Remove the ServicePath from the extender attributes altogether.
  2. Specify the name of your method in the ServiceMethod attribute,
  3. Add your method call to the script of the page itself. It will not work with a codebehind directly. For example:

Put this code in the page markup:
<script runat="server">

[System.Web.Services.WebMethod]

[System.Web.Script.Services.ScriptMethod]

public static string[] GetCompletionList(string prefixText, int count)

{

return "this is sample text".Split()

}

</script>

Also make sure you set the service method to the function name and omit the ServicePath :

<cc1:autocompleteextender id="AutoCompleteExtender1" runat="server" behaviorid="AutoCompleteEx" targetcontrolid="txtOrderNumber" ServiceMethod="GetCompletionList" CompletionInterval="500" EnableCaching="true" CompletionSetCount="20" DelimiterCharacters=";, :">