menu
  index  ==>  papers  ==>  web  ==>  asp_net  ==>  delphi_asp_net_20_databases   

Delphi Asp.Net 2.0 Databases - Felix John COLIBRI.


1 - Asp.Net 2.0 database access

1.1 - Ado.Net and Asp.Net

With Rad Studio, we can develop Asp.Net application, and the purpose of this paper is to present how to acess Sql engine data in order to display them in Asp.Net pages.



1.2 - ADO.Net 1.0 Data Providers

Ado.Net introduced the "Data Providers" for database access. In Win32, the basic idea was to build generic layers, like the BDE, dbExpressDataSnap, Ado or Zeos, which would let the developer use the same components and code, irrespective of the target Sql Engine.

Ado.Net on the other hand came up with the Data Providers wich were tailored to fit the different Database types. By default, Ado.Net offered:

  • for Sql Server access, the Sql data provider (with SqlConnection, SqlCommand, SqlDataReader, SqlDataAdapter, SqlParameters, even SqlType for parameters)
  • for Oracle, we had the Oracle Data Provider (OraConnection, OraCommand etc)
  • and the remainder could be handle in a mixed bag called OleDb Provider, or ODBC Provider
Of course Microsoft did not take too much time to build a specific Data Provider for Interbase. For Delphi 8 (and for the next versions as well), we, could use OleDb, wich like other OleDb stuff left the developer alone to find somewhere the correct connection string (usually googling around long enough to find someone who published the string with all those magic values).

With Delphi 2005, Borland offered a Data Provider of their ow, the BDP (Borland Data Provider), which we could use to access InterBase, and also all other databases. One could use this bona fide .Net Data Provider with any kind of .Net compliant language (Prolog, Fortran) to access some database.

At the time, we published several articles telling in detail how on could use this BDP with InterBase, Firebird etc.



1.3 - Ado.Net 2.0 Data Access

With Ado.Net 2.0 the situation on the .Net side is very similar: there are still separate providers for Sql Server, Oracle, OleDb and ODBC. The first two are present in the Rad Studio Data Explorer, under the Ado.Net node.

To accomodate the new Blackfish Sql embedded database engine, CodeGear added two additional Ado.Net providers: a local and a remote Blackfish Sql Data Providers

The BDP node was completely removed from the Data Explorer, and the BDP is considered "deprecated".

To replace the BDP, CodeGear introduced the AdoDbxClient, which is a new Ado.Net provider implementation that we can use to handle all Sql Servers available with DbExpress: InterBase, MySql, etc and Blackfish as well.

This AdoDbxClient uses the new low-level DbExpress 4 (Dbx4 for short) driver layer, and we can use the Dbx4 components to connect to databases and execute Sql statements. We have presented this kind of programming in the Delphi Dbx4 programming article.



To recap:

  • for Blackfish, we can use
    • the Ado.Net Blackfish local or remote providers
    • the AdoDbxClient provider
  • for Sql Server or Oracle:
    • the Microsoft Ado.Net Sql Server or Oracle Ado.Net providers
    • the AdoDbxClient provider
  • for Interbase or other Sql engines:
    • the AdoDbxClient provider
In addition
  • one can still try the ODBC route
  • we will also show how we can use the low-level Dbx4 components, mainly for code handling (no databinding to Web Controls).


The general schema of the Data Providers is the following:

ado_net_data_providers



1.4 - Data Access Components

Once we have selected a Data Provider, we will use the component set of this provider.

We described in great detail the component sets and their use in several articles (Ado.Net and Sql Server, Interbase and the BDP, even Firebird). Without detailing each piece of the Ado.Net pipeline, the following schema sums it all up:

ado_net_component_architecture

and:

  • the xxx_Connection contains the database address, the user and password, and is used to connect to the database
  • the xxx_command will contain the Sql request. This request may be parametrized and use xxx_parameters
  • the xxx_datareader is mainly used to read rows for code handling
  • the DataAdapter is a intermediate component between the connected components and the in-memory DataSet
  • the DataSet contains DataTables, Relations, DataViews
  • those in-memory data can then linked (data bound) to the visual controls


1.5 - Ado.Net and Asp.Net

We will present how to include in our Asp.Net pages two databases:
  • InterBase, for which no dedicated Asp.Net provider is available
  • Blackfish where we can use the same components as those used for InterBase, plus the dedicated Blackfish Ado.Net providers
We used the following setup:
  • the DbxConnections.Ini (c:\Shared Documents\RAD Studio\dbExpress) was copied and saved in another folder, just in case
  • we created a _data folder and copied the Interbase EMPLOYEE.GDB database, as well as the EMPLOYEE.JDS Blackfish database)



2 - Asp.Net 2.0 and InterBase

2.1 - Using the dbx DbExpress 4 access components

2.1.1 - Create an Interbase Dbx Connection

As explained before, we can use the DbxConnection, DbxCommand, DbxDataReader driver-level access components to read and write data, even from Asp.Net.

To use those components, we first have to create a Dbx4 connection. This can be done with those steps:
   load Rad Studio 2007, .Net personality
   select the top-right pane, the Data Explorer tab, select "DbExpress | Interbase | Add new connection"
   in the connection name dialog, enter a name, for instance DBX_EMPLOYEE_7_IB_DAN (Dbx and not Ado.Net, the name and Ib version, for the Delphi Asp.Net presentation) and type Ok
   in the "Add Connection" dialog, enter the path and file of your database, the user and the password:

create_dbx_connection_entry

Test the connection and click "Ok"

   here is the new Interbase connection:

new_interbase_connection



2.1.2 - Test the connection

Now create the project and test the connection:
   select "File | New | Asp.Net Web Application"
   the path and file dialog is displayed:

create_asp_net_application

   enter your path and application name (INTERBASE_DBX) and click Ok
   the default files are displayed in the Project Manager and the design surface is set up:

