Skip to content

Covariant and Contravariant Subtyping In the .NET 2.0 CLR

Danny Thorpe used to say that he was very much interested in things that the .NET CLR did which C# did not support. Examples include unmanaged Win32 exports and exception filters. One thing which had previously escaped my notice is covariant and contravariant subtyping of generic types, which was apparently introduced along with generics in the 2.0 CLR. C# does not presently have a syntax for this, although the team is talking about it. Of course, some would question whether adding such support is such a good idea. Steve Yegge proposes that dynamic languages are a better solution to the problem. Since I, when thinking about covariance and contravariance, have a hard time remembering which is which, and since I suspect that many programmers don’t understand them at all, I can sympathize with his position.

On an unrelated point, I enjoyed the Steve Yegge blog post that I referenced earlier. There’s a lot of good stuff referenced there, and not just for users of dynamic languages. He cites work on, for example, how to make virtual method calls (nearly) as fast as static method calls, it least in places where it counts. From a paper published in 1994.

Embarcadero

The Embarcadero deal seems, from my outsider’s point of view, to be a great deal for CodeGear and for Embarcadero. There are so many different ways that the two companies’ products can work together that it just makes a great deal of sense to me. It’s less clear to me how it makes sense for Borland, but that’s their problem. I’m curious about one thing, though. Embarcadero is, at present, a database tools vendor. With their purchase of CodeGear, they are acquiring two fine databases: InterBase and Blackfish SQL. Thus far, they’ve said very little about their intentions for these products, at least relative to what they’ve said about other CodeGear tools. Again, it seems like a great fit for Embarcadero’s product line; there’s lots of ways this could make sense. But I’d be interested to know what their plans are.

If You Use OnUpdate, Your Code Must Be Perfect

I’ve never particularly liked the idea of OnUpdate events. They have always seemed kind of inelegant to me, but I haven’t talked about it much since I don’t really have a compelling argument against them, save for "bad smells." Today, though, I was using RAD Studio and saw an access violation error in the IDE. This was immediately followed by the following error, which repeated over and over again. The stack dump tells the story:

[20A635EB]{coreide100.bpl} DocModul.AnyModuleModified (Line 3466, "DocModul.pas" + 10) + $41
[20A635E5]{coreide100.bpl} DocModul.AnyModuleModified (Line 3466, "DocModul.pas" + 10) + $3B
[00419D8B]{bds.exe} AppMain.TAppBuilder.FileSaveAllActionUpdate (Line 4067, "ui\AppMain.pas" + 1) + $0
[200401A7]{rtl100.bpl } Classes.TBasicAction.SetOnExecute (Line 11109, "common\Classes.pas" + 9) + $9
[201513B1]{vcl100.bpl } Forms..TScrollBox + $231
[2004009D]{rtl100.bpl } Classes.TBasicAction.Destroy (Line 11048, "common\Classes.pas" + 2) + $5
[2013D8C7]{vcl100.bpl } Controls.TControl.ScaleConstraints (Line 4033, "Controls.pas" + 12) + $7
[2015E854]{vcl100.bpl } Forms.DoPosition (Line 6778, "Forms.pas" + 44) + $2
[2015E86E]{vcl100.bpl } Forms.DoPosition (Line 6778, "Forms.pas" + 44) + $1C
[2015E8DC]{vcl100.bpl } Forms.DoAlign (Line 6795, "Forms.pas" + 0) + $4
[201631AA]{vcl100.bpl } Clipbrd.TClipboard.AssignPicture (Line 386, "Clipbrd.pas" + 7) + $14
[201632F9]{vcl100.bpl } Clipbrd.TClipboard.SetAsHandle (Line 430, "Clipbrd.pas" + 0) + $9
[20162637]{vcl100.bpl } Forms.TCustomFormHelper.WriteGlassFrameBottom (Line 8945, "Forms.pas" + 2) + $0
[2016291F]{vcl100.bpl } Forms.TApplicationHelper.GetEnumAllWindowsOnActivateHint (Line 9087, "Forms.pas" + 0) + $3
[0042297A]{bds.exe } bds.bds (Line 195, "" + 7) + $7

