Monday, December 19, 2005

SQL Reporting Services: Alternating Item Color

To my supprise Reporting services does not seem to have support for an alternating item color when displaying a table (well that I can find at least).

After some playing I came up with a simple little iif statement.
Simply set the rows Background color in the properties pane to this:
=iif(RowNumber(Nothing) Mod 2, "LightGrey", "White")

Wednesday, December 14, 2005

Drive Type

I am currently working on a project that imports data from a SD card and I wanted an easy way to find the drive letter that windows creates for the device so that it was defaulted for the user. After a bit of searching I found that I could use WMI to do this and it is quite easy. Here is my implementation:



Public Enum DiskDriveType
Unkown = 0
NotRootDirectory = 1
RemovableDisk = 2
LocalDisk = 3
NetworkDrive = 4
CompactDisk = 5
RamDisk = 6
End Enum

Public Function DriveType(ByVal driveLetter As String) As DiskDriveType
Dim sms As New System.Management.ManagementObject(String.Format("Win32_logicaldisk='{0}:'", DriveLetter))
sms.Get()
Select Case Convert.ToInt32(sms.Properties("DriveType").Value)
Case 0 : Return DiskDriveType.Unkown
Case 1 : Return DiskDriveType.NotRootDirectory
Case 2 : Return DiskDriveType.RemovableDisk
Case 3 : Return DiskDriveType.LocalDisk
Case 4 : Return DiskDriveType.NetworkDrive
Case 5 : Return DiskDriveType.CompactDisk
Case 6 : Return DiskDriveType.RamDisk
Case Else : Return DiskDriveType.Unkown
End Select
End Function



