Using Delphi as a script language for ASP.NET

By: John Kaster

Abstract: A preview of Delphi for .NET support inside ASP.NET

At the VSLive! show in Manhattan, New York on June 17th and 18th, 2002, Borland was talking about our plans to support .NET, previewing the Delphi for .NET compiler, and demonstrating how all our IDE products support Web Services today. For more information and pictures from the show, see Anders' article on the VSLive NY show.

We were demonstrating support for several functional areas of .NET support: native applications, which is discussed in the Delphi for .NET compiler preview article; Web Services support (which will be a forthcoming article by Anders) and .ASP.NET support, which this article covers.

Note! The technical details and steps described in this article are all subject to change before Borland makes the Delphi for .NET preview publicly available with Delphi 7.

Recognizing Delphi as a script language

The first step in getting support for ASP.NET is making sure it recognizes Delphi as a scripting language, and knows how to invoke the Delphi for .NET compiler for the various ASP file types.

ASP.NET will look for a web.config file in the root of whatever virtual directories you set up for IIS. Here are the contents of this file for using Delphi as a scripting language with ASP.NET.


<configuration>
  <system.web>                            
    <compilation debug="true">
       <assemblies>
          <add assembly="DelphiProvider" />
       </assemblies>
       <compilers>
          <compiler language="Delphi" extension=".pas" 
            type="Borland.Delphi.DelphiCodeProvider,DelphiProvider" />   
       </compilers>
    </compilation>
  </system.web>
</configuration>

For more information on the web configuration file, see MSDN.