Note the line in bold. Because this is an OnUpdate event, it’s going to repeat forever. The user is trapped. There is no chance to save your work, or to exit the IDE normally. You have to end task. Sure, this is a secondary error; it’s almost certainly the consequence of the preceding access violation, rather than a defect in the OnUpdate event itself. But because that event is not robust enough to handle the consequences of the previous error, the user is trapped.

My conclusion is that OnUpdate event handlers are dangerous if they do not detect anything that could possibly go wrong with the references they use, and exit gracefully in that case.

New Delphi Blog

MelanderBlog is the home of Anders Melander, who, among other things, developed the TGIFImage component included with Delphi 2007. This site includes a number of useful Delphi components.

Stupid PageControl Tricks

Most of the user interface for our Delphi applications is generated dynamically, at runtime. When we display this dynamically-generated user interface in a page control, we don’t generate the user interface for each page until you first go to it. This is for performance reasons, as there is some overhead to this, mostly due to database access.

When we enabled XP themes for our applications, I started noticing a flashing on the screen the first time you went to each tab in the page control. A little testing demonstrated that the gradient background of the tab was being rendered before the controls were created.

This turned out to be a fairly difficult problem to solve. When the user changes tabs, I need to figure out to which tab they are changing, and create the controls for that tab if they are not already created. In the TPageControl.OnChanging event, I don’t know to which tab the user is changing. Unfortunately, both the TPageControl.OnChange and the TTabSheet.OnShow event occur after the background of the tab has already been painted. I needed a way to render the controls, if necessary, in between these events, but after the tab to which the user is changing is already known.

After some rooting around in the VCL source code, I came up with the following solution.

type
  TChangeToPageEvent = procedure (Sender: TObject; const APageIndex: Integer) of object;
  TChangeToEventPageControl = class(TPageControl)
  private
    FOnChangeToPage: TChangeToPageEvent;
    procedure DoOnChangeToPage(const APageIndex: Integer);
  protected
    procedure Change; override;
  public
    property OnChangeToPage: TChangeToPageEvent read FOnChangeToPage write FOnChangeToPage;
  end;

{ TChangeToEventPageControl }

procedure TChangeToEventPageControl.Change;
begin
  DoOnChangeToPage(TabIndex);
  inherited;
end;

procedure TChangeToEventPageControl.DoOnChangeToPage(const APageIndex: Integer);
begin
  if Assigned(FOnChangeToPage) then begin
    FOnChangeToPage(Self, APageIndex);
  end;
end;

Since I also create the page control itself dynamically, I don’t even have to install this on the component palette to make it work. Handling the OnChangeToPage event allows me to create the controls when the page to which the user is changing is known, but before the tab itself renders.

ASP.NET MVC Membership

One of my disappointments with Ruby on Rails is that it provides no support whatsoever for site logins/membership, which I consider to be a fundamental part of many database-driven websites. Of course, the Rails community has responded — and responded, and responded, and responded — to this need. The Rails wiki notes that there are about a "gazillion" different systems for solving this problem in Rails. I’ve tried acts_as_authenticated, which does work, but, at least at the time I tried it, required quite a bit of manual tweaking and patching to get it going.

ASP.NET 2.0 and higher, on the other hand, includes a standard membership framework which is sufficiently extensible that I’ve never seen an application which it couldn’t be adapted to fit, and, for most applications, it will work right out of the box.

The usual way to configure page access rights for the ASP.NET membership (in web.config), however, isn’t really applicable to the new MVC framework. Scott Guthrie had promised to cover this subject, but it was Rob Conery who finally delivered the goods. In short, you decorate your controller actions with attributes specifying whether the current user must be logged in, or be within a certain role, and a controller filter handles the work of validating those security assertions at runtime. Troy Goode built upon Rob’s original code, producing the ASP.Net MVC Membership Starter Kit.

