The Musings and Findings of Software Consultant David Klein (Sydney, Australia)
Wednesday 31 October 2007
Where can I get the Visual Studio 2008 Runtime installer?
Friday 26 October 2007
Creating a root Virtual Path usng Cassini
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
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
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
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
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
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
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
- 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).
- Collection initializers (so I can set properties inline with the call to the constructor).
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
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
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."); }
}
Saturday 13 October 2007
Wednesday 10 October 2007
PLINQO - Professional LINQ Objects
See:
http://community.codesmithtools.com/blogs/pwelter/archive/2007/08/08/plinqo.aspx
Contextual Autocomplete - aka AutoCompleteExtender that allows multiple input values
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
}
private string ConvertIntToString(int intParameter)
{
return intParameter.ToString();
}
Saturday 6 October 2007
Cost Effective UML Modelling - Altova UModel 2008 Released
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
- Zimmer required updates to the database, so I used the driver to perform direct updates (a SQL linked server doesn't work with updates).
- 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 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?
- 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
- 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"
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
- Remove the ServicePath from the extender attributes altogether.
- Specify the name of your method in the ServiceMethod attribute,
- 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.Script.Services.ScriptMethod]
{
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=";, :">