ORA-01008 Not All Variables Bound error

I have recently had the opportunity to work (once again) with Oracle. Specifically, I had to create a mechanism that would, based on configurable settings, update either a SQL Server or an Oracle database. In and of itself, this is not particularly challenging. Not since ADO.NET implemented a provider model using the Db... classes that are part of System.Data. The provider name can be used to generate the appropriate concrete instance of the DbConnection class and away you go.

While testing out this capability, I ran into this error when the target data source was Oracle. One would think (and I certainly did) was that I had missed out assigning one of the in-line parameters. The text associated with the error certainly gave that impression. And I was, after all, building the SQL statement on the fly. A bug in my logic could have placed a parameter into the SQL and not created a corresponding DbParameter.

But that was not the case.

Instead, it was that the value of one of my parameters (a string, as it turned out) was null. Not String.Empty, but null. And when you assign a null value to the parameter, it's as if you didn't bind anything to the parameter, the result being that when executing the query, a nice ORA-01008 exception is thrown. The correct way to do the assignment is to set the parameter value to System.DbNull value instead. It would appear that the SQL Server data provider doesn't have this issue, in that the problem only appeared against an Oracle data source. Not a particularly vexing problem, but still it's something to be aware of.

And a couple of years had passed since my last Oracle post ;)

The Cost of Migrating from VB6

Recently, a question regarding the cost associated with migrating from VB6 to VB.NET was asked by one of our clients. Since that is a question whose answer has a broader appeal than just to the asker, I thought I would replicate my response here.

It should be stated up front, however, that I don't normally recommend a migration from VB6 to .NET. This isn't to say that there aren't ways to benefit from the move. It's just that a straight migration typically won't see any of those benefits. Re-architecting an application is generally the only way to get those improvements and a straight migration doesn't accomplish this. And if you already have a working VB6 application, there is little to be gained by creating a VB.NET application that does exactly the same thing.

Keep in mind that I said "little", not "nothing". While the benefits are greater for redesigning, there are still times where a migration is the right choice. Which is the reason why the question of cost does need to be addressed.

There are a number of factors that come into play when trying to determine the cost of just migrating a VB6 application to VB.NET. Let me provide an admittedly incomplete list.

Code Migration

Naturally, the first thing to consider is moving the code from VB6 to .NET. Where there are migration wizards that are available, both from Microsoft and third parties, it is important to realize that there is no way to simply 'wizard' a migration. While the syntax between VB6 and VB.NET are similar, there are many concepts that are different. And no wizard will take a VB6 application and create a VB.NET application that is designed in the most effective manner for .NET. So while you can get an application to be compilable quite quickly, it won't be taking advantage of many of the features of .NET that can improve developer productivity. This is one of the reasons that many companies consider re-writing VB6 applications instead of just migrating them.

That having been said, it is certainly faster to migrate an application than it is to rewrite. An average developer can produce 500-1000 lines of tested and deployable code in a month. However, that same developer can migrate 35,000 to 40,000 lines of code a month. So to calculate raw cost per line of a migration, figure out how much you pay an average developer in a month and divide by 35,000.

Developer Training

Of course, migrating the code is only part of the associated cost. Developers have to be retrained to use VB.NET. A typical VB6 developer will take about six months to regain the productivity level in VB.NET that they had in VB6. A junior developer might take 8 months, while a senior developer will take around 4 months.

Part of the process of getting people up to speed will be training. Depending on the technologies that are being used, anywhere from 10-20 days of in-class training will be needed. A typical breakdown of the topics covered would be 3-8 days of .NET Framework training, 3-5 days of ASP.NET development, 1-2 days of testing. 4-5 days of advanced programming concepts (WCF, Sharepoint, WPF, etc).

While this might seem like an expensive process, there are hidden costs and lost productivity associated with trying to get developers up to speed 'on the cheap'. There is too much in .NET for a single class to provide all of the necessary information in sufficient depth to be able to use it effectively. The problem is that some organizations (and some developers) will pretend that a 5 day course on .NET is good enough. The developer will end up spending the next 12 months looking up how to do the common tasks that weren't covered in the 5 days and will end up making design decisions that, had they the correct knowledge at the time, would not be made. Both company and developer can spend years trying to correct the bad choices made through inexperience.

Preparing the VB6 Application

There are a number of common issues that arise during the migration process, issues that can't be addressed by a wizard. These issues generate the large majority of problems that are found when migrating and include such items as

  • The default property is no longer used
  • The property/method is not found in VB.NET
  • The property/method is found, but has a slightly different behavior
  • COM functionality has changed

If these issues are addressed before the migration (that is, in the VB6 application), it can help speed up the migration process. Each of these issues actually results in a cascade of problems (on the order of 5 VB.NET problems for each instance of a VB6 problem) in the migrated application, so it is worthwhile to spend some time 'fixing' the VB6 application in order to get it ready for migration.

While there are other considerations involved (is the new application to be web-enabled? is there integration with other legacy applications?, etc.), these items are the largest sources of cost associated with migrating from VB6 to .NET.

GetType on a Dynamically Loaded Assembly

The application on which I'm working has the opportunity to dynamically load and assembly. Later in the application, one of the types from the loaded assembly need be retrieved using GetType. More specifically, the GetType was performed using a fully qualified type name (including the name of the assembly). But the GetType failed. Naturally, this begged the question of why GetType didn't find the type in the dynamically loaded assembly.

Ultimately, it comes down to how GetType operations. If you provide a string of the form "A,C" (for assembly,class), then GetType operates as if it were performing Assembly.Load(A).GetType(C).

Notice the Load method that is in there. If A exists in the default probing path, then there is not problem. But A doesn't include a fully qualified path. And if the assembly was originally loaded from a location other then the executable's directory, GetType will fail.

There are a couple of solutions to the problem.

1 - Put the assembly in the GAC. Then Load will find the assembly and GetType will work

2 - Add a codebase element to the config file. This element can be used to control the probing path, but you cannot probe outside of the executable's directories and subdirectories. The codebase element (which has to be created for each DLL) allows you to specify the full path to the assembly.

An Old Problem is New Again

I ran into a problem yesterday that brought back memories. And not the good kind of memories either.

I'm doing some work using TypeConverters. In particular, I'm creating a Windows Forms control that functions in a manner similar to a PropertyGrid, but with a user interface that looks more like a typical Windows form. You know, with labels, and text boxes. Because this is a generic control (that is, the fields which get displayed depends on the object associated with the control), I'm doing a lot of reflection to retrieve the properties that are required. And I'm using reflection (the GetValue/SetValue) to interact with the object. This last part (the SetValue method in particular) means that I'm using type converters to move the string values that are in the text boxes into the actual properties themselves.

Now type converters are quite useful. They expose a ConvertFromString method that (not surprisingly) takes a string value and converts it to a value of the associated type. If the string is not valid for the destination type, an exception is thrown.

Problem 1: the raised exception is System.Exception. I have no idea why the exception surfaced from ConvertFromString is an Exception. The InnerException is a FormatException, which is the type of exception that I would have expected. But no, ConvertFromString throws Exception. This forces me to use a pattern that I loathe - try {...} catch (Exception) { }

Whenever I teach, I always harp on this. You should (almost) never catch the general Exception object. You should only catch specific Exceptions. But because of ConvertFromString, I have to add to my list of cases where catch (Exception) is required.

But why should this matter, you ask. TypeConverters exposes an IsValid method. You should be able to check for the validity of the string value prior to calling ConvertFromString. This would completely eliminate the need to catch any exceptions at all. And it's the best practice/recommended/ideal way to go.

Problem 2: Int32Converter.IsValid("bad") returns true.

Think about that last one for a second.

According to the type converter for Int32, the string 'bad' is a valid integer value. In my world, not so much. If you spelunk the various classes in .NET Framework, you find out that IsValid is supposed to be overridden in the various converter classes. But the numeric classes don't bother to do so. As a result, the IsValid that actually services my request just returns a true regardless of whether the string is valid or not.

What's worse, this is a known bug that will never be resolved. Apparently to make IsValid work for Int32Converter is considered a breaking change.

So my suggestion (courtesy of Dave Lloyd) is to add a new method to the TypeConverters. Call it IsReallyValid. IsReallyValid could then be implemented properly without the breaking change. It could take the CultureInfo object necessary to truly determine whether a string is valid. For the base type converter, it could simply return a true.

Heck, let's go a step further and mark IsValid as being Obsolete. That would force people to move to IsReallyValid and correct any problems in their applications. And maybe, in a couple of versions, Microsoft could reintroduce a working version of IsValid and mark IsReallyValid as being obsolete. In this way, four versions from now (that's about 10 developer years), IsValid will work the way it's supposed to (and everyone would expect it to).

Looking for Mr (or Ms) GoodDeveloper

Business has been booming of late at ObjectSharp. Don't know whether it's the weather or the business cycle, but our recent company barbeque had more new faces that I've seen in many years. And we haven't lost any of the old faces either.

And yet it doesn't seem to end. At the moment, we're looking to add some consultants to our team. Specifically, we have the need for someone with Windows Forms experience, either in creating commercial-grade user interfaces on their own or with the CAB application block.

If you (or someone you know) has that experience and is looking to join a fun team of top-notch developers, drop me your (or your friend's) resume. You'll get a chance to work on projects that use cutting edge technology. You'll learn about (and sometimes use) technologies that aren't yet available. And, if you have the interest, we have six MVPs on staff to help you get your own designation.

If you don't fit into this skill set, fret not. There will be others coming along in the very near future. Just keep your eye tuned to this blog. 

Relieving File in Use Problems

It's quite possible this is old news to people, but it took me more than a couple of minutes to find the solution. I was using the FromFile method on the Image class to create a new Image object. When I was done with the Image, I needed to move the file. But it turns out that the file was still 'being used by another process'.

The solution is simple, but it didn't come immediately to mind because I'm so used to basically ignoring the scope of variables. The trick is to call Dispose immediately rather than waiting for garbage collection to do its thing. Once Dispose has been invoked, you can manipulate the underlying image file as you need to.

Expression Web

I just sat through a session on Expression Web. They were showing some of the functionality that was currently being used, along with some of the plans for the next version. There are a couple of points that I'd like to make clear that came out of the session.

  • Expression Web fills the same space as Visual Studio. They are targeted to different audiences, naturally (Expression Web is aiming at designer-type people), but they are both development environments. They even work on the same project structure, although Expression Web is incapable of modifying the code behind files.
  • Expression Web does not currently have any support for Team Foundation Server. My first thought was 'ewwww'. But (and this is a killer but) you can only modify the Web pieces of the application. No code behind can be touched. While I'm not thrilled with not having integration, I'm more willing to relax it for the Web content.
  • They are currently thinking about whether to include TFS support in xWeb (the cutesy name for Expression Web) in the next version. If you want to influence their decision, send them a message through http://connect.microsoft.com.

I'm looking forward to playing with the elements of Expression Web to see what kind of trouble I can get into. :) If you want the same opportunity, you can download a trial version from http://www.microsoft.com/expression.

Identifying Formatting Errors with Expression Web

One of the more interesting aspects of Expression Web is its ability to help debug CSS errors. I'm in a talk on Designing with Expression Web and the story is about how to use Expression Web to identify problems in a web page. The web page is loaded and the problematic element is selected. One of the windows is a rules summary pane that includes all of the formatting rules that apply to the selected item, in the order in which they occur.

So the process of identifying the problem is finding the particular rule that actually causes the unexpected formatting. And having tried to suss this out on my own in the past, I can greatly appreciate the benefits of having such a tool available.

A Big Strike Against TableAdapters

I'm hoping that someone reads this post and corrects me. But I'm not holding out much hope.

I don't normally use TableAdapters, but for a small application I decided that they seem like a reasonable choice. And so long as I was using them on my development platform, all was well and good. The problem arose when I delivered the application.

Like any good developer, I store the connection string to the data store in the configuration file. Which is what I did for this application. When I defined the TableAdapters, I pointed the connection string property to the same setting. Or so I thought. But what actually happened was that the connection string was actually stored in the DLL that contained the adapters. Hard-coded. As in not modifiable through the config files. And, naturally, this wasn't discovered until deployment.

So I started to look for ways to have the TableAdapter pull the connection string from the config files. I figured that this would be a fairly common scenario, so it should be address. Not so much

Apparently there is no way to automatically have TableAdapters use config settings. The "solution" is to use the fact that TableAdapters are partial classes to create a write-only ConnectionString property. It looks like the following:

public partial class FormTableAdapter
{
   public string ConnectionString
      {
         set
         {
            System.Data.SqlClient.SqlConnection conn = new 
               
System.Data.SqlClient.SqlConnection();
            conn.ConnectionString = value;
            this.Connection = conn;
         }
      }
   }
}

Then, where the adapter is instantiated in your code, you set the ConnectionString property to the value from the config file.

To me, this is unacceptable. I was already a little skeptical of TableAdapters (I don't like the DataSet and the data access code in the same assembly), but this takes the cake. It almost seems like they were designed to not allow a reasonable deployment model. Maybe it will get better in the next version, but until then, I'm sticking with DataAdapters.

Toronto Code Camp Slide and Demo

The second annual Toronto Code Camp took place this past Saturday. By all accounts, it was a tremendous success with over 250 people giving their Saturday over to many things technical. I gave a presentation on extending the ObjectDataSource control, and while you should be able to get the slides and demo code from the Toronto Code Camp site shortly, I thought I'd post them here as well, just to get it out as quickly as possible.

Slide Deck - Extending the ObjectDataSource

Demo Code - Extending the ObjectDataSource Demo Code