Development / Under the Hood

  Working with the Baseline

Table of Contents
Development / Under the HoodPrint||
Working with the Baseline

Code On Time web application generator automatically creates a baseline application straight from your database. For example, if you follow instructions explaining creation of the Northwind sample then a complete web application will be displayed in your web browser without much effort on your part.

'Products' page of the automatically geneated Web Site Factory project

Application generator enumerates all database tables and views specified by the developer to compose the pages and data controllers.

One page is created for each database table or view. The generator will use foreign key relationships that exist in the database to create the baseline navigation menu.

One data controller is composed for each database table. A typical data controller describes available fields, user interface presentation views, and an action state machine in XML format. The framework of the generated application knows how to interpret the contents of the data controller files.

A typical baseline application page defines the data views linked to data controllers.

All pages are stored in the single file Application.Baseline.xml.

All data controllers are stored in the single file Controllers.Baseline.xml.

Both files can be found in the root folder of your project.

This is the snippet from the application baseline that defines the Products page.

<page name="Products" title="Products" description="View Products" index="1080" 
      path="Categories | Products" generate="always" style="Generic">
  <containers>
    <container id="container1" flow="NewRow" />
    <container id="container2" flow="NewRow" style="padding-top:8px" />
  </containers>
  <dataViews>
    <dataView id="view1" controller="Products" view="grid1" showInSummary="true" 
              container="container1" activator="None" text="" />
    <dataView id="view2" controller="OrderDetails" view="grid1" text="Order Details" 
              filterSource="view1" container="container2" filterFields="ProductID" 
              activator="Tab" autoHide="Container" pageSize="5" showModalForms="true" />
  </dataViews>
  <controls />
  <about>This page allows products management.</about>
</page>

Next snippet shows abbreviated definition of the Products data controller. This data controller is referenced by the page definition above.