The Starter Kit includes a lot of really valuable code, but very little documentation, so I’m going to write out instructions for "getting started with the Starter Kit." I’m presuming that you’re going to use the SQL membership provider in this example, although the membership framework does include support for OpenID and Windows Live authentication as well. You can, of course, use any authentication framework you care to.

  1. Download the full installer from the Releases tab at CodePlex. I’d also strongly recommend getting the source code download, which is available on the same page.
  2. Run the installer.
  3. If you’re using Visual Studio, you want to install the templates into VS. There’s an item on your Start menu (in the "Starter Kits") folder which will do that.
  4. If you are creating a brand-new site, you can start by just copying the sample project included with the download. Skip ahead to step 10.
  5. If you are adding membership support to an existing project, there’s a few things you need to do. First, you need to reference the assembly you’ve just installed. One way to do this is to simply reference the StarterKits.Mvc assembly in your project. But I chose to add the source code for the StarterKits.Mvc project to my solution instead, which helps with debugging.
  6. If you haven’t done so already, edit your web.config to include Forms authentication and set the Login URL, and specify a ConnectionString if you’re not using the default. I’ll include an example of this below. Note that you need to include a membership provider and a role provider. Also, if you’re not using the default database, run aspnet_regsql.exe to add the required metadata to your database.
  7. You are not required to use the controllers or views supplied with the Starter Kit. The filters in the Starter Kit will work fine with any controller you care to write. However, it’s probably easiest, especially at the beginning, to use the supplied code. If you’re using Visual Studio, you can do this with the templates you installed in step 3. Right click the Controllers folder in Solution Explorer, choose Add New Item, then choose MvcMembership Controllers FormsAuthenticationController. Do the same for FormsAuthenticationAdministrationController. Right click the Views folder, and add both View templates.
  8. The current release of the Starter Kit has a bug where the codebehind and codegen files are not added along with the aspx files to the FormsAuthenticationAdministration views. This will be fixed in the next release (see Troy Goode’s comment below for details), but if you encounter this bug, I’ve included a workaround in my defect report.
  9. You need to add a couple of lines to Global.asax in order to register the routes for the new controllers. You can copy those from the sample project.
  10. Your site should now work, but there are a few things you need to do in order to configure e-mail and the like. These are indicated with TODO comments. You can find these in the Task List.

As promised, here is what you need to add to web.config to use a custom database. I’ve omitted the connection string definition, which is specified like any other connection string.

<authentication mode="Forms">
  <forms loginUrl="/Login"/>
</authentication>
<roleManager enabled="true">
  <providers>
    <clear/>
    <add name="AspNetSqlRoleProvider" connectionStringName="MyConnectionString"
      applicationName="MyApp" type="System.Web.Security.SqlRoleProvider, System.Web,
      Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    </providers>
</roleManager>
<membership defaultProvider="MySqlMembershipProvider">
  <providers>
    <add name="MySqlMembershipProvider"
      type="System.Web.Security.SqlMembershipProvider"
      connectionStringName="MyConnectionString"
      enablePasswordRetrieval="false"
      enablePasswordReset="true"
      requiresQuestionAndAnswer="true"
      applicationName="MyApp"
      requiresUniqueEmail="true"
      passwordFormat="Hashed"
      maxInvalidPasswordAttempts="5"
      minRequiredPasswordLength="7"
      minRequiredNonalphanumericCharacters="1"
      passwordAttemptWindow="10"
      passwordStrengthRegularExpression=""/>
    </providers>
</membership>

Literate Programming

Andrew Binstock’s recent interview with Donald Knuth has received a lot of attention for his comments about unit testing, multicore, and other things of which Knuth is skeptical, but I was more interested in his comments on the topics which he endorses. Knuth is the originator of (amongst many other things) literate programming. One way to think about literate programming is that it turns the notion of commenting on its head. In a traditional program, you use relatively few comments, and partition the comments with the limiters. Literate programs are mostly English text, with compilable code delimited.