new_asp_net_project



Not related to Asp.Net database programming, we usually add a TextBox to the Form to be able to follow the action:
   in the Object Inspector's Selector, select DOCUMENT, and set the property PageLayout to GridLayout
   from the Tools Palette, drag a TextBox, set its TextMode property to MutliLine
   add the display method:

procedure TDefault.display(p_textString);
  begin
    TextBox1.Text:= TextBox1.Textp_textchr(13)+ chr(10);
  end// display



First we will simply connect to Interbase:
   from the Tools Palette, drag a Button on the Form, create its OnClick event, and create a tDbxConnection:

const k_connection_name'DBX_EMPLOYEE_7_IB_DAN';

procedure TDefault.connect_dbx__Click(senderTObject;
    eSystem.EventArgs);
  var l_c_dbx_connection_factoryTDBXConnectionFactory;
      l_c_dbx_propertiesTDBXProperties;
      l_c_dbx_connectiontDbxConnection;
  begin
    display('> get_dbx_connection');
    l_c_dbx_connection_factory:=
        TDBXConnectionFactory.GetConnectionFactory;
    l_c_dbx_properties:=
        l_c_dbx_connection_factory.GetConnectionProperties(k_connection_name);
    display(l_c_dbx_properties.Values['Database']);

    display('GetConnection');
    l_c_dbx_connection:=
        l_c_dbx_connection_factory.GetConnection(l_c_dbx_properties );

    display('< get_dbx_connection');
  end// connect_dbx__Click

   in the Project Manager, select "interbase_dbx | References | right click | Add Reference"
   the "Add Reference" dialog is displayed, and all available assemblies are displayed:

add_dynalink_reference

   select Borland.Data.Common.Dll and Borland.Data.Dbx.DynalinkDriver.Dll and click "Add Reference" and "Ok"
   the assembly references are added to our project:

dynalink_reference

   select the ".PAS" tab, and in the IMPLEMENTATION add the USES import for Borland.Data.Common and Borland.Data.DBXCommon

   compile
   here is the result:

connect_interbase_dbx



Note that
  • if you do not add the references, the project still compiles, but you will receive a "Unable to cast object of type 'Borland.Data.TDBXDynalinkDriverLoader' to type 'Borland.Data.TDBXDriverLoader'", which, of course, means "add the DynalinkDriver reference"
  • we cannot "Open" the tDbxConnection. Creating it automatically calls a PROTECTED Open.


2.1.3 - Read and display the rows

We now will use a tDbxReader to read and fetch the rows of a Table, COUNTRY in our case:
   from the Tools Palette, drag a Button on the Form, create its OnClick event, create a tDbxConnection, create a tDbxReader and display the row values in the TextBox:

function f_c_dbx_connection(p_connection_nameWideString): tDbxConnection;
  var l_c_dbx_connection_factoryTDBXConnectionFactory;
      l_c_dbx_propertiesTDBXProperties;
  begin
    l_c_dbx_connection_factory:=
        tDBXConnectionFactory.GetConnectionFactory;
    l_c_dbx_properties:=
        l_c_dbx_connection_factory.GetConnectionProperties(p_connection_name);

    Result:= l_c_dbx_connection_factory.GetConnection(l_c_dbx_properties );
  end// f_c_dbx_connection

procedure TDefault.dbx_reader__Click(senderTObjecteSystem.EventArgs);
  var l_c_dbx_connectiontDbxConnection;
      l_c_dbx_commandtDbxCommand;
      l_c_dbx_readertDbxReader;
      l_column_indexInteger;
      l_row_displayString;
  begin
    l_c_dbx_connection:= f_c_dbx_connection(k_connection_name);
    l_c_dbx_command:= l_c_dbx_connection.CreateCommand;
    l_c_dbx_command.Text:= 'SELECT * FROM country';
    l_c_dbx_reader:= l_c_dbx_command.ExecuteQuery;

    while l_c_dbx_reader.Next do
    begin
      l_row_display:= '  ';
      for l_column_index:= 0 to l_c_dbx_reader.ColumnCount- 1 do
        l_row_display:= l_row_display
            + l_c_dbx_reader.Value[l_column_index].GetAnsiString' ';
      display(l_row_display);
    end// while l_c_dbx_reader.Next
  end// dbx_reader__Click

   compile
   here is the result:

dbx_reader



Please note
  • we started to implement some helper routines, like the f_c_dbx_connection function which will at the end be placed in a helper unit.
  • if we try to link the tDbxReader to a WebControl_xxx.DataSource, we will receive the following error message: "must be either an IListSource, IEnumerable, or IDataSource".

    In fact, the tDbxReader is a CLASS which does not implement any of those enumerators

  • in fact, here are the (partial) CLASSes involved in our example:

    dbx_uml_class_diagram



2.2 - Using the generic Db Ado.Net access components

2.2.1 - Connect using DbConnection

A more general approach would be to use Ado.Net compatible components, which will allow us to bind our access components to the bindable Web Controls.



First let's try to connect to the Interbase database:
   select "File | New | Asp.Net Web Application" and name it INTERBASE_DB
   set the grid mode and add a TextBox and the display method
   make sure that the "References" contain System.Data.Dll
   in the IMPLEMENTATION import in the USES clause the System.Data.Common UNIT
   select "Data Explorer | INTERBASE | DBX_EMPLOYEE_7_IB_DAN | right click | modify connection"
   the connection dialog is displayed
   select "Advanced ..."
   the "Advanced Properties" dialog is displayed, and near the bottom is the connection string
   copy this string to the ClipBoard, and paste it in a CONST of the .PAS text
   from the Tools Palette, drag a Button on the Form, create its OnClick event, create a DbConnection, and open it:

const k_ib_provider_name'Borland.Data.AdoDbxClient';

      k_ib_connection_string_1'drivername=INTERBASE;'
          + 'blobsize=-1;commitretain=False;'
          + 'database=C:\programs\us\web\asp_net\asp_net_20_ado_net\_data\employee_7.gdb;'
          + 'localecode=0000;rolename=RoleName;sqldialect=3;'
          + 'interbase transisolation=ReadCommited;';
      k_ib_connection_string_2'user_name=sysdba;waitonlocks=True;trim char=False'
          + ';password=masterkey';

procedure TDefault.connect_db__Click(senderTObjecteSystem.EventArgs);
  var l_c_db_provider_factoryDbProviderFactory;
      l_c_db_connectionDbConnection;
  begin
    l_c_db_provider_factory:= DbProviderFactories.GetFactory(k_ib_provider_name);
    l_c_db_connection:= l_c_db_provider_factory.CreateConnection();
    l_c_db_connection.ConnectionString:=
        k_ib_connection_string_1k_ib_connection_string_2;
    l_c_db_connection.Open();
    l_c_db_connection.Close();
  end// connect_db__Click


   run
   the connection is open and closed


Please note that:

  • to create the DbConnection, DbProviderFactories requires the name of an Ado.Net provider. Since Rad Studio only offers those for Sql Server, Oracle and BlackFish, we use the generic AdoDbxClient implementation, which implements the Ado.Net providers, and this for all Sql engines listed under "DbExpress". And among those we have InterBase

    So in fact we are creating a AdoDbxClient connection, but will here only use the generic Ado.Net possibilities

  • we wanted to use the connection name. This did not succeed. In fact, the DbxConnections.Ini is a totally Dbx4 feature, and is not available to the more general Ado.Net level

    Since we cannot use the connection name, we used the connection string.

  • we placed the connection string required by the Ado.Net provider in a CONST. We were very surprised to receive an error because this string was greater then 255 Characters. Well it's nearly since the time of the Apple ][ that we had not seen this message !

    This explains why we did split the string in two.

  • and then we also had to add the password to the connection string, since it is not in the "Advanced Properties" label.

  • we also closed the connection, since we are not supposed to leave this DbConnection open


2.2.2 - Display the Rows

We will then display the rows of some Table:
   from the Tools Palette, drag a Button on the Form, create its OnClick event, create a DbConnection, create a DbDataReader and display the row values in the TextBox:

procedure TDefault.db_reader__Click(senderTObjecteSystem.EventArgs);
  var l_c_db_provider_factoryDbProviderFactory;
      l_c_db_connectionDbConnection;
      l_c_db_commandDbCommand;
      l_c_db_datareaderDbDataReader;

      l_row_displayString;
      l_column_indexInteger;
  begin
    l_c_db_provider_factory:=
        DbProviderFactories.GetFactory(k_ib_provider_name);
    l_c_db_connection:= l_c_db_provider_factory.CreateConnection();
    l_c_db_connection.ConnectionString:=
        k_ib_connection_string_1k_ib_connection_string_2;

    l_c_db_command:= l_c_db_connection.CreateCommand();
    l_c_db_command.CommandText:= 'SELECT * FROM country';

    l_c_db_connection.Open;
    l_c_db_datareader:= l_c_db_command.ExecuteReader;

    while l_c_db_datareader.Read do
    begin
      l_row_display:= '  ';
      for l_column_index:= 0 to l_c_db_datareader.FieldCount- 1 do
        l_row_display:= l_row_display
            + l_c_db_datareader.Item[l_column_index].ToString' ';
      display(l_row_display);
    end// while l_c_db_datareader.Read

    l_c_db_connection.Close;
  end// db_reader__Click

   compile
   the rows of COUNTRY are displayed


2.2.3 - Fill a ListBox

Since the DbDataReader implements the enumerable interface, we can use it to fill the values of bindable Web Controls. Here is how to do it by code:
   from the Tools Palette, drag a Listbox on the Form
   drag a Button on the Form, create its OnClick event, create a DbConnection, create a DbDataReader and connect the ListBox.DataSource and ListBox.DataTextField:

function f_c_db_datareader(p_c_db_connectionDbConnection;
    p_select_requestString): DbDataReader;
  // -- receives an OPEN connection
  var l_c_db_commandDbCommand;
  begin
    l_c_db_command:= p_c_db_connection.CreateCommand();
    l_c_db_command.CommandText:= p_select_request;

    Result:= l_c_db_command.ExecuteReader;
  end// f_c_db_datareader

procedure TDefault.fill_listbox__Click(senderTObject;
    eSystem.EventArgs);
  var l_c_db_provider_factoryDbProviderFactory;
      l_c_db_connectionDbConnection;
      l_c_db_datareaderDbDataReader;
  begin
    l_c_db_provider_factory:=
        DbProviderFactories.GetFactory(k_ib_provider_name);
    l_c_db_connection:= l_c_db_provider_factory.CreateConnection();
    l_c_db_connection.ConnectionString:=
        k_ib_connection_string_1k_ib_connection_string_2;

    l_c_db_connection.Open;

    l_c_db_datareader:= f_c_db_datareader(
        l_c_db_connection'SELECT * FROM country');

    ListBox1.DataSource:= l_c_db_datareader;
    ListBox1.DataTextField:= 'COUNTRY';

    DataBind;
  end// fill_listbox__Click

   compile
   the rows of COUNTRY are displayed

ado_net_connection1_get_the_connection



Please note
  • we did not use a FUNCTION which returns the DbConnection, since we prefer to keep the Open and Close in the same method. In this case a DbConnection creation FUNCTION would not have much to do
  • we created a FUNCTION which returns a DataReader however
  • the DataBind technique is the old Asp.Net 1 technique. We are now supposed to use DataSources, and this will be demonstrated below. The old technique still works however.

    In addition, when we use DataBind, we can select when to do the binding (when NOT Page.IsPostBack, for instance), and avoid doing this at each round trip, and we might even disable the ViewState. We will not develop those very common Asp.Net techniques in this article.