<dataController name="Products" conflictDetection="overwriteChanges" label="Products" 
                nativeSchema="dbo" nativeTableName="Products">
  <commands>
    <command id="command1" type="Text">
      <text> select
        "Products"."ProductID" "ProductID"
        ,"Products"."ProductName" "ProductName"
        ,"Products"."SupplierID" "SupplierID"
        ,"Supplier"."CompanyName" "SupplierCompanyName"
        ,"Products"."CategoryID" "CategoryID"
        ,"Category"."CategoryName" "CategoryCategoryName"
        ,"Products"."QuantityPerUnit" "QuantityPerUnit"
        ,"Products"."UnitPrice" "UnitPrice"
        ,"Products"."UnitsInStock" "UnitsInStock"
        ,"Products"."UnitsOnOrder" "UnitsOnOrder"
        ,"Products"."ReorderLevel" "ReorderLevel"
        ,"Products"."Discontinued" "Discontinued"
        from "dbo"."Products" "Products"
        left join "dbo"."Suppliers" "Supplier" on "Products"."SupplierID" = "Supplier"."SupplierID"
        left join "dbo"."Categories" "Category" on "Products"."CategoryID" = "Category"."CategoryID"
    </text>
    </command>
    <command id="ProductIDIdentityCommand" type="Text" event="Inserted">
      <text>select @@identity</text>
      <output>
        <fieldOutput fieldName="ProductID" />
      </output>
    </command>
  </commands>
  <fields>
    <field name="ProductID" type="Int32" allowNulls="false" isPrimaryKey="true" 
           label="Product#" readOnly="true" />
    <field name="ProductName" type="String" allowNulls="false" 
           label="Product Name" showInSummary="true" />
    <field name="SupplierID" type="Int32" label="Supplier#" showInSummary="true">
      <items style="Lookup" dataController="Suppliers" newDataView="createForm1" />
    </field>
    <field name="SupplierCompanyName" type="String" readOnly="true" 
           label="Supplier Company Name" />
    <field name="CategoryID" type="Int32" label="Category#" showInSummary="true">
      <items style="Lookup" dataController="Categories" newDataView="createForm1" />
    </field>
    <field name="CategoryCategoryName" type="String" readOnly="true" label="Category Name" />
    <field name="QuantityPerUnit" type="String" 
           label="Quantity Per Unit" showInSummary="true" />
    <field name="UnitPrice" type="Decimal" default="((0))" label="
           Unit Price" showInSummary="true" />
    <field name="UnitsInStock" type="Int16" default="((0))" label="Units In Stock" />
    <field name="UnitsOnOrder" type="Int16" default="((0))" label="Units On Order" />
    <field name="ReorderLevel" type="Int16" default="((0))" label="Reorder Level" />
    <field name="Discontinued" type="Boolean" allowNulls="false" 
           default="((0))" label="Discontinued" />
  </fields>
  <views>
    <view id="grid1" type="Grid" commandId="command1" label="Products">
      <headerText>$DefaultGridViewDescription</headerText>
      <dataFields>
        <dataField fieldName="ProductName" columns="40" />
        <dataField fieldName="SupplierID" aliasFieldName="SupplierCompanyName" />
        <dataField fieldName="CategoryID" aliasFieldName="CategoryCategoryName" />
        <dataField fieldName="QuantityPerUnit" columns="20" />
        <dataField fieldName="UnitPrice" dataFormatString="c" columns="15" />
        <dataField fieldName="UnitsInStock" columns="15" />
        <dataField fieldName="UnitsOnOrder" columns="15" />
        <dataField fieldName="ReorderLevel" columns="15" />
        <dataField fieldName="Discontinued" />
        </dataField </view>
    <view id="editForm1" type="Form" commandId="command1" label="Review Products"> . . . . .
    </view>
    <view id="createForm1" type="Form" commandId="command1" label="New Products"> . . . . .
    </view>
  </views>
  <actions>
    <actionGroup id="ag1" scope="Grid">
      <action id="a1" commandName="Select" commandArgument="editForm1" />
      <action id="a2" commandName="Edit" />
      <action id="a3" commandName="Delete" />
      <action id="a6" />
      <action id="a7" commandName="Duplicate" commandArgument="createForm1" />
      <action id="a8" commandName="New" commandArgument="grid1" />
    </actionGroup> . . . . .
  </actions>
</dataController>

Notice that the page Products is also referencing the OrderDetails data controller, which is not shown.

The Products data controller is referencing Categories and Suppliers data controllers in the definitions of CategoryID and SupplierID fields.

Multiple pages can reference the same data controller, which significantly simplifies application maintenance. Change one data controller and the changes will propagate to all application pages that make use of it.

The application generator creates Application.Baseline.xml and Controllers.Baseline.xml files the first time the application is generated.

Web application generator also creates Application.Cache.xml and Controllers.Cache.xml files that initially represent the exact copies of the baseline files.

'Baseline' and 'Cache' versions of applications and data controllers are stored in the root folder of the generated web application.

The project generation scripts will read the Application.Cache.xml file and create physical ASP.NET Web Forms for each page. The web forms make use of the standard ASP.NET components and some custom components that come with the application framework.  The complete source code of the entire application framework is included in the generated code base.
The project generation scripts will also split the Controllers.Cache.xml file into multiple data controller files. Web Site Factory projects have all data controllers stored in the ~/Controllers folder. Other types of projects have the data controllers stored in the Controllers folder of the application class library. The definitions of data controllers found in the application folders are shorter and do not have some elements and attributes required by the code generator and Project Designer.

Developers can use the Project Designer to modify the application pages and data controllers.

Properties of 'Products'.'SupplierID' field displayed in Project Designer

The log of changes is automatically recorded in Application.Log.xml and Controllers.Log.xml files. The Project Designer simultaneously updates the contents of Application.Cache.xml and Controllers.Cache.xml files.
The basic relationship of the files is as follows:

Application.Baseline.xml + Application.Log.xml = Application.Cache.xml
Controllers.Baseline.xml + Controllers.Log.xml = Controllers.Cache.xml

If the contents of the database have changed then the data controller baseline of the project can be refreshed. The application generator will delete the “cache” versions of the files and re-apply the contents of the logs to the baselines to produce the new “cache” versions.

Refreshed data controllers of the project will be replaced in the baseline or removed from it.

New data controllers will be added to the baseline for the new database tables and views included in the project. The application generator will also compose a new host page for each new data controller and include it under New Pages navigation option of the site menu.

The Project Designer offers a logical view of the objects. Developers can browse the generated applications and visually inspect and change the properties of various application elements.

Experienced developers may find that the data controller XML files are fairly easy to understand. Data controller files are physical components of your project required at runtime. If you change the data controllers then the changes will be reflected in the application instantly.

You have to remember that the data controller files are the “byproduct” of the application generation process. Direct changes to the data controller will be lost if the project is regenerated.

The same applies to the application pages.

Avoid changing the data controllers and pages in the text editor and either use  the Project Designer or modify the data controller and application baseline instead.

Notice that you can define custom controls that are placed on the application pages. The custom controls are generated as ASP.NET User Controls and can be configured to be produced the “first time only”. Such controls can in fact be modified in the text editor without loosing the changes.

We recommend using Visual Studio or Visual Web Developer when working with the baseline files. Code completion available in these tools will speed up the development process and will highlight the markup errors if any.

Code completion is available in Visual Studio when editing the Application and Controllers baselines.

If you make changes to the baseline files then the changes will not take effect until your refresh the project.

Start the application generator, click on the project name and choose the Refresh action.

Do not select any data controllers in the list. Simply click the Refresh button at the bottom of the dialog to initiate the refresh process. Proceed to generate the application to see the changes in action.

Project 'Refresh' Dialog

Select the data controllers only if you want them to be replaced by the application generator. The application generator will compose a brand new temporary baseline and replace any selected data controllers with their new versions from the temporary file.

You can also create copies of data controllers directly in the baseline.

For example, open the Controllers.Baseline.xml file in the editor and copy the Products controller to the clipboard. Paste the controller right next to the original and remove “nativeSchema” and “nativeTableName” attributes from “dataController” element of the copy.

<dataController name="MyProducts" conflictDetection="overwriteChanges" label="My Products" >
  . . . . .
</dataController>
<dataController name="Products" conflictDetection="overwriteChanges" label="Products" 
                nativeSchema="dbo" nativeTableName="Products" 
                xmlns="urn:schemas-codeontime-com:data-aquarium">
  . . . . .
</dataController>

The application generator will treat MyProducts data controller as a custom data controller. Custom data controller is the controller that cannot be matched to any of the tables or views included in your application. The matching is done by comparing nativeSchema and nativeTableName attributes of controllers. Removal of these attributes turns the copy into a custom data controller.

Once again select the project Refresh action. The new controller will be in the list. It has a green icon indicating its special status.

'Refresh' dialog with the custom data controller 'MyProducts'

Do not select the custom controller, click the Refresh button to complete the refresh. If you select the data controller then the refresh process will simply remove it from the baseline.

Activate the Project Designer to see the properties of the custom data controller.  The next screen shot shows the custom data controller MyProducts structure in the Project Explorer. The original data controller Products is highlighted in the hierarchy.

Structure of the custom data controller 'MyProducts' displayed in the Project Explorer. The original controller 'Products' is highlighted in the hierarchy.

Use the custom data controller as you would use any other data controller of your application.

The application and data controller baselines will coexist with the changes done in the Project Designer. Keep in mind that your baseline definitions are combined with the logged modifications produced in the Designer. The final combined result is stored in the “cache” files. The “cache” files are the foundation of the actual application generated by Code On Time.

Individual Baseline Controllers

An alternative way of defining baseline controllers is to create a separate controller file. The name of this file must end with “.baseline.xml”. When refreshing the project, the app generator will scan for files matching the name and inject controllers defined in this file into the baseline. If the name of the controller matches one in the baseline, then the baseline controller will be replaced.