This site contains older material on Eiffel. For the main Eiffel page, see http://www.eiffel.com.

Behind the scene of the Eiffel Web Service Demo

OVERVIEW

This demo showcases an Eiffel Web Service. The service exposes features to add registrants and registrations to a conference into a database; it also provides queries to retrieve the content of the database.

The demo is entirely fictitious: entering your information in the registration page will not subscribe you to the TOOLS conference. If you are interested in registering for the TOOLS conference, please go to the TOOLS registration page.

WRITING A WEB SERVICE IN EIFFEL#

All you need to do to write a web service in EiffelSharp is to write a class that inherits from WEB_SERVICE.

The class WEB_SERVICE is an external class. This means that it has already been compiled and is available in an existing assembly (in the case of WEB_SERVICE, the assembly is System.Web.Services.dll). The EiffelSharp compiler will not generate MSIL code for this class.

Any call to a feature of WEB_SERVICE will be direct. This ensures that there is no performance penalty when calling methods on classes that are already compiled; there is no more need for proxies or stubs as used to be the case with COM.

The class WEB_SERVICE gives access to the standard ASP objects: session, application, context and server. The class that implement the web service can access any of these objects to manage the state of the ASP+ application, access any information in the HTTP query and create the resulting HTML page.

This demo includes the class REGISTRAR that inherits from WEB_SERVICE and defines features to add and retrieve registrations to a conference.

ACCESSING THE SERVICE VIA AN ASP+ PAGE

This demo provides two ASP+ pages to access the web service.

    + The registration.aspx page supports entering new data into the database.

    + The report.aspx page retrieves the information and displays it into a data grid.

    Note: The ASP+ pages currently use a small amount of C# code. This is temporary; it will shortly be possible to write the ASP+ part in Eiffel# along with the rest of the application.

Adding entries to the tables

The registration page supports entering new registrants and registrations into the database. The page consists of a form with input fields for all the necessary information (first and last name, address, company etc.

When the page is first loaded, it creates an instance of the Eiffel class REGISTRAR and stores it in the Application dictionary:        

void Page_Init( Object Source, EventArgs E )
{
    registrar = new Registrar();
    registrar.start();
    Application ["Registrar"] = registrar;
    registered = false;
    register_click = false;
}

ASP+ knows where to find the Regsitrar type thanks to the following lines:

<%@ Assembly Name="registrationservice" %>
<%@ Import Namespace="RegistrationService" %>

The assembly registrationservice was compiled with the Eiffel# compiler and copied into the bin folder of the demo web site.

Whenever the “Register” button is clicked, the C# script calls the features add_registrant and add_registration from REGISTRAR

void Register_Click( Object Source, EventArgs E )

{
         
// Add registrant
          registrar.add_registrant( address_form.SelectedItem.Value,
                                                 
first_name.Value,
                                                  last_name.Value,
                                                  company_name.Value,
                                                  address.Value,
                                                  city.Value,
                                                  state.Value,
                                                  zip.Value,
                                                  country.Value );

        if( registrar.last_operation_successful())
        {

                  // Add registration
                  registrar.add_registration( registrar.last_registrant_identifier(),
                                                             quantity.Value,
                                                             discount_plan.SelectedItem.Value,
                                                             preconf.Checked,
                                                             wet.Checked,
                                                             conference.Checked,
                                                             esummit.Checked,
                                                             postconf.Checked);
        }

          … 

 Where add_registrant and add_registration have the following signatures:

add_registrant (address_form, first_name, last_name, company_name, address, city, state, zip, country: STRING)
                                  -- Add new registrant.
                      require
                                  non_void_address_form: address_form /= Void
                                  non_void_first_name: first_name /= Void
                                  non_void_last_name: last_name /= Void
                                  non_void_company_name: company_name /= Void
                                  non_void_address: address /= Void
                                  non_void_city: city /= Void
                                  non_void_state: state /= Void
                                  non_void_zip: zip /= Void
                                  non_void_country: country /= Void
                                  valid_adress_form:
                                 
                              address_form.equals ("Mr.")
                                                                            or
                                                                address_form
.equals ("Mrs.")
                                                                            or
                                                                address_form.equals ("Miss")
                                                                            or
                                                                address_form
.equals ("Ms.")
                                                                            or
                                                                address_form.equals ("Dr.")
                                  valid_first_name: first_name.length /= 0
                                  valid_last_name: last_name.length /= 0
                                  valid_address: address.length /= 0
                                  valid_city: city.length /= 0
                                  valid_state: state.length /= 0
                                  valid_zip: zip.length /= 0
                                  valid_country: conutry.length /= 0

 

add_registration (registrant_id: INTEGER;
                                         
quantity, discount_plan: STRING;
                                          p
reconf,
                                         
wet,
                                          conference,
                                          esummit,
                                          postconf: BOOLEAN)
                                  -- Add new registration.
                      require
                                  non_void_quantity: quantity /= Void
                                  non_void_discount_plan: discount_plan /= Void
                                  valid_discount_plan:
                                                                
discount_plan.equals ("Regular")
                                                                            or
                                                                 discount_plan
.equals ("Non-academic Authors")
                                                                            or
                                                                 discount_plan.equals ("Full-Time Students")
                                                                            or
                                                                
discount_plan.equals ("Full-Time Faculty Members")

Both features include a number of preconditions that must be met before they can be called.  For the purpose of the demo, the calls to these features are not protected and you can trigger a contract violation by e.g. entering an empty first name in the registration page. The demo is running in debug mode so that you can see the kind of information a contract violation generates.

The C# script then retrieves the return status and the error message, if any:

registered = registrar.last_operation_successful();
error_message = registrar.last_error_message();

If the calls were successful it then loads the confirmation page:

if( registered )
          Navigate( "Done.aspx" );

This page contains a link “See Report” to the report page where you can see the content of the tables. In this example the database is just an Eiffel hash table which is reinitialized every 20 entries.

Retrieving the content of the database

The report page, Report.aspx, contains two tables built with web forms that contain the current database entries. The first table lists all the registrants while the second table lists all the registrations. These tables are built from the information retrieved through the queries registrants_database and registrations_database of the class REGISTRAR.

     registrants_database: DATABASE [REGISTRANT, INTEGER]
                 -- Registrants database

     registrations_database: DATABASE [REGISTRATION, INTEGER]
                 -- Registrations database

DATABASE is a generic class with two generic parameters:

class
     DATABASE [G -> DATABASE_ITEM, H]

The first generic parameter is constrained to DATABASE_ITEM, meaning that for any generic derivation of DATABASE, the type actually used will conform to DATABASE_ITEM. This type applies to the items to be stored in the database.

The second generic parameter is unconstrained and defines the type of the keys associated with the items stored in the database. In the example, G is either REGISTRANT or REGISTRATION while H is INTEGER. DATABASE uses an Eiffel hash table to store the items; it would be very easy to extend the example using ADO+ to connect to a relational database such as SQL Server.

EXTENDING THE EXAMPLE

We have seen how easy it is to write a web service in Eiffel#, just inherit from WEB_SERVICE and all the public features will be exported to the web. We have also seen how you can write ASP+ pages to access such a web service.

The next step will enable us to access the web service not through the web but from another application.

Creating a proxy to the web service

The .NET SDK includes tools that automatically generate the C# sources for the proxy to any .NET web service.

The tool for the example is webserviceutil.exe, which takes as input the Service Description Language (SDL) page that describes the service. The SDL page is not something that you have to write: it can be automatically generated from the web service using the following URL:

You can then comple the resulting C# file into an assembly, accessible from Eiffel#.

Writing a Win Forms application in Eiffel#

The Eiffel# application that accesses the web service uses Win Formss to implement the GUI. Win Formss, as any other .NET library, is directly accessible via Eiffel#. ISE is developing a set of Eiffel# classes that will provide a standard encapsulation of all Win Formss types.

If you have .NET installed on your computer you can download the Eiffel# application here. You can then run it on your computer and see the information you enter in the form automatically sent to the web service.

The report page will show the entries you added on the application running on your machine. The debug files are included in the download, so that you can run the application in a debugger. The application is written in Eiffel# and, for demonstration purposes, also uses a component written in C#. In the same debugging session, you can step back and forth between the Eiffel# and C# parts of the application.

Writing your own client

If you have the .NET SDK installed on your machine, you can use webserviceutil.exe to write your own client to the Eiffel# web service in any language. The following command line will generate the C# sources for the web service proxy:

webserviceutil /c:proxy /pa:http://dotnet.eiffel.com/registrar.asmx?SDL

This will generate the file Registrar.cs that you can compile with the following command line:

csc /t:library /r:System.Web.Services.dll;System.Xml.Serialization.dll Registrar.cs

You will then have access to the assembly Registrar.dll that you can reuse in any .NET language to write your own client to the web service running on dotnet.eiffel.com.