And here is the (shortened) UML Class diagram of the Db components we used in our examples:

db_uml_class_diagram




2.3 - The AdoDbxClient access components

2.3.1 - AdoDbxClient

The third route is the AdoDbxClient Ado.Net provider.

This brand new component suite was created to have the Ado.Net provider for all the Dbx accessible Sql engines. We have the full source code, and in addition it works for both .Net and .Win32.



2.3.2 - Testing the AdoDbxClient connection

Here is how to test the connection:
   select "File | New | Asp.Net Web Application" and name it INTERBASE_ADODBXCLIENT
   set the grid mode and add a TextBox and the display method
   select "Project Manager |`References" and add Borland.Data.AdoDbxClient.Dll
   in the IMPLEMENTATION import in the USES clause the Borland.Data.AdoDbxClientProvider UNIT
   copy the connection string from the DBX_EMPLOYEE_7_IB_DAN connection and paste it in a CONST of the .PAS text
   from the Tools Palette, drag a Button on the Form, create its OnClick event, create a tAdoDbxConnection, and open it:

const k_ib_connection_string_1'drivername=INTERBASE;'
          + 'blobsize=-1;commitretain=False;'
          + 'database=C:\programs\us\web\asp_net\'
          +   'asp_net_20_ado_net\_data\employee_7.gdb;'
          + 'localecode=0000;rolename=RoleName;sqldialect=3;'
          + 'interbase transisolation=ReadCommited;';
      k_ib_connection_string_2'user_name=sysdba;waitonlocks=True;trim char=False'
          + ';password=masterkey';

procedure TDefault.connect_ib_adodbx__Click(senderTObjecteSystem.EventArgs);
  var l_c_ado_dbx_connectiontAdoDbxConnection;
  begin
    l_c_ado_dbx_connection:=
        TAdoDbxProviderFactory.Instance.CreateConnection as TAdoDbxConnection;
    l_c_ado_dbx_connection.ConnectionString :=
        k_ib_connection_string_1k_ib_connection_string_2;

    l_c_ado_dbx_connection.Open;
    l_c_ado_dbx_connection.Close;
  end// connect_ib_adodbx__Click

   run
   the connection is open and closed


2.3.3 - Connect using the Connection Name

The tAdoDbxConnection.ConnectionString can also be initialized by using the connection name, and setting this value in a "key=value" fashion:
   from the Tools Palette, drag a Button on the Form, create its OnClick event, create a tAdoDbxConnection, and open it:

const k_ib_connection_name'DBX_EMPLOYEE_7_IB_DAN';

procedure TDefault.connect_ib_adodbx_2__Click(senderTObject;
    eSystem.EventArgs);
  var l_c_ado_dbx_connectiontAdoDbxConnection;
  begin
    l_c_ado_dbx_connection:= TAdoDbxProviderFactory.Instance.CreateConnection
        as TAdoDbxConnection;
    l_c_ado_dbx_connection.ConnectionString:=
        'ConnectionName='k_ib_connection_name;

    l_c_ado_dbx_connection.Open;
    l_c_ado_dbx_connection.Close;
  end// connect_ib_adodbx_2__Click

   run
   the connection is open and closed


2.3.4 - Using tAdoDbxCommand and tAdoDbxDataReader

To read the rows and display them, or otherwise handle the values, we can use the following code:
   from the Tools Palette, drag a Button on the Form, create its OnClick event, create a tAdoDbxConnection, and open it, then create a command and create the tAdoDbxDataReader:

function f_c_ado_dbx_connection_from_string(p_connection_stringWideString):
    TAdoDbxConnection;
  begin
    Result:= TAdoDbxProviderFactory.Instance.CreateConnection
        as TAdoDbxConnection;
    Result.ConnectionString := p_connection_string;
  end// f_c_ado_dbx_connection_from_string

procedure TDefault.adodbx_datareader__Click(senderTObject;
    eSystem.EventArgs);
  var l_c_ado_dbx_connectiontAdoDbxConnection;
      l_c_ado_dbx_commandtAdoDbxCommand;
      l_c_ado_dbx_readertAdoDbxDataReader;

      l_row_displayString;
      l_column_indexInteger;
  begin
    l_c_ado_dbx_connection:= f_c_ado_dbx_connection_from_string(
        k_ib_connection_string_1k_ib_connection_string_2);
    l_c_ado_dbx_command:= l_c_ado_dbx_connection.CreateCommand
        as tAdoDbxCommand;
    l_c_ado_dbx_command.CommandText:= 'SELECT * FROM country';

    l_c_ado_dbx_connection.Open;
    l_c_ado_dbx_reader:= l_c_ado_dbx_command.ExecuteReader
        as TAdoDbxDataReader;
    while l_c_ado_dbx_reader.Read do
    begin
      l_row_display:= '';
      for l_column_index:= 0 to l_c_ado_dbx_reader.FieldCount- 1 do
        l_row_display:= l_row_display
            + l_c_ado_dbx_reader.GetString(l_column_index)+ ' ';

      display(l_row_display);
    end// while l_c_db_datareader.Read

    l_c_ado_dbx_connection.Close;
  end// adodbx_datareader__Click

   run
   the rows of COUNTRY are displayed


2.3.5 - Filling a ListBox

The same technique as the one used with the DbConnection can be used to fill a ListBox:
   from the Tools Palette, drag a Listbox on the Form
   drag a Button on the Form, create its OnClick event, create the connection, the tAdoDbxDataReader and connect the ListBox.DataSource and ListBox.DataTextField:

function f_c_ado_dbx_datareader(p_c_adodbx_connectiontAdoDbxConnection;
    p_select_requestString): tAdoDbxDataReader;
  // -- the connection must be open
  var l_c_ado_dbx_commandtAdoDbxCommand;
  begin
    l_c_ado_dbx_command:= p_c_adodbx_connection.CreateCommand
        as tAdoDbxCommand;
    l_c_ado_dbx_command.CommandText:= p_select_request;

    Result:= l_c_ado_dbx_command.ExecuteReader as TAdoDbxDataReader;
  end// f_c_ado_dbx_datareader

procedure TDefault.fill_listbox__Click(senderTObject;
    eSystem.EventArgs);
  var l_c_ado_dbx_connectiontAdoDbxConnection;
  begin
    l_c_ado_dbx_connection:= f_c_ado_dbx_connection_from_string(
        k_ib_connection_string_1k_ib_connection_string_2);
    l_c_ado_dbx_connection.Open;

    ListBox1.DataSource:= f_c_ado_dbx_datareader(
        l_c_ado_dbx_connection,
        'SELECT * FROM country');
    ListBox1.DataTextField:= 'CURRENCY';

    DataBind;

    l_c_ado_dbx_connection.Close;
  end// fill_listbox__Click

   compile
   the rows of COUNTRY are displayed


Using the tAdoDbxConnection component

The Tools Palette also contains tAdoDbx Provider components:

AdoDbx_Provider



So lets try to connect using those components:
   drag the tAdoDbxConnection on the Form
   the component is placed below the designer:

tAdoDbxConnection

   fill the ConnectionString with the connection string (copied and pasted from the Data Explorer), or set the individual properties of the connection string

   drop a tAdoDbxCommand, set its Connection property to AdoDbxConnection1, and its CommandText to some Sql request, like "SELECT * FROM country"
   from the Tools Palette, drag a Listbox on the Form
   drag a Button on the Form, create its OnClick and connect the ListBox2.DataSource and ListBox2.DataTextField:

procedure TDefault.fill_listbox_2__Click(senderTObject;
    eSystem.EventArgs);
  begin
    tAdoDbxConnection1.Open;

    ListBox2.DataSource:= tAdoDbxCommand1.ExecuteReader
        as TAdoDbxDataReader;
    ListBox2.DataTextField:= 'CURRENCY';

    DataBind;

    tAdoDbxConnection1.Close;
  end// fill_listbox_2__Click

   compile
   the rows of COUNTRY are displayed


Please Note

  • we CANNOT drag the DBX_EMPLOYEE_7_IB_DAN connection from the Data Explorer to the Form. And we cannot drag one of its Tables. This is only available for Win32 or Vcl.Net applications
  • there is no Connected property which would allow us to check the connection, or any connection editor with a "Test" button
  • the AdoDbx_xxx objects have a funny T before the component name.
  • there is also a tAdoDbxDataAdapter, but we could not figure out how to use it, since it has no SelectCommand or similar property


2.3.6 - Using DataSources

One of the main innovation of Asp.Net 2.0 is the DataSource technique. Instead of calling DataBind to fill the Web Controls, we use the new DataSource component and this component takes care of the databinding part.

There are several DataSources:

  • by default, the SqlDataSource was offered to handle Sql Server data types. It also works with Blackfish, but not for InterBase when the Table uses some types with different formats than Sql Server
  • so CodeGear created a DbxDataSource, which can handle InterBase Tables, and this is the one we will use
  • there are also other DataSources, used to implement the "Provider Model":

    tAdoDbxConnection

    Those mainly allows us to fill data structures like Site Maps, Memberships or other from different sources, or otherwise allow us to implement business logic. We will not present those in this article



In order to use the DbxDataSource, there are two approaches
  • drop an xxx_DataSource, initialize it, and then connect it to some databindable Web Control (ListBox, GridView etc)
  • directly drop a Web Control and use its smart tab to create and initialize the DataSource and connect the Web Control to it


Here is the first approach:
   select "Tools Palette | Web Data" and double click on DbxDataSource
   the control is dropped on the Form, and displayed in the top left corner
   you may drop an HTML Table, position thie Table where you like, then cut and paste DbxDataSource1 in the first cell of this Table
   click the top-right little arrow of DbxDataSource1 (red arrow)
   the "smart tasks" are displayed

dbxdatasource_smart_tasks

   select "Configure DataSource"
   a huge configuration dialog is displayed:

configure_datasource

the connection that we displayed are Ado.Net connections

   select "New Connection"
   the "Choose DataSource" dialog is displayed

chose_datasource

   select "dbExpress" and click "Continue"
   a connection dialog, very similar to the Data Explorer dialog is presented:

datasource_add_connection

   fill this dialog and click "Ok"
   you are brought back to the start dialog
   click "Next"
   a "save connection" is displayed:

save_connection_in_web_config

   click "Next"
   after some disc activity, the "Configure the Select Statement" is presented:

configure_the_select_statement

   either type your request, or select "specify colums", select the table and the columns and select "Next"

   the "Test Query" dialog is presented:

test_query

   click "Test Query" and then "Finish"
   you are back to the Form


Please note that
  • you CANNOT drag any DataSource on the Form if you are using the GridLayout. You will see a red error "the DataSource has no style property". This means that in GridLayout, the designer adds a STYLE attribute, and since the non-visible DataSource component have no such attribute, you will get this error. We circumvented this by using an HTML Table, but only for presenting this paper. First we should use FlowLayout, and second, leaving the DataSource at the top left position is not such a big problem


Now we add any bindable Web Control, for instance a GridView:
   select "Tools Palette | Web Data | GridView" and drag it below the DbxDataSource

   click the top-right arrow to display the smart task list
   the GridView smart task menu is displayed:

choose_datasource

   select "Choose DataSource | DbxDataSource1"
   the COUNTRY schema is loaded, and the GridView headers are changed to "Country" and "Currency". In addition the smart tasks menu now presents a raft of additional possibilities which will not be explored any further here:

gridview_updated

   compile
   the GridView is filled, without having to call DataBind:

gridview_and_datasource



