There are layers and layers in .NET. A new release involves changes to all of these layers. First there's the CLR, which actually manages the details of executing .NET code. On top of that is the Framework Class Library (FCL), Microsoft's own set of useful classes (which are themselves .NET code). Visual Studio .NET and other development environments layer their own shortcuts and code generators on top of the FCL, and your application software sits at the top of the stack.
Some of the most interesting changes from .NET 1.0 to .NET 1.1 are in the FCL. Microsoft has catalogued over 100 breaking changes as a result of the new version. While many of these changes are in specialized areas that most .NET developers will never visit, there are some major changes that you should be aware of, lest they bite you when you upgrade.
The goal of the team that works on the FCL is to provide complete compatibility between versions of the Framework. You ought to be able to write an application using version 1.0 of the Framework and run it on version 1.1 with no problems and vice versa.
If you examine the .NET 1.0 and 1.1 FCLs, you'll find about 1100 API
changes: members, classes, and even entire namespaces added or
removed. The vast majority of these changes consist of adding new things
to the Framework. By definition, these are not breaking
changes. You can't reasonably expect an application that uses a new class
to run using the old FCL. For example, the 1.1 FCL contains the
System.Data.Odbc namespace, which isn't a part of the 1.0 FCL
(though it's available as an add-on). Any code that uses classes from this
namespace won't run on the base 1.0 FCL.
Breaking changes are more insidious. A change is considered breaking when code looks like it ought to run on the other version of the FCL, and is syntactically valid, but behaves differently. These behavioral changes are further subdivided into backward breaking changes (which affect a 1.0 application trying to run with the 1.1 Framework) and forward breaking changes (which affect a 1.1 application trying to run on the 1.0 Framework).
Comparatively few developers have yet moved on to the new versions (Visual Studio .NET 2003 and the .NET Framework 1.1). Consequently, most of us are more likely to hit backward breaking changes than forward breaking changes. Here are my selections for the backward breaking changes most likely to cause trouble for the average .NET developer.
1. Internet Security Changes Microsoft has waffled again on the correct security default for code downloaded from the Internet. In 1.0, the default security policy never gave code from the Internet permissions to run. In 1.1 this has been liberalized a bit: code from the Internet will now run within the constraints of the Internet permission set (which is pretty constrained but does allow, for example, use of disk files via the Isolated Storage mechanism). If you want to tighten up security back to 1.0 levels, you can use the Adjust Security Wizard to move the Internet zone back to No Trust.
2. Better handling of null defaults in DataSets. In 1.0 there are times when the DataSet will treat a default value of "" (the empty string) as a DBNull. In particular, if you serialize the DataSet using WriteXml() and then deserialize it with ReadXml(), the column default will magically change to DBNull. In 1.1, the empty string remains as an empty string. This is obviously the correct behavior, but if you wrote code in 1.0 to catch or depend on the incorrect behavior you'll need to revise it.
3. New exception from SqlDataReader. In the System.Data.SqlClient namespace, you construct a SqlDataReader with code similar to this:
Dim myCommand As New SqlCommand(mySelectQuery, myConnection) myConnection.Open() Dim myReader As SqlDataReader myReader = myCommand.ExecuteReader()
In 1.0 this code would succeed, even if the command was chosen as the deadlock victim by SQL Server (in the case where there's a locking issue with another connection, of course). You wouldn't get a SqlException back until you actually tried to read data from the SqlDataReader. In 1.1 the ExecuteReader() method can now throw a SqlException. If you've got error handling that depends on only seeing this particular exception when you're actually reading data, you'll need to revise the code to move up.
4. HttpWebRequest.MaximumResponseHeadersLength property. This property is new in version 1.1 of the FCL. As you might guess, it sets the maximum size of the headers that will be processed with the HttpWebRequest object. Any response with more than the allowed length will raise an exception. By default, this is set to 64K. In the 1.0 FCL there was no limit. This is the sort of change that I used to think would never bring any grief to anyone. But these days I know that someone, somewhere, is using HTTP headers to pass truly enormous blocks of information around. If that someone is you, be sure you set this property in your code to avoid maddening failures.
5. Dialogs that don't hang. I'm not sure that I can see how anyone would depend on the 1.0 behavior here, but the potential issue is so common that it's worth mentioning anyhow. .NET divides threads into UI interactive (those running on a desktop with a user watching) and non-UI interactive. In the 1.0 Framework, if you call Form.ShowDialog() or CommonDialog.ShowDialog() from a non-UI interactive thread, you'll hang the thread. In 1.1, the method will throw an InvalidOperationException instead.
6. Environment.UserInteractive now works. Related to the previous item, the FCL supplies the Environment.UserInteractive property, which is supposed to tell you whether the current thread is UI interactive. The problem is that in 1.0 this property returns true whether the thread is UI interactive or not. In 1.1 this is fixed, and now returns false if you call it from, for example, a Windows service or XML Web service. If you're using the ShowDialog() method from arbitrary threads, it now makes sense to use this property to avoid throwing an exception when the thread isn't UI interactive.
7. Form Load fixes. There are a pair of changes to the Load event of Windows forms that are worth knowing about. In 1.0 if you call Close() from within the Load event of a modal form, nothing happens. In 1.1 the form closes, which is what you'd expect. The second is a fix to a rather more insidious bug: in 1.0 if a form has an ActiveX control (remember ActiveX controls?), the Load event doesn't fire. But in 1.1 it does.
8. Change to WSDL mapping. The Web Services Description Language Tool (wsdl.exe) is used to create proxy classes for calling web services. In 1.0, this tool uses System.String as the native data type for schema elements with a WSDL data type of xsd:anyType. In 1.1, the tool uses System.Object for this purpose. If you're using the tool to generate proxies on the fly, or refreshing a Web Reference, you might find that the proxy class changes from what you expected even though the web service hasn't changed.
To round out the list, here are a few changes that can make it tough for 1.1 applications to run on the 1.0 Framework.
9. Improvements to SQL Execution. The 1.0 version of the SqlCommand object isn't friendly to batched statements. For example, you might want to execute this set of statements as a single operation:
SET STATISTICS PROFILE ON SELECT * FROM MyTable SET STATISTICS PROFILE OFF
In 1.0 an attempt to execute this through a SqlCommand (to return a second result set with query execution statistics) will always fail. In 1.1 such a batch query will succeed, which saves you from needing to build a stored procedure to execute it. Of course, once you depend on this feature, your 1.1 code will no longer work on the 1.0 FCL.
10. TextBox.MaxLength changes. The MaxLength property on a Windows Forms TextBox control sets the maximum length of text that can be set via the keyboard or the mouse. Sometimes, though, it's useful to limit keyboard input and still be able to set the text to longer values programmatically. In the 1.1 FCL you can use the AppendText() method and the SelectedText property to work with a strong longer than the specified MaxLength. In 1.0 this won't work.
It's important to bear in mind that these breaking changes can only break your applications if you are running with the "wrong" version of the FCL (that is, the one that you didn't build the application with). By default, though, 1.1 applications will try to run with the 1.0 FCL, and vice versa, if the correct version can't be found.
There's a two-pronged strategy you can adopt to deal with this. First, if you're not positive that your application will run with the other FCL, simply install the version that you expect. The two versions are designed to run side-by-side. They can both be installed on the same computer without conflicts. This is the best way to handle potential incompatibilities, especially if you're not sure that your testing has been exhaustive. Second, you can use XML binding policy files to specify exactly which versions of which assemblies your application needs to run. This gives you a way to precisely specify the behavior that your application should have with the other FCL.
And finally, don't neglect the underlying lesson here: version compatibility is never perfect. You'll always need to deal with issues such as these when some component further down the software stack than your application gets upgraded. The best developers are those with a proactive strategy for spotting such issues. If you don't already have a solid set of unit tests and a good bug-tracking system for your application, put them in place before the next round of breaking changes.
Mike Gunderloy is the lead developer for Larkware and author of numerous books and articles on programming topics.
Return to ONDotnet.com
Copyright © 2009 O'Reilly Media, Inc.