To test the ASP.NET support on my local machine (I'm using Windows 2000 Pro) I simply needed to add a virtual directory that pointed to my directory with Delphi for .NET's ASP.NET support. (In the preview, this will probably be the aspx directory underneath the Delphi for .NET preview installation directory.) I called this directory "vslive" for the VSLive! show in NY, so some of the forthcoming screen shots will show that directory.

With these minor changes to my "machine" configuration, we're ready to start writing ASP.NET pages with Delphi as the scripting language.

Using Delphi code with ASP.NET

Of course, one of the first things we should do is just write a simple application to make sure we've configured the Delphi support correctly. Here's the source the file editdemo.aspx. It's a simple form that provides an input field for text values, a button to click, and an action for the button to display the text typed into the input field.

<html>
    <script language="Delphi" runat="server">
    procedure ButtonClick(Sender: System.Object; E: EventArgs);
    begin
        Message.Text := Edit1.Text;
    end;
    </script>
    <body>
    <form runat="server">
        <asp:textbox id="Edit1" runat="server"/>
        <asp:button text="Click Me!" OnClick="ButtonClick" runat="server"/>
    </form>
    <p><b><asp:label id="Message" runat="server"/></b></p>
    </body>
</html>

You will notice that the Delphi script routine ButtonClick is referenced in the OnClick event for the ASP button object, and that the code for the routine refers to the label Message and text box Edit1. Even though those variable names don't appear in the Delphi code, it is still able to refer to them because the Delphi source code unit generated by the Delphi provider for ASP.NET will know about them automatically due to their references in the above script.

Now, we can test the page by opening it with our local browser. (Once you install the .NET run-time or SDK, you'll be able to run ASP pages with IIS on your local machine.) Since my virtual directory is "vslive" I open http://localhost/vslive/editdemo.aspx.

Initialized page
Click the image to view the compiled and initialized page, ready for text entry.

Text is entered
Some text has been entered in the text box, and the button was clicked.

Here is the Delphi code that was generated and compiled by ASP.NET to produce the WebForm.


//------------------------------------------------------------------------------
// <autogenerated>
//     This code was generated by a tool.
//     Runtime Version: 1.0.3705.209
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </autogenerated>
//------------------------------------------------------------------------------

unit ASP;

interface

uses System.Collections, System.Collections.Specialized, System.Configuration,
    System.Text, System.Text.RegularExpressions, System.Web, System.Web.Caching,
    System.Web.SessionState, System.Web.Security, System.Web.UI, System.Web.UI.WebControls,
    System.Web.UI.HtmlControls, System.Globalization;

var
  editdemo_aspx___autoHandlers: Integer;
  editdemo_aspx___intialized: Boolean = False;
  editdemo_aspx___fileDependencies: System.Collections.ArrayList;
type
  editdemo_aspx = class(System.Web.UI.Page, System.Web.SessionState.IRequiresSessionState)
  protected
    Edit1: System.Web.UI.WebControls.TextBox;
    __control3: System.Web.UI.WebControls.Button;
    __control2: System.Web.UI.HtmlControls.HtmlForm;
    Message: System.Web.UI.WebControls.Label;
    procedure ButtonClick(Sender: System.Object; E: EventArgs);
  public
    constructor Create;
    function get_AutoHandlers: Integer; override;
    function get_ApplicationInstance: System.Web.HttpApplication; virtual;
    function get_TemplateSourceDirectory: System.String; override;
    procedure set_AutoHandlers(Value: Integer); override;
  protected
    property AutoHandlers: Integer read get_AutoHandlers write set_AutoHandlers;
    property ApplicationInstance: System.Web.HttpApplication read get_ApplicationInstance;
  public
    property TemplateSourceDirectory: System.String read get_TemplateSourceDirectory;
  private
    function __BuildControlEdit1: System.Web.UI.Control;
    function __BuildControl__control3: System.Web.UI.Control;
    function __BuildControl__control2: System.Web.UI.Control;
    function __BuildControlMessage: System.Web.UI.Control;
    procedure __BuildControlTree(__ctrl: System.Web.UI.Control);
  protected
    procedure FrameworkInitialize; override;
  public
    function GetTypeHashCode: Integer; override;
  end;
  
implementation


    procedure editdemo_aspx.ButtonClick(Sender: System.Object; E: EventArgs);
    begin
        Message.Text := Edit1.Text;
    end;
    constructor editdemo_aspx.Create;
var
  dependencies: System.Collections.ArrayList;
begin
  inherited Create;
  
  if (ASP.editdemo_aspx___intialized = False) then
  begin
    dependencies := System.Collections.ArrayList.Create;
    dependencies.Add('d:\vslive\editdemo.aspx');
    ASP.editdemo_aspx___fileDependencies := dependencies;
    ASP.editdemo_aspx___intialized := True;
  end;
  Self.Server.ScriptTimeout := 30000000;
end;

function editdemo_aspx.get_AutoHandlers: Integer;
begin
  Result := ASP.editdemo_aspx___autoHandlers;
end;

function editdemo_aspx.get_ApplicationInstance: System.Web.HttpApplication;
begin
  Result := Self.Context.ApplicationInstance as System.Web.HttpApplication;
end;

function editdemo_aspx.get_TemplateSourceDirectory: System.String;
begin
  Result := '/vslive';
end;

procedure editdemo_aspx.set_AutoHandlers(Value: Integer);
begin
  ASP.editdemo_aspx___autoHandlers := Value;
end;

function editdemo_aspx.__BuildControlEdit1: System.Web.UI.Control;
var
  __ctrl: System.Web.UI.WebControls.TextBox;
begin
  
  __ctrl := System.Web.UI.WebControls.TextBox.Create;
  Self.Edit1 := __ctrl;
  __ctrl.ID := 'Edit1';
  __ctrl.Width := System.Web.UI.WebControls.Unit.Parse('300px', System.Globalization.CultureInfo.InvariantCulture);
  Result := __ctrl;
end;

function editdemo_aspx.__BuildControl__control3: System.Web.UI.Control;
var
  __ctrl: System.Web.UI.WebControls.Button;
begin
  
  __ctrl := System.Web.UI.WebControls.Button.Create;
  Self.__control3 := __ctrl;
  __ctrl.Text := 'Click Me!';
  __ctrl.add_Click(Self.ButtonClick);
  Result := __ctrl;
end;

function editdemo_aspx.__BuildControl__control2: System.Web.UI.Control;
var
  __parser: System.Web.UI.IParserAccessor;
  __ctrl: System.Web.UI.HtmlControls.HtmlForm;
begin
  
  __ctrl := System.Web.UI.HtmlControls.HtmlForm.Create;
  Self.__control2 := __ctrl;
  __parser := __ctrl as System.Web.UI.IParserAccessor;
  __parser.AddParsedSubObject(System.Web.UI.LiteralControl.Create(''#13#10'        '));
  Self.__BuildControlEdit1;
  __parser.AddParsedSubObject(Self.Edit1);
  __parser.AddParsedSubObject(System.Web.UI.LiteralControl.Create(''#13#10'        '));
  Self.__BuildControl__control3;
  __parser.AddParsedSubObject(Self.__control3);
  __parser.AddParsedSubObject(System.Web.UI.LiteralControl.Create(''#13#10'    '));
  Result := __ctrl;
end;

function editdemo_aspx.__BuildControlMessage: System.Web.UI.Control;
var
  __ctrl: System.Web.UI.WebControls.Label;
begin
  
  __ctrl := System.Web.UI.WebControls.Label.Create;
  Self.Message := __ctrl;
  __ctrl.ID := 'Message';
  Result := __ctrl;
end;

procedure editdemo_aspx.__BuildControlTree(__ctrl: System.Web.UI.Control);
var
  __parser: System.Web.UI.IParserAccessor;
begin
  __parser := __ctrl as System.Web.UI.IParserAccessor;
  __parser.AddParsedSubObject(System.Web.UI.LiteralControl.Create('<html>'#13#10'    '));
  __parser.AddParsedSubObject(System.Web.UI.LiteralControl.Create(''#13#10'    <body>'#13#10'    '));
  Self.__BuildControl__control2;
  __parser.AddParsedSubObject(Self.__control2);
  __parser.AddParsedSubObject(System.Web.UI.LiteralControl.Create(''#13#10'    <p><b>'));
  Self.__BuildControlMessage;
  __parser.AddParsedSubObject(Self.Message);
  __parser.AddParsedSubObject(System.Web.UI.LiteralControl.Create('</b></p>'#13#10'    </body>'#13#10'</html>'#13#10));
end;

procedure editdemo_aspx.FrameworkInitialize;
begin
  Self.__BuildControlTree(Self);
  Self.FileDependencies := ASP.editdemo_aspx___fileDependencies;
  Self.EnableViewStateMac := True;
end;

function editdemo_aspx.GetTypeHashCode: Integer;
begin
  Result := -764444463;
end;

end.

Note! This is still a preview of the Delphi for .NET compiler. The actual code that gets generated with the released product could vary significantly from the example I show here. I am only showing you this example to demonstrate the functionality of the compiler registration for ASP.NET.

I was able to view the generated source code by introducing a syntax error into the page source by changing the Edit1.Text reference in the ButtonClick event to Edit2.Text. I'm sure there is another way to do it, but I'm new to ASP.NET so I haven't found an easier way to see the generated source, other than navigating to the temporary page.

This is some of the debug information that automatically appeared in the browser when I introduced the syntax error. I have put it in a text box to delineate the section with generated output. You can see the error mentioned in the first highlighted text section.

Server Error in '/vslive' Application.

Compilation Error

Description: An error occurred during the compilation of a resource required to service this request. Please review the following specific error details and modify your source code appropriately.

Compiler Error Message: The compiler failed with error code 1.


Show Detailed Compiler Output:

c:winntsystem32> "c:winntmicrosoft.netframeworkv1.0.3705dccasp.exe" -ud:Convert.net
 "-xC:WINNTMicrosoft.NETFrameworkv1.0.3705Temporary ASP.NET Filesvslived1dc5720817c58e0owpz_hhz.dll"
  -D:DEBUG -v+ -$O-  
  -q "C:WINNTMicrosoft.NETFrameworkv1.0.3705Temporary ASP.NET Filesvslived1dc5720817c58e0owpz_hhz.0.pas"


Borland Delphi Version 16.0
Copyright (c) 1983,2002 Borland Software Corporation
Confidential pre-release version

C:WINNTMicrosoft.NETFrameworkv1.0.3705Temporary ASP.NET Filesvslived1dc5720817c58e0owpz_hhz.0.pas(60) Error: Undeclared identifier: 'Edit2'

Show Complete Compilation Source:

Line 1:    //------------------------------------------------------------------------------
Line 2:    // <autogenerated>
Line 3:    //     This code was generated by a tool.
Line 4:    //     Runtime Version: 1.0.3705.209
Line 5:    //
Line 6:    //     Changes to this file may cause incorrect behavior and will be lost if 
Line 7:    //     the code is regenerated.
Line 8:    // </autogenerated>
Line 9:    //------------------------------------------------------------------------------
Line 10:   
Line 11:   unit ASP;
Line 12:   
Line 13:   interface
Line 14:   
Line 15:   uses System.Collections, System.Collections.Specialized, System.Configuration, 
Line 16:       System.Text, System.Text.RegularExpressions, System.Web, System.Web.Caching, 
Line 17:       System.Web.SessionState, System.Web.Security, System.Web.UI, System.Web.UI.WebControls, 
Line 18:       System.Web.UI.HtmlControls, System.Globalization;
Line 19:   
Line 20:   var
Line 21:     editdemo_aspx___autoHandlers: Integer;
Line 22:     editdemo_aspx___intialized: Boolean = False;
Line 23:     editdemo_aspx___fileDependencies: System.Collections.ArrayList;

The text box is wide because the invocation line for the compiler is so long, even though I manually wrapped the output. I apologize in advance for people who may want to print this document.

More advanced pages

ASP.NET includes some very robust controls that go far beyond conventional HTML controls. One of them is the Calendar control. I've liberally "borrowed" code that Anders Hejlsberg (architect of .NET and C#) presented at BorCon 2002 to write the code for this page.

This code is contained in the file calendar.aspx. This code supports two ways of setting the date for the control: by navigating and clicking the calendar itself, or by typing the date with a wide variety of date format strings that are supported by the Convert class in the .NET framework.


<script language="Delphi" runat="server">
procedure Calendar1Selected(Sender: System.Object; E: EventArgs);
begin
  Label1.Text := 'Delphi for .NET says you picked ' + Calendar1.SelectedDate.ToString('D');
end;

procedure Button1Click(Sender: System.Object; E:EventArgs);
begin
  Calendar1.VisibleDate := System.Convert.ToDateTime(Edit1.Text);
  Label1.Text := 'Delphi for .NET says you set ' + Calendar1.VisibleDate.ToString('D');
end;
</script>

<body style="font:18pt Verdana">
  <form runat="server">
    <center>
       <h1>Delphi for .NET running in ASP.NET</h1>
       <p>Please pick a date</p>
       <asp:Calendar id="Calendar1" runat="server" ForeColor="#0000FF" BackColor="#FFFFCC" 
         OnSelectionChanged="Calendar1Selected">
         <TodayDayStyle Font-Bold="True"/>
         <NextPrevStyle ForeColor="#FFFFCC"/>
         <DayHeaderStyle BackColor="#FFCC66"/>
         <SelectedDayStyle ForeColor="Black" BackColor="#CCCCFF"/>
         <TitleStyle Font-Size="14pt" Font-Bold="True" ForeColor="#FFFFCC" BackColor="#990000"/>
         <OtherMonthDayStyle ForeColor="#CC9966"/>
       </asp:Calendar>
       <p><asp:TextBox id="Edit1" width=200 runat="server"/>
          <asp:Button text="Set date" id="Button1" OnClick="Button1Click" runat="server" />
       </p>
       <p><asp:Label id="Label1" runat="server"/></p>
    </center>
  </form>
</body>

Once we have the source for the page, we're ready to open it in the browser.

calendar page initialized
This is how the page looked when it first loaded. It defaults to the web server machine's date.

picked date
This is after choosing the date by navigating. Note that the text says "picked" instead of "set," which shows that the Calendar1Selected event fired.

set date
Setting the date type by typing it in and clicking the button.

Data access

Now that we have a calendar, the next step is of course using it to drive selections from a database. For this ASP.NET Delphi page, we'll add a DataGrid and a TextBox for specifying the list of fields to display in the Grid.


<%@Import Namespace="System.Data"%>
<%@Import Namespace="System.Data.SqlClient"%>

<script language="Delphi" runat="server">
const
  ProdName = 'Delphi for .NET';
  DispFields = 'OrderID, CustomerID, ShipName, ShipCity, ShipCountry';

procedure DateSelected(Sender: System.Object; E: EventArgs);
begin
  Label1.Text := ProdName + ' says you picked ' + Calendar1.SelectedDate.ToString('D');
  DataGrid1.DataSource := GetOrders(Calendar1.SelectedDate);
  DataGrid1.DataBind;
end;

procedure Button1Click(Sender: System.Object; E:EventArgs);
begin
  Calendar1.VisibleDate := System.Convert.ToDateTime(Edit1.Text);
  Label1.Text := ProdName + ' says you set ' + Calendar1.VisibleDate.ToString('D');
end;

procedure Button2Click(Sender: System.Object; E:EventArgs);
begin
  DisplayFields.Text := DispFields;
end;

function GetOrders(Date : DateTime) : DataSet;
var
  Adapter : SqlDataAdapter;
begin
  Adapter := SqlDataAdapter.Create(
    'select ' + DisplayFields.Text + ' from Orders '+
    'where OrderDate = ''' + date.ToString('d')+'''',
    'Server=(local);Database=Northwind;Trusted_Connection=yes');
  Result := DataSet.Create;
  Adapter.Fill(Result);
end;

</script>

<body style="font:18pt Verdana">
  <form runat="server">
     <h1><%=ProdName %> with a Calendar, DataGrid, & SqlClient in ASP.NET</h1>
    <table>
    <tr valign="top"><td>
    <p><b>Pick a date</b></p>
    <asp:Calendar id="Calendar1" runat="server" ForeColor="#0000FF" BackColor="#FFFFCC" 
      OnSelectionChanged="DateSelected">
      <TodayDayStyle Font-Bold="True"/>
      <NextPrevStyle ForeColor="#FFFFCC"/>
      <DayHeaderStyle BackColor="#FFCC66"/>
      <SelectedDayStyle ForeColor="Black" BackColor="#CCCCFF"/>
      <TitleStyle Font-Size="14pt" Font-Bold="True" ForeColor="#FFFFCC" BackColor="#990000"/>
      <OtherMonthDayStyle ForeColor="#CC9966"/>
    </asp:Calendar>
    <p><asp:TextBox id="Edit1" width=150 runat="server"/>
       <asp:Button text="Set date" id="Button1" OnClick="Button1Click" runat="server" />
    </p>
    </td><td valign="top">
    <p><b>Display fields:</b> <asp:TextBox id="DisplayFields" 
         text="OrderID, CustomerID, ShipName, ShipCity, ShipCountry" width=500 runat="server"/>
       <asp:Button text="Reset fields" id="Button2" OnClick="Button2Click" runat="server" /></p>
    <asp:DataGrid id="DataGrid1" runat="server" BorderColor="#FFCC66" ForeColor="#0000FF">
      <HeaderStyle ForeColor="#FFFFCC" BackColor="#990000"/>
    </asp:DataGrid>
    </td></tr></table>
    <p><asp:Label id="Label1" runat="server"/></p>
  </form>
</body>

The data connectivity is provided by a SQLClient connection talking to the MS SQL 2000 sample database Northwind. The SqlDataAdapter is used to populate the query results into the DataGrid, as you can see in the procedure GetOrders. This routine is called by the DateSelected event, which fires whenever the user clicks on a date in the calendar. It does not get called when a date is set manually using the "Set Date" button.

This page requires a little more code, but then again, it's doing a fair amount more. Let's see what it looks like.


Initial data-aware calendar page

This is what the page first looks like after it's been compiled. The default date is the current system date on the server.


April 1998

We'll use the "Set Date" button to move to April 1, 1998, which actually has some orders in it for the Northwinds database. Then, we'll "pick" April 15, 1998 so the GetOrders routine is called to populate the DataGrid.


Added "freight" to the field list

After we actually can see some orders, I've modified the field list to include the "Freight" field. You could see all fields in the Orders table by entering an asterisk (*) as the value for the display fields. Once you modify the field list, simply click on the date you want to see, and the grid will get refreshed.

That's about all we need to provide a calendar-driven data retrievel system in the browser using Delphi for .NET and ASP.NET.

Conclusion

This article is intended to be a quick demonstration of how easy it is to use Delphi for .NET in ASP.NET. Please keep in mind that this is a preview of emerging technology, and the final result may not resemble what has been presented here, although it probably will.



Server Response from: ETNASC03