The other way to use the DataSource is to start with a bindable Web Control, and use its smart tab menu to create and initialize the DataSource:
   select "Tools Palette | Web Data | GridView" and drag it on the Form
   select "smart tasks | Choose DataSource | New DataSource"
   all available DataSource types are presented:

datasource_configuration_wizard

   select "DbxDataBase"
   a new ID, DbxDataSource2 is created, and this DbxDataSource2 is dropped on the Form (top left corner)
   click "Next"
   the same configuration dialog "Choose your Data Connection" presented earlier is displayed
   etc etc !


Note that
  • when we accepted to "save the connection in the configuration file", it was saved in the Web.Config file.

    By selecting "Project Manager | Web.Config" you can view this file in the IDE. Here is a (shortened) version of this file:

     
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <connectionStrings>
        <add name="BlackfishSqlAspNet"
            connectionString="database=|DataDirectory|bsql_aspnetdb.jds; ...ooo..."
            providerName="Borland.Data.BlackfishSQL.RemoteClient" />
        <add name="C:\programs\us\web\asp_net\asp_net_20_ado_net\
                _data\employee_7.gdbConnectionString
    "
            connectionString="DriverName=Interbase;DriverUnit=DBXDynalink; ...ooo... "
            providerName="Borland.Data.AdoDbxClient" />
      </connectionStrings>
      <system.data>
        <!-- ... ooo -->
      </system.data>
      <!-- ... ooo -->
    </configuration>

    If you now try to use another connection, those connections will be presented along with the Ado.Net connections already presented in the Data Explorer.

    You may also notice that each entry has a name connectionString and providerName attribute. We will use those to set the connection later in this article.



2.3.7 - AdoDbxClient UML Class Diagram

And here is the (shortened) AdoDbxClient CLASS Diagram, extracted from Borland.Data.AdoDbxClientProvider.Pas:

adodbxclient_uml_class_diagram

This diagram clearly shows that the AdoDbxClient is an implementation of the Ado.Net provider




3 - Asp.Net 2.0 and Blackfish Sql

3.1 - Using the dbx DbExpress 4 access components

3.1.1 - Create a BlackFish Dbx Connection

As we already presented in the
Blackfish Sql paper, we can use the DbxConnection, DbxCommand, DbxDataReader driver-level access components to read and write data, even from Asp.Net.

To use those components, we first have to create a Dbx4 connection. This can be done with steps similar to those explained for Interbase:
   load Rad Studio 2007, .Net personality
   select the top-right pane, the Data Explorer tab, select "DbExpress | BlackfishSql | right click | Add new connection"
   in the connection name dialog, enter a name, for instance DBX_EMPLOYEE_BFS_DAN (Dbx and not Ado.Net, the name, for the Delphi Asp.Net presentation) and type Ok
   in the "Add Connection" dialog, enter the path and file of your database, the user and the password:

dbx_blackfish_connection

Test the connection and click "Ok"



Note that

  • for some not well understood reason, we received an error "cannot change employee.jds database log file ...". We renamed the file employee_2.jds and this solved the problem (but what we did wrong is still mysterious)


3.1.2 - Test the connection

Now create the project and test the connection:
   select "File | New | Asp.Net Web Application", BLACKFISH_DBX
   add the TextBox and display method


First we will simply connect to BlackfishSql:
   from the Tools Palette, drag a Button on the Form, create its OnClick event, and create a tDbxConnection:

const k_blackfish_dbx_connection_name'dbx_employee_jds';

procedure TDefault.connect_jds_dbx__Click(senderTObject;
    eSystem.EventArgs);
  var l_c_dbx_connection_factoryTDBXConnectionFactory;
      l_c_dbx_propertiesTDBXProperties;
      l_c_dbx_connectiontDbxConnection;
  begin
    l_c_dbx_connection_factory:=
        TDBXConnectionFactory.GetConnectionFactory;
    l_c_dbx_properties:=
        l_c_dbx_connection_factory.GetConnectionProperties(k_blackfish_dbx_connection_name);

    display('  'l_c_dbx_properties.Values['Database']);

    l_c_dbx_connection:= l_c_dbx_connection_factory.GetConnection(l_c_dbx_properties );
  end// connect_jds_dbx__Click

   in the Project Manager, select "blackfish_dbx.dll | References | right click | Add Reference"
   the "Add Reference" dialog is displayed
   select Borland.Data.Common.Dll and Borland.Data.DbxCommonDriver.Dll and add them
   select the ".PAS" tab, and in the IMPLEMENTATION add the USES import for Borland.Data.Common and Borland.Data.DBXCommon

   compile
   the Blackfish Dbx connection is created


3.1.3 - Read Rows with the DbxReader

We can also display the values of the rows of the COUNTRY Table:
   from the Tools Palette, drag a Button on the Form, create its OnClick event, and create a tDbxConnection, create DbxCommand, and with a DbxReader display the values:

function f_c_dbx_connection(p_connection_nameWideString): tDbxConnection;
  var l_c_dbx_connection_factoryTDBXConnectionFactory;
      l_c_dbx_propertiesTDBXProperties;
  begin
    l_c_dbx_connection_factory:= TDBXConnectionFactory.GetConnectionFactory;
    l_c_dbx_properties:=
        l_c_dbx_connection_factory.GetConnectionProperties(p_connection_name);

    Result:= l_c_dbx_connection_factory.GetConnection(l_c_dbx_properties );
  end// f_c_dbx_connection