One thing to be warry of is that floppy drives are also considered removable devices so I put a hardcode in to skip the default drive letter for a floppy ("A:\"). I am thinking of improving this in the future to check the capacity of the device to avoid this issue (but then i might get confused between SD cards and external hard drives)

Usage:

For Each driveLetter As String In System.IO.Directory.GetLogicalDrives()
If driveLetter.Substring(0, 1) <> "A" And DriveType(driveLetter.Substring(0, 1)) = DiskDriveType.RemovableDisk Then
txtSDDrive.Text = driveLetter
End If
Next

Friday, December 09, 2005

Checking if you are connected to the internet

I have not tested this but it looks legit:

[DllImport("wininet.dll")]private extern static bool InternetGetConnectedState (out int connectionDescription, int reservedValue ) ;

public bool IsConnected() {
int connectionDescription = 0;
return InternetGetConnectedState(out connectionDescription, 0);
}

Refactoring and comments

If you have not read Martin Fowlers book Refactoring then you should. There is no book that I have read that has helped me more in what I do everyday. The book basically gives techniques on how to make code better and easier to work with after it has been written. One of the most used and common refactorings is "extract method". This is where you take a chunk of code and pull it into its own method. (eg. http://www.refactoring.com/catalog/extractMethod.html).

I so love the idea that a method does one task and that is it. If it does two tasks then the method calls two methods (one for each task).

Now that I have been using this technique for a while I have found that you don't need comments anymore. The reason for this is that when you break everything into small and well named function that anyone can read the code without comments.


if I gave a method like this:
function LoadTransactionType() as boolean
sub SelectFirstRow()
sub AddTransaction()
sub DeleteTransaction()

It is pretty easy to see what these methods do and the code in those methods only has 5 - 20 lines of code which then might call another simply named method.

I have now noticed that whenever I open code I look for comments. Comments usually indicate that the code is doing something not obvious so I extract the method and delete the comment.

I still feel weird that most of my code now has little to no comments in it but it is still increddibly readable and easy to follow.

Tuesday, December 06, 2005

SQL Reporting Services: Deploying Reports To Production

I can not beleive how hard it is to deploy reports to production! Its easy for development. Just tell visual studio where the server is and bang! its done. I spent most of today trying to figure out how to deploy to production without having to manually upload each report through the web interface and then manually linking them to my shared data source.

I tried writing a vb script to do it which I got 90% their but could not link to a shared datasource. Microsofts documentation of the scripting commands you can do ARE GARBAGE! when trying to set a property you need to send a name value pair (which is fine) but there is no docs I could find that say what the strings are that I can pass to it. But I digress.

In the end I found this tool:
http://www.sqldbatips.com/showarticle.asp?ID=62

This person wrote a tool that connects to my development RS and generates a folder of scripts along with a batch file to run them all. I opened the batch file, changed the server to production and a couple of minutes later I was .....happy? yes. that is the word.

For those of you that want to deploy reports with a shared datasource via a manual script (using the rs.exe program to execute it) here is a snip of one of the scripts:


Public Sub Main()
Dim name As String = "Allocation Report"
Dim parent As String = "/MyProgram.ReportStaging"
Dim location As String = "c:\tempreports\Allocation Report.rdl"
Dim overwrite As Boolean = True
Dim reportContents As Byte() = Nothing
Dim warnings As Warning() = Nothing
Dim fullpath As String = parent + "/" + name

'Common CatalogItem properties
Dim descprop As New [Property]
descprop.Name = "Description"
descprop.Value = ""
Dim hiddenprop As New [Property]
hiddenprop.Name = "Hidden"
hiddenprop.Value = "False"

Dim props(1) As [Property]
props(0) = descprop
props(1) = hiddenprop

'Read RDL definition from disk
Try
Dim stream As FileStream = File.OpenRead(location)
reportContents = New [Byte](stream.Length) {}
stream.Read(reportContents, 0, CInt(stream.Length))
stream.Close()

warnings = RS.CreateReport(name, parent, overwrite, reportContents, props)

If Not (warnings Is Nothing) Then
Dim warning As Warning
For Each warning In warnings
Console.WriteLine(Warning.Message)
Next warning
Else
Console.WriteLine("Report: {0} published successfully with no warnings", name)
End If

'Set report DataSource references
Dim dataSources(0) As DataSource

Dim dsr0 As New DataSourceReference
dsr0.Reference = "/MyProgram.ReportStaging/dsMyProgram"
Dim ds0 As New DataSource
ds0.Item = CType(dsr0, DataSourceDefinitionOrReference)
ds0.Name="dsMyProgram"
dataSources(0) = ds0


RS.SetReportDataSources(fullpath, dataSources)

Console.Writeline("Report DataSources set successfully")



Catch e As IOException
Console.WriteLine(e.Message)
Catch e As SoapException
Console.WriteLine("Error : " + e.Detail.Item("ErrorCode").InnerText + " (" + e.Detail.Item("Message").InnerText + ")")
End Try
End Sub

Monday, December 05, 2005

LostFocus = Crap?

I discovered something interesting with the LostFocus method. Leaving a feild by clicking out of it does not always fire the LostFocus event. The reason for this (from my understanding) is that if the control runs through validation the CancelEventArgs object is set to true. This causes all events after it (in this case LostFocus) to not be fired.

In order to work around this use the Leave, Validating, or Validated events.

Thursday, December 01, 2005

.NET: String Comparisons

typically most developers compare strings in a case instensitive way like this:

if var.toLower = "expected" then
...
end if

this way is a little inneficient and does not take into account cultural differnces (some languages use groups of letters to signify one letter, some letters follow uppercase / lowercase rules differently).

for comparisons I use this everywhere now:

If String.Compare(var, "expected", True) = 0 Then
...
end if

I wish it would return a boolean though.
it returns less than 0 is var is less than expected
0 if they are equal
greater than 0 if expected is greater than var

Debug and release config files

One of my biggest issues was having certain lines in my config file for working in my developement shop and others for production. Whenever a bug was found or the application was released you would have to swap configs around. After a few years of reading I happened accross this method:

AppDomain.CurrentDomain.SetData()

The first paramaeter is the name of the domain property we want to change, and the second is the value.

I have this line in my application as the first line of code to be called


#If DEBUG Then
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", "c:\debug.config")
#End If


you can also retreive the data using this:
appDomain.CurrentDomain.GetData("APP_CONFIG_FILE")

for a list of items you can use with get/set data look here:
ms-help://MS.VSCC.2003/MS.MSDNQTR.2004OCT.1033/cpref/html/frlrfsystemappdomainclassgetdatatopic.htm

now that I write this posting it looks like the same can be done in a more managed way by using
appDomain.CurrentDomain.SetupInformation.ConfigurationFile = "c:\debug.config"