Thursday, 26 June 2008

Reporting Services Data Corruption

Not sure how you might find this post via google. Not sure what you would search for but maybe: Reporting Services, Corrupted Data, Corrupted Layout, Preview Layout not Working, Array index out of bounds in preview, preview doesn't match data, grouping not working as expected.
Anyway, however you got here, I experienced some very weird stuff in a reporting services report and it was related to RS caching data for the report before changing the sql for the data source. Despite pressing refresh and save and all sorts of things I was getting very weird errors, fields appearing in the wrong place on the preview, weird error messages and just something appearing to be not right. The fix? Close Visual Studio, find the folder with the report in and delete the reportname.rdl.data file and then reopen. VS will requery the data and you should hopefully be alright. Sweet as.

Copy and Paste is Not your friend!!

I heard that quote a few years back from a trainer on a .Net training course and the more time goes by, the truer it becomes. Almost all of my code errors are related to copying and pasting code and forgetting to modify it for the new location. It is not surprising then that there are techniques to avoid copying and pasting or "inheriting from the clipboard" as I've also heard it.
Firstly, if you are copying and pasting, you might well be hiding a required function. Even a simple repetition of 2 lines could be a function:
Rectangle rect = new Rectangle();
rect.Width = 100;

When copied several times (but almost invariably getting renamed in the process) is prone to bugs even though it is surely so simple it is idiot proof!? A "factory method" is simply a method that creates an object for you, hiding the detail, and then passes it back, e.g.:
Rectangle Create100WideRect()
{
Rectangle rect = new Rectangle();
rect.Width = 100;
return rect;
}

Note that although the function is 6 lines instead of the original 2, you only need to replace 3 calls to save room and more importantly there is no grey area, every single place that needs a 100 wide rectangle can call the factory method. Note that if you need to make slightly different instances then either call the factory method and modify the object afterwards or pass the difference into the function and let the function customise it for you. If the items you are creating exist in another library then try and avoid the need for a local variable by passing the create function directly to whatever needs the object like:
ProcessRectangle(Create100WideRect());

and you haven't tied the user to the creation of the object.
The second common reason for copy and paste is when you really need another class. If you are doing two thing similarly, maybe you need a base class doing all the same things and then two sub-classes that specialise the code. Again, doing this means that no two blocks of code are the same because the same bits are put into a base class in 1 place! Just because you think that the two blocks of similar code belong in two functions in the same class doesn't mean you can't create some helper classes purely to use for those two functions rather than:
if ( something ) Function1(); else Function2();

You could instead do:
CurrentHelper->DoFunction();

with no ifs, no buts and solid separation of functionality. You can pass in any variables these functions need to use from your class as constructor or function parameters.
The third common use for copy-and-paste like the second is when your single class actually needs to be abstracted into some sort of hierarchy. Suppose for instance you have a large switch statement with lots of similar code, this could be a place to use a pattern, perhaps "chain of responsibility" or "state pattern" where each case block becomes an object and the code that is the same for multiple blocks can be abstracted into base classes. Remember that two blocks of code is double the bug risk so keeping things in one place means there is only one place to fix it!

Events and access in .Net

I was trying something today in C# (.net) that I thought might be doable, although I wasn't sure.
I wanted to pass the event from an object into another object so that the second (helper) object could subscribe to the event. I think of events as objects so didn't think this would be a problem. I added the event to my event source class:
public event EventHandler WorkDayChanged;

I then passed this event externally to the constructor of my helper class:
ColourBoundRectangle r = new ColourBoundRectangle(w.WorkDayChanged, w.GetTypeNumber);

but when I compiled, I got the error:
The event 'ResourcingBusinessObjects.WorkDay.WorkDayChanged' can only appear on the left hand side of += or -=

I didn't really understand but here is the crack: when you use the word "event" on the event in the event source class, it will only allow external objects to add and remove handlers to the event, even though it is public. If you want to access the event as an event object, you must miss off the word event and use (in my case):
public EventHandler WorkDayChanged;

Which seemed to compile OK anyway! I tried the code but the events weren't being fired (in the debugger I noticed that the events were empty at the time of firing) even though I was calling += on the event with my helper class. Nice having a good debugger, I realised that because the event is really a delegate which seems to behave like a value type, because I wasn't passing it by ref into my helper class, I was adding a handler onto a local copy of the event which went out of scope outside of the constructor. I simply changed my code to:
ColourBoundRectangle r = new ColourBoundRectangle(ref w.WorkDayChanged, w.GetTypeNumber);

And it was all fine. Sweet.

Wednesday, 25 June 2008

Problems opening .net project

I grabbed a .net project off of code project the other day to find out about drag and drop. When I tried to open it in Visual Studio, I got an error that a project import could not be found: c:\microsoft.csharp.targets and therefore the project couldn't open. I had a look around the net and even on the page I downloaded it from but to no avail.
Firstly I found out that I had installed the .net framework 3.0 redistributable rather than the framework for building apps. This hadn't caused problems before but anyway, I found and installed .Net framework 3.5 and now found two copies of the file that was being looked for in c:\windows\microsoft.net\framework\v2.0.50727 and now in the v3.5 directory. Since the project I was looking at was based on v3 stuff I decided that the project should find the version 3 one.
I opened the project file into wordpad and found that the import looked like this: and when I checked, there was no environment variable set up for MSBuildToolsPath, even for version 2 of the .net framework so I went into Control Panel - System - Advanced - Environment Variables and added a new key: MSBuildToolsPath = c:\windows\microsoft.net\framework\v3.5 and then OK'd out of the dialog.
I restarted Visual Studio to force it reload the environment variables and then opened the project and hey presto. Quality.

Tuesday, 17 June 2008

Tables and Repeater Controls

Still knocking around with an asp.net 2.0 web site. Surprise, surprise it is possible to make it xhtml 1.0 strict compliant. I found one issue which was due to the way that a previous coder had implemented a table with an asp:repeater control. He had effectively written
with relevant code in between. The problem arises when the repeater has no data and the page therefore renders:
which is illegal in xhtml and during validation it complains: "end tag for "table" which is not finished".
The solution is easy enough. Use the and tags inside the repeater control to start and close the table. That way, if the repeater is empty, none of the table is rendered and no xhtml errors produced. The header template code is rendered once when there is one or more items in the repeater and likewise the footer is rendered once but after the items. As well as displayable items that you might want to display (header and total rows) you can use them to output functional markup like xhtml tags.
Always easy when you know how!

Tuesday, 10 June 2008

Changing Vb.net to C#.net

I thought I would share a few experiences changing vb.net 'code-behind' files into c# using Visual Studio 2005 for an asp.net web site.
1) Make sure you are not changing files in the wrong place - it won't work!!
2) You can leave the solution open when you do so: Go into windows explorer and rename the code file from something.aspx.vb to something.aspx.cs
3) Open the aspx html file for the file yu have changed in the solution and change the page directive to language="c#" and CodeFile="something.aspx.cs".
4) Important! save and close the aspx file before compiling. If you don't do this, the compiler will change things in the 'c#' file back to VB style - e.g. "namespace" will become "Namespace" and "try" will become "Try". It took me a while to find out.
5) Open the code file which is currently full of vb but now expecting c# and do some find and replaces to make the syntax correct. For example find and replace "If " with "if ( " and "End If" with "}" (Don't worry about indenting, you can do this later.
6) Hopefully you can work out most of the differences between C# and Vb but some of the funny ones are a) VB uses = instead of == in logical comparison. Fortunately c# will complain about this! b) If VB functions take no arguments, they are called with no brackets which in c# will cause an error which is slightly helpful as to the cause (i.e. you need ToString() instead of ToString) c) VB doesn't use brackets and semi-colons for scoping code so these will need adding.

Enjoy.

Tuesday, 3 June 2008

The pains of SQL Server 2005!

I quite like Sql Server and have used it at various jobs but I recently had a problem restoring a backup that failed with "There is insufficient free space on disk volume" and "restore failed for server" or such like and I had to start a long foogle (find out on google) to try and track down the cause. It is worth mentioning that the free space reported was much lower than was actually available.
It was confusing because I had successfully restored it previously but since the last restore I had installed Windows over the top of the old installation and re-installed SQL Server so I assumed it was a SQL Server setting that had been reset but couldn't find anything.
It turns out the message is quite common and like a lot of half-arsed programs provides a very unhelpful message that maybe sounds more reasonable once the real fault it found out but doe not really lead you there. I thought it was because SQL server might require contiguous space on the disk so I copied off the 35Gb backup which took hours and this didn't help! I also checked that the size of the restored database would fit onto the server since some people think the size of the backup is the same size it will take up when restored but unfortunately not: SQL Server pre-allocates the sizes of tables required even if they are empty. You can fix this on the database but you would then need to take another backup afterwards since you can't modify the backup in this way. Since I had already restored the backup previously, the number reported (35Gb) was about right and I had this space.
My problem turned out to be "quota management" which is turned on by default in windows server 2003 and was preventing me from using over 30Gb of disk space during the restore even though I was not putting the database into My Documents! Not sure why because the quote management looked more like I had 5Gb max but some random number was obtained and displayed to me in the error dialog.
I turned off the quota management (Right-click the disk in explorer and choose Properties->Quota) and everything was fine except now I had copied the backup away from the server I had to restore from the network which was painfully slow.
Not sure why it is such a difficult thing but you get a help link when you get the error and it goes to a microsoft site in which I have yet to see with any help at all, it usually says, "there is no additional help for this error". What a load of crap. These types of problems are massively common but rather than nailing down the help system and making it sweet and usable, we have 400,000 pages of google to try and filter down to our specific problem, oh and old links get broken which is also crap.