procedure TDefault.display_dbxreader__Click(senderTObject;
    eSystem.EventArgs);
  var l_c_dbx_connectiontDbxConnection;
      l_c_dbx_commandtDbxCommand;
      l_c_dbx_readertDbxReader;
      l_column_indexInteger;
      l_row_displayString;
  begin
    l_c_dbx_connection:= f_c_dbx_connection(k_blackfish_dbx_connection_name);
    l_c_dbx_command:= l_c_dbx_connection.CreateCommand;
    l_c_dbx_command.Text:= 'SELECT * FROM country';
    l_c_dbx_reader:= l_c_dbx_command.ExecuteQuery;

    while l_c_dbx_reader.Next do
    begin
      l_row_display:= '  ';
      for l_column_index:= 0 to l_c_dbx_reader.ColumnCount- 1 do
        l_row_display:= l_row_display
            + l_c_dbx_reader.Value[l_column_index].GetAnsiString' ';
      display(l_row_display);
    end// while l_c_dbx_reader.Next
  end// display_dbxreader__Click

   compile
   the values are displayed


3.2 - BlackFish and Ado.Net

3.2.1 - Ado.Net Providers

Unlike InterBase, Blackfish does have direct Ado.Net providers, which are displayed in the Data Explorer:

blackfish_ado_net_providers



We will first add a Remote Provider entry for our EMPLOYEE_2.JDS database:
   load Rad Studio 2007, .Net personality
   select the top-right pane, the Data Explorer tab, select "Ado Net | BlackfishSql Remote Provider | rigth click | Add new connection"
   in the connection name dialog, enter a name, for instance ANR_EMPLOYEE_JDS_DAN (Ado Net Remote, the name, for the Delphi Asp.Net presentation) and type Ok
   in the "Add Connection" dialog, enter the path and file of your database, the user and the password:

blackfish_ado_net_connection

Test the connection and click "Ok"



3.2.2 - Test the DbConnection

Now create the project and test the connection:
   select "File | New | Asp.Net Web Application", BLACKFISH_DB
   add the TextBox and display method


First we will simply connect to Blackfish Sql:
   from the Tools Palette, drag a Button on the Form, create its OnClick event, and create a DbConnection:

const k_bfs_anr_provider_name'Borland.Data.BlackfishSQL.RemoteClient';
      // -- taken from Data Explorer + password
      k_bfs_bfs_connection_string'host=LocalHost;user=sysdba'
          + ';database=C:\programs\us\db\blackfishsql\_data\employee.jds'
          + ';password=masterkey';

procedure TDefault.connect_jds_an_remote__Click(senderTObject;
    eSystem.EventArgs);
  var l_c_db_provider_factoryDbProviderFactory;
      l_c_db_connectionDbConnection;
  begin
    l_c_db_provider_factory:=
        DbProviderFactories.GetFactory(k_bfs_anr_provider_name);

    l_c_db_connection:= l_c_db_provider_factory.CreateConnection();

    l_c_db_connection.ConnectionString:= k_bfs_bfs_connection_string;

    l_c_db_connection.Open;
    l_c_db_connection.Close;
  end// connect_jds_an_remote__Click

   in the Project Manager, select "blackfish_dbx.dll | References | right click | Add Reference"
   the "Add Reference" dialog is displayed
   select Borland.Data.Common.Dll and add this assembly
   select the ".PAS" tab, and in the IMPLEMENTATION add the USES import for Borland.Data.Common

   compile
   the Blackfish DbConnection is opened and closed


Please note
  • since the creation of the DbConnection uses the provider name and the connection string, there is no real need to create an entry in the Data Explorer: the code never uses the connection NAME. However this entry was used to get the connection STRING


3.2.3 - Read Rows with the DbDataReader

We can also display the values of the rows of the COUNTRY Table:
   from the Tools Palette, drag a Button on the Form, create its OnClick event, and create a DbConnection, create DbCommand, and with a DbDataReader display the values:

procedure TDefault.display_datareader__Click(senderTObjecteSystem.EventArgs);
  var l_c_db_provider_factoryDbProviderFactory;
      l_c_db_connectionDbConnection;
      l_c_db_commandDbCommand;
      l_c_db_datareaderDbDataReader;

      l_row_displayString;
      l_column_indexInteger;
  begin
    l_c_db_provider_factory:= DbProviderFactories.GetFactory(k_bfs_anr_provider_name);
    l_c_db_connection:= l_c_db_provider_factory.CreateConnection();
    l_c_db_connection.ConnectionString:= k_bfs_bfs_connection_string;

    l_c_db_connection.Open;
    l_c_db_command:= l_c_db_connection.CreateCommand();
    l_c_db_command.CommandText:= 'SELECT * FROM country';

    l_c_db_datareader:= l_c_db_command.ExecuteReader;

    while l_c_db_datareader.Read do
    begin
      l_row_display:= '  ';
      for l_column_index:= 0 to l_c_db_datareader.FieldCount- 1 do
        l_row_display:= l_row_display
            + l_c_db_datareader.Item[l_column_index].ToString' ';
      display(l_row_display);
    end// while l_c_db_datareader.Read

    l_c_db_connection.Close;
  end// display_datareader__Click

   compile
   the values are displayed


3.2.4 - DataBind a ListBox

We could fill a ListBox from the DbDataReader, as we did with the InterBase example.



3.2.5 - Using the DataStore components

The Tools Palette contains data access components for Blackfish:

DataStoreConnection

The "DataStore" is the new generic name for the CodeGear embedded database. This all started with JDataStore (a Java thingy), and was rewritten for .Net and renamed NDataStore and later Blackfish. So the "DataStore" comes from those times.



Here is how to use the DataStoreConnection component:
   drag a DataStoreConnection on the Form
   it is displayed in the "component tray", under the designer surface
   select "Object Inspector | Connection String | ellipsis"
   a connection editor is presented:

connect_DataStore

   select the employee_2.jds, test the connection and click "Ok"


We can also use a DataStoreCommand component, and will have

  • to set its Connection property (to DataStoreConnection1)
  • type some Sql request ("SELECT * FROM country")
And, in a similar vein, a DataStoreDataAdapter, which behaves somehow like the Windows Forms DataAdapter:
   drag a DataStoreDataAdapter on the Form
   in the component tray, select "DataStoreDataAdapter1 | right click | configure Data Adapter"
   the configuration dialog is displayed (Ed: right part of this huge dialog truncated):

configure_datastore_data_adapter

   add the connection, select the Table, the rows and click "Generate Sql" (on the right, in the truncated part).

You may look at the data, using the "Preview Data" tab

Then click "Ok"

   select "Tools Palette | DataComponents"

add_dataset

and drag a DataSet on the Form

   from the Tools Palette, drag a ListBox on the Form

   from the Tools Palette, drag a Button on the Form, create its OnClick event, and fill DataSet1 and databind it to ListBox1

procedure TDefault.fill_and_databiind__Click(senderTObject;
    eSystem.EventArgs);
  begin
    DataStoreDataAdapter1.Fill(DataSet1);

    ListBox1.DataSource:= DataSet1.Tables[0].DefaultView;
    ListBox1.DataTextField:= 'COUNTRY';

    DataBind;
  end// fill_and_databiind__Click

   run
   here is a snapshot of clicking all our buttons:

fill_and_databind



Please Note
  • you cannot create a DataSet from the "Configure DataStore" dialog (like we could do with Windows Forms). But since we still need to call DataBind, this is not very important.
  • you can also start the connecting process with the bindable Web Control, and, using the smart tab, create the SqlDataSource and initialize it
  • instead of using the existing blackfish remote connection, we can select "create a new connection", and fill this with the usual parameters. This has been demonstrated for Interbase above (where this was the only route, since there was no InterBase Ado.Net provider)


3.2.6 - Using SqlDataSource

We can also use the new Ado.Net 2.0 SqlDataSource to automatically bind some Web Controls to our BlackfishSql database. And this time, we can use the original SqlDataSource, since the BlackfishSql data types are aligned to the .Net data types (which is not the case for InterBase).

Here is an example:
   drag an SqlDataSource on the Form (eventually position it in an HTML Table if you are in GridLayout)
   select "smart tab | Configure Data Source
   the "Choose Your Data Connection" dialog is displayed
   select the drop down combobox
   our ANR_EMPLOYEE_JDS_DAN is displayed among the other Ado.Net providers:

sqldatasource_connection

   select this connection, of course, click "Next", "Next", and in the Sql Request dialog setup the COUNTRY table, click "Next" and "Finish"


The Web.Config now contains our new connection:

 
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="BlackfishSqlAspNet"
        connectionString="database=|DataDirectory|bsql_aspnetdb.jds;protocol=TCP;
             host=localhost;user=SYSDBA;password=masterkey;create=true
"
        providerName="Borland.Data.BlackfishSQL.RemoteClient" />
    <add name="C:\programs\us\web\asp_net\asp_net_20_ado_net\_data\
             employee_2.jdsConnectionString
"
        connectionString="host=LocalHost;database=C:\programs\us\web\asp_net\
             asp_net_20_ado_net\_data\employee_2.jds;user=sysdba;password=masterkey
"
        providerName="Borland.Data.BlackfishSQL.RemoteClient" />
  </connectionStrings>
  <!-- ...ooo... -->
</configuration>



And we can now bind this SqlDataSource to a bindable Web Control, like the GridView:
   drag a GridView on the Form
   select "smart tab | Choose Data Source" and in the combobox select SqlDataSouce1
   run
   here is the display:


3.3 - Blackfish and AdoDbxClient

3.3.1 - tAdoDbxConnection

We connect using tAdoDbxConnection in the same way as we did with Interbase. Here is the code of the connection using the connection STRING and the connection NAME:

const k_bfs_connection_string_2'drivername=BLACKFISHSQL;user_name=sysdba'
          + ';port=2508;create=False;readonlydb=False;hostname=localhost;'
          + 'database=C:\programs\us\web\asp_net\asp_net_20_ado_net\'
          +     '_data\employee_2.jds'
          + ';password=masterkey';

procedure TDefault.connect_bfs_adodbx__Click(senderTObject;
    eSystem.EventArgs);
  var l_c_ado_dbx_connectiontAdoDbxConnection;
  begin
    l_c_ado_dbx_connection:=
        TAdoDbxProviderFactory.Instance.CreateConnection as TAdoDbxConnection;
    l_c_ado_dbx_connection.ConnectionString := k_bfs_connection_string_2;

    l_c_ado_dbx_connection.Open;
    l_c_ado_dbx_connection.Close;
  end// connect_bfs_adodbx__Click

const k_dbx_bfs_connection_name'DBX_EMPLOYEE_JDS';

procedure TDefault.connect_bfs_adodbx_2__Click(senderTObject;
    eSystem.EventArgs);
  var l_c_ado_dbx_connectiontAdoDbxConnection;
  begin
    l_c_ado_dbx_connection:=
        TAdoDbxProviderFactory.Instance.CreateConnection as TAdoDbxConnection;

    l_c_ado_dbx_connection.ConnectionString:=
        'ConnectionName='k_dbx_bfs_connection_name;

    l_c_ado_dbx_connection.Open;
    l_c_ado_dbx_connection.Close;
  end// connect_bfs_adodbx_2__Click



If our connection string is not correct, we will receive the following error: "Borland.Data.TDBXError: C:\programs\us\db\blackfishsql\_dataemployee_2.jds database not found or fileName property set to null."



Displaying the data with an AdoDbxDataReader, and databinding this to a tListBox behaves just like it did for InterBase.



3.4 - Using Web.Config

3.4.1 - Application Parameters

All our connection parameters have been hardwired either in the DbxConnections.Ini or even in CONST in the code.

This is supposed to be bad programming.

Instead we should use the .XML Web.Config file to store this kind of information.



3.4.2 - Get connection from Web.Config

The key is to use the ConfigurationManager CLASS (new in Asp.Net 2.0) which has two NameValueCollection properties
  • AppS