Knuth seems uncertain as to why literate programming hasn’t really taken off:

Jon Bentley probably hit the nail on the head when he once was asked why literate programming hasn’t taken the whole world by storm. He observed that a small percentage of the world’s population is good at programming, and a small percentage is good at writing; apparently I am asking everybody to be in both subsets.

Well, sure, but even if one assumes that 1/10 of working programmers are decent at their job, and 1/10 of those can express themselves in English and a reasonably clear manner, that’s not really enough to account for the low use of literate code. There are number of good bloggers and technical publication authors, and literate coding would seem to be well suited for these tasks. Maybe we just need some better examples to get the ball rolling.

You don’t have to read it many books on software development before you’ll encounter one with source code examples which produce erroneous results, or even fail to compile altogether. It’s an absurdly common error. Taken to an extreme, it can be maddening. In the past month or so, I’ve been reading the preprints at Bruce Eckel and Jamie King’s new book on LINQ. One interesting thing about this book is that in the process of writing it, the authors have developed a build system which builds all of the source code examples, including verifying the in-line assertions, and even goes so far as to ensure that the examples which are not supposed to build at all really do fail to compile. The finished book may not be flawless, but the examples are going to work.

I think it’s a perfect application of literate programming; I cannot think of a better solution to this problem.

Two Updated Delphi Books

Bob Swart has updated two of his books: Delphi for Win32 VCL Database Development and Delphi 2007 for Win32 Development Essentials.

SQL-to-Code Macro

OK, I’m not trying to steal Joe White’s thunder here on Delphi macros, but when I plugged Joe’s series, I mentioned that I use Delphi macros for converting SQL created in a query analysis tool into Delphi constants, and commenter Jack asked for more details on this. I thought it would make a better post than comment.

The general idea is that we want to start with an SQL statement, like this:

SELECT
  *
FROM
  MY_TABLE;

…and turn it into a Delphi constant, like this:

const
  MySQL = ‘SELECT ‘
        + ‘  * ‘
        + ‘FROM ‘
        + ‘  MY_TABLE;’;

The first step is to paste the SQL into the code at the appropriate place. We need to type the non-repeating portion of the constant, and then paste in the SQL:

const
  MySQL = ‘SELECT
  *
FROM
  MY_TABLE;

Now, record the macro:

  1. Press the End key to go to the end of the line
  2. Press Ctrl+Shift+R to start recording
  3. Type a space, then the closing single quote
  4. Press the down arrow, then the Home key to go to the beginning of the next line
  5. Type the appropriate whitespace, then a ‘+’ sign (for string concatenation), then the open single quote character
  6. Press the End key to go to the end of line, to be ready for the next time the macro is played back
  7. Press Ctrl+Shift+R to stop recording

To use the macro, press and hold Ctrl+Shift+P. The keyboard auto-repeat who repeatedly play back the macro, formatting the rest of the query. Don’t worry about going too far, since you can always press Ctrl+Z to undo if you do. Since the final closing single quote and semicolon are different from how the rest of the lines are formatted, just type them.

By the way, the Delphi editor works really well for editing plain SQL. If you are editing a file with the ‘. SQL’ extension, it does syntax highlighting (and printing) for SQL. You can also use macros like the one described above when creating your SQL or DDL. For example, I often use macros to turn an INSERT statement into a UPDATE statement with parameters or trigger variable references, and similarly to turn a SELECT into a CREATE VIEW statement.

ADO.NET 2.0 Driver for InterBase

An ADO.NET 2.0 driver for InterBase is available for free download from the IB registered users page. Delphi users already have this; you only need it if you want to connect to IB from a .NET language other than Delphi and you don’t already own Delphi/RAD Studio.

Close