.Net ramblings
# Tuesday, 02 October 2007
Bug in ComboBox SelectedIndex
big bug in ComboBox control here: http://support.microsoft.com/default.aspx?scid=kb;en-us;327244
you have to set the SelectedIndex to -1 twice in a row if you want it to take effect!
i came across this in a compact framework app, but it looks like it applies to win-forms as well.


Tuesday, 02 October 2007 17:17:18 (GMT Daylight Time, UTC+01:00)  #    Comments [0]  .Net Compact | .Net Windows Forms

# Tuesday, 18 September 2007
Profiler For SQL 2005 Express
wow, i never knew this existed: http://sqlprofiler.googlepages.com/
thanks to nikolay.zhebrun
it works great.


Tuesday, 18 September 2007 13:43:58 (GMT Daylight Time, UTC+01:00)  #    Comments [0]  .Net General | Database

LINQ Sql Using Skip And Take
Here is the sql that is generated by a DataContext when you use Skip() and Take() to efficiently select records for the grid:
SELECT TOP 10 [t1].[Name], [t1].[Address], [t1].[Tel1], [t1].[Tel2], [t1].[Email], [t1].[DateCommenced], [t1].[Comments], [t1].[Active], [t1].[Fax]
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY [t0].[Name], [t0].[Address], [t0].[Tel1], [t0].[Tel2], [t0].[Email], [t0].[DateCommenced], [t0].[Comments], [t0].[Active], [t0].[Fax]) AS [ROW_NUMBER], [t0].[Name], [t0].[Address], [t0].[Tel1], [t0].[Tel2], [t0].[Email], [t0].[DateCommenced], [t0].[Comments], [t0].[Active], [t0].[Fax]
FROM [dbo].[Table1] AS [t0]
) AS [t1]
WHERE [t1].[ROW_NUMBER] > @p0
-- @p0: Input Int32 (Size = 0; Prec = 0; Scale = 0) [50]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.20706.1

it's not immediately obvious why they use the subquery like this, but i'm sure they have been very thorough in optimising LINQ. 


Tuesday, 18 September 2007 13:22:32 (GMT Daylight Time, UTC+01:00)  #    Comments [1]  .Net General | Asp.Net | Database

# Monday, 17 September 2007
LINQ with strings
Today was the first time i used LINQ with non-database data.  I always knew you could do it but didn't realise how satisfying it would actually be to use LINQ to crunch out some results that i would otherwise have to code up in some gnarly routine involving on-the-fly DataTables and DataView filters etc.  Also, I'm mainly posting this because i can never remember the LINQ group by syntax...

i have a plain list of reference numbers, e.g. 1.a, 2.b, 3.c, 4.a, 10.d, 11.c etc.  they contain duplicates and i was asked to figure out the top 10 most popular references.  A simple regex separates out the numbers, i could also have used string.split() but regex is better in my case.  Copy each one into an array and then a simple LINQ query on the array gives me the top 10 references:
List<string> lst = new List<string>();
foreach(Match m in Regex.Matches(this.txtInput.Text, @"\w+\.\w+", RegexOptions.IgnoreCase))
lst.Add(m.Groups[0].Captures[0].Value);
var results = (from l in lst group l by l into g select new {Ref = g.Key, Total = g.Count()}).OrderByDescending(z => z.Total).Take(10);
foreach(var v in results)
this.txtOutput.Text += v.Ref + "\t" + v.Total + "\r\n";


Monday, 17 September 2007 15:01:26 (GMT Daylight Time, UTC+01:00)  #    Comments [0]  .Net General

# Friday, 31 August 2007
SQL: order by with parameters of different datatype
It's fair enough that SQL won't accept a parameterised query like below, because it cannot verify that the parameter is referring to a valid column.
select * from table order by @OrderBy
the work around then is to use a case statement like so:
select * from table order by case 
when @OrderBy = 'Column1' then Column1
when @OrderBy = 'Column2' then Column2
end
but i ran into a problem with this approach, where sql raises an error if the datatypes of the columns are not all the same, e.g. you may want to sort by an Int or NVarChar column.  the datatype precedence rules applied to the case statement are well explained in this post on google groups.  the solution posted by Erland Sommarskog is to have a separate case statement for each clause.  so the more robust approach is like so:
select * from table order by 
case when @OrderBy = 'Column1' then Column1 end,
case when @OrderBy = 'Column2' then Column2 end




Friday, 31 August 2007 12:36:35 (GMT Daylight Time, UTC+01:00)  #    Comments [0]  .Net General | Database

# Wednesday, 29 August 2007
IE problems with javascript window.open()
i couldn't figure out why IE was giving me an 'invalid argument' for using code like:
window.open("http://whatever", "1234321-ABCDE-1234231");
the problem is that IE only accepts alphanumeric and underscore characters for the second parameter (window name), so the hyphens were causing problems.  just trim them out and it will work fine.


Wednesday, 29 August 2007 13:34:31 (GMT Daylight Time, UTC+01:00)  #    Comments [1]  Asp.Net

# Friday, 27 July 2007
VS 2008: First impressions
Just got up and running with VS 2008 Beta 2 and converted my Orcas Beta 1 projects over to VS 2008.  have a read of Scott Gu's post on downloading and installing.  whaddya know? VS 2008 hasn't crashed yet :)  seriously though, this is a major relief to see good stability.  as a developer it did my head in to have to keep nuking devenv.exe and losing a few minutes work, driving me quietly insane.  hopefully those days are over now.  Beta 1 was a thousand times better than VS 2005, but Beta 2 looks even better. 

in terms of breaking changes with upgrading projects etc., here are some tidbits i came across
  • the namespace System.Data.Linq.Expressions no longer exists.  i just deleted this namespace and it worked fine, they must have moved various classes back into the root Linq namespace or something.
  • if you use SqlMetal, you will have to use the new version of this tool, which i found at C:\Program Files\Microsoft SDKs\Windows\V6.0A\Bin\SqlMetal.exe. The generated code from the new tool is very different to before so you will need to regenerate in order to use the new LINQ libraries.
  • web.config changes are also introduced.  the best way to figure out the changes is to open up a blank web application and compare the standard web.config in the empty project to your own web.config.
  • Crystal Reports assembly version numbers are the same with this release of VS, 10.5.3700.0, so no changes here since Beta1.
i'll post more next week when i have some real experience using VS 2008.  so far so good.  great job Scott & Co.

Update 30 July 2007 - Deployment Issues
  • To deploy a VS 2008 beta 2 web application to a Server 2003 with .net runtime 2.0, you need to install 3.5 of the runtime.  i got all sorts of web.config errors due to the new syntax with beta 2.  A web app compiled against v3.5 in Beta 1 had no trouble running on top of the 2.0 runtime with the various v3.5 DLLs deployed to the /bin directory, however this doesn't seem to be possible anymore.  it took me a while to find the correct download for the 3.5 beta 2 redistributable, it installed in about 10 mins and required a reboot, and did not affect any of the v1.1 and v2 web sites running on the server. 


Friday, 27 July 2007 17:49:16 (GMT Daylight Time, UTC+01:00)  #    Comments [0]  .Net General | .Net Windows Forms | Asp.Net

# Wednesday, 18 July 2007
JPEG Resizing Quality Problems .Net
public static ImageCodecInfo GetEncoderInfo(String mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for(j = 0; j < encoders.Length; ++j)
if(encoders[j].MimeType == mimeType)
return encoders[j];
return null;
}

public static Bitmap resizeImage(Bitmap originalImage, int max)
{
// never increase the size of an image from this method because it loses resolution
if(originalImage.Height <= max && originalImage.Width <= max)
return originalImage;

int height, width;
if(originalImage.Width > originalImage.Height)
{
width = max;
height = (int)(max/((double)originalImage.Width / (double)originalImage.Height));
}
else
{
height = max;
width = (int)(max/((double)originalImage.Height / (double)originalImage.Width));
}

Bitmap thumb = new Bitmap(originalImage as Image, width, height);
Graphics resizer = Graphics.FromImage(thumb);
resizer.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
resizer.DrawImage(originalImage, 0, 0, width, height);

resizer.Dispose();
return thumb;
}

public static bool ThumbnailCallback()
{
return false;
}

this lovely code highlighted in red solved problems i was having with resizing JPEGs using .net.   without the code in red, some images would appear very grainy when reduced in size.  i got this tip from http://forums.asp.net/p/532033/532033.aspx.



Wednesday, 18 July 2007 17:41:41 (GMT Daylight Time, UTC+01:00)  #    Comments [0]  .Net General | .Net Windows Forms

# Sunday, 15 July 2007
Migrating from Windows software RAID to Hardware RAID
i started a thread on the DELL community forums asking about migrating my windows 2003 software RAID setup to a hardware RAID controller, without reinstalling windows.  you can read the thread to get the context for these documented instructions below:

success. after a marathon 7 hour session in the datacenter it finally works. however the whole thing was much too stressful for my liking!

my problems were significantly compounded by the lack of a CD drive, so i had to get my imaging software (Terabyte Unlimited 'Image') to boot via a USB drive, which was never going to be easy with a Dell BIOS etc. although to be fair it does work, once you understand that you must reboot after configuring the BIOS to recognise the USB drive, and then go into the bios again and tell it what order to boot in.

i thought it useful to document the steps i took in case anyone else is in a similar situation.

in summary, i wanted to do an in-place migration from software raid in windows 2003 to hardware raid with the dell/adaptec CERC 6 channel SATA RAID card. all without using a CD drive (trust me, get one if you ever have to do this. i tried but couldn't get the BIOS to recognise the drive).

i got a very helpful email from Terabyte support before starting the migration:
****************************************
Hello,
If you just have a single simple volume (once the software RAID is broke), you can remove the dynamic drive by changing the file system ID back to 7 (via BootItNG partition work/properties). If you backup while dynamic, it will just backup the whole dynamic partition sector by sector.
So what I'd personally do is:
1. break the raid, (maybe remove the secondary drive as a backup)
2. ensure it's a single simple volume that's left (I'm assuming you just have c,
3. boot BootItNG, change the file system ID to 7,
4. boot back to windows (if it didn't boot, just put back the file system id to dynamic),
5. install the CERC drivers (if needed),
6. do the image, with byte-for-byte validation (slower in windows because it thrashes a bit),
7. ensure I can access the image to restore with Image For Dos or Image For Linux.
8. Configure the hardware RAID,
9. restore the image (use additional option of TA when restoring the boot partition).
*****************************************

windows software RAID requires dynamic disks, which are not officially reversible back to basic disks. this further complicated the process because i wanted to go back to basic disks for the RAID. you can convert back to a basic disk as described above with BootItNG, and also via the DskProbe windows support tool, instructions available from: http://www.nthelp.com/NT6/dynamic.htm. although i also read that if you use software raid, you shouldn't use this approach, so i unplugged the second drive, booted into windows and removed the mirror, and the offline disk from Windows Disk Manager. Then i changed to a basic disk via DskProbe and it did work, although DiskManager reported a single drive, which was marked offline or something very worrying like that. however the files were all there and working etc.

i followed the instructions above, although i did install the RAID card before changing any drivers or removing the software RAID, and i got windows to load up the drivers for it, so it would work automatically once booting from the RAID controller. this was one less problem in the equation and i was keen to get it out of the way as early in the process as possible.

to get Image For Dos to boot off USB, i used the bundled 'makedisk' utility without any init strings. I configured the BIOS to emulate a USB drive as a hard drive, floppy did not work. Then rebooted and changed the boot sequence so that the USB drive came first.
the disk image (of the 'active' drive, the other was still unplugged) took about 30 minutes with byte-for-byte validation, ~25Gb of data, copying to an external USB drive, using ImageForDos. i had to change the keyboard to a PS2 connection because in my first attempt the program locked out the keyboard after identifying the USB drive.

then i verified that i could access the image with the restore option. and went ahead with setting up a RAID mirror in the CERC configuration utility. plugged in both drives into ports 4 and 5. incidentally i needed a longer SATA cable for drive0 because it only had a 4inch cable and the RAID plugs on the card are miles away from the drive bays. The cable for the other drive fit with a stretch into the last port on the card.
so i did a reboot, pressed Ctrl-A etc. initialise the 2 drives. set up a RAID 1 mirror as per the manual instructions. i used the quick-init option because i was really in a hurry to get the job done. i set high priority also. i did not 'clear' the drives, but this may have been no harm to do.

then i rebooted and loaded my restore image with ImageForDos. the 'TA' option referred to in the instructions marks the partition active and inserts a standard MBR. it took about 30 minutes again and finished successfully. i was a bit worried because the restore program asked me which drive did i want to restore to, i assumed it didn't matter and chose the second drive. my cause for concern was that i wasn't sure if the controller would do it's mirroring thing during this restore operation. my fears were doubled when i rebooted after the restore, and got a message something like "no operating system, F1 to continue, F2 to setup". at this stage i had about 5 minutes before 'visiting hours' were over in the datacentre and i would have had to take the server home for the weekend to get it ready before monday 9am, no thanks! i thought it had failed. i was about to pack up and go home when i went in to look in the bios one last time in case i missed anything. i didn't change anything, and i also inspected the RAID config via the BIOS utility, but again, didn't change anything. i tried to turn off caching as a desperate attempt to fix the boot problems, but it wouldn't accept the change for some reason. anyhow, i rebooted again, said a quiet prayer, and i couldn't believe it but windows started loading, i was never so happy to see that Windows logo appear on screen (you probably had to be there to understand, 7 hours of white noise from being surrounded by a gzillian racks, no lunch, very frustrated etc!)

now all is well with the 2 drives showing up 'optimal' in the DELL/Adaptec storage manager. i gather this means that the build/verify has already completed in the background. the URL for the storage manager is http://tinyurl.com/2nddta

i didn't want to install all that dell 'management' software that came with the RAID card.
another good thing i forgot to mention. windows disk manager now shows a healthy primary partition for C:. it doesn't say 'fault tolerant' anymore obviously because the software raid is gone and the hardware RAID is abstracted away from the windows disk manager.

hope this helps someone someday.


Sunday, 15 July 2007 00:33:07 (GMT Daylight Time, UTC+01:00)  #    Comments [3]  General | Windows Server

# Thursday, 05 July 2007
HowTo: Invert datagrid rows and columns
this is a bit of a strange one.  I have a table and it has approx 60 columns, with only one row being shown.  I wanted a human-readable display of all the fields in the table for any given row, i.e. i didn't want the user to have to scroll several screens across just to read the data for a single row.  Vertical scrolling is obviously easier, so i wrote a Repeater implementation, which works nicely.  For a table with X columns and Y rows, the output will have X rows and Y columns.  Screenshot below shows the output of all the ServerVariables in a HTTP-request, which is a good example of a datasource that does not naturally fit the traditional 'grid' display.
<!-- ASPX usage -->
<xyz:VerticalGrid ID="vGrid" runat="server" />

// sample databinding...
this.vGrid1.DataSource = new DataView(dataSet1.Tables[0]);
this.vGrid1.DataBind();

/// <summary>
/// A Repeater control that shows the rows and columns inverted.
/// No template is required for the items.
/// The datasource must be an ADO.Net DataView
/// </summary>
[ToolboxData("<{0}:VerticalGrid runat=server></{0}:VerticalGrid>")]
public class VerticalGrid : Repeater
{
public VerticalGrid()
{
this.EnableViewState = false;
}

protected override void Render(HtmlTextWriter writer)
{
// invert the rows and cols
if (!(base.DataSource is DataView))
throw new Exception("Error: only a dataview datasource can be used");

DataView dv = base.DataSource as DataView;

// output start of table
writer.Write("\n<table class='grid'>");

// output each row
for (int i = 0; i < dv.Table.Columns.Count; i++)
{
writer.Write("\n<tr class='{0}'><th>{1}</th>", i % 2 == 1 ? "gI" : "gA", dv.Table.Columns[i].ColumnName); // output the column name in the first cell. class names gI and gA are abbreviations of gridItem gridAlternatingItem, to reduce output markup.
for (int j = 0; j < dv.Count; j++)
writer.Write("<td>{0}</td>", dv[j].Row.IsNull(i) ? "&nbsp;" : dv[j][i].ToString());
writer.Write("</tr>");
}
writer.Write("\n</table>");
}
}




Thursday, 05 July 2007 15:53:40 (GMT Daylight Time, UTC+01:00)  #    Comments [0]  Asp.Net