Blog: Posts from March, 2012

Labels
AJAX(112) App Studio(7) Apple(1) Application Builder(245) Application Factory(207) ASP.NET(95) ASP.NET 3.5(45) ASP.NET Code Generator(72) ASP.NET Membership(28) Azure(18) Barcode(2) Barcodes(3) BLOB(18) Business Rules(1) Business Rules/Logic(140) BYOD(13) Caching(2) Calendar(5) Charts(29) Cloud(14) Cloud On Time(2) Cloud On Time for Windows 7(2) Code Generator(54) Collaboration(11) command line(1) Conflict Detection(1) Content Management System(12) COT Tools for Excel(26) CRUD(1) Custom Actions(1) Data Aquarium Framework(122) Data Sheet(9) Data Sources(22) Database Lookups(50) Deployment(22) Designer(177) Device(1) DotNetNuke(12) EASE(20) Email(6) Features(101) Firebird(1) Form Builder(14) Globalization and Localization(6) How To(1) Hypermedia(2) Inline Editing(1) Installation(5) JavaScript(20) Kiosk(1) Low Code(3) Mac(1) Many-To-Many(4) Maps(6) Master/Detail(36) Microservices(4) Mobile(63) Mode Builder(3) Model Builder(3) MySQL(10) Native Apps(5) News(18) OAuth(9) OAuth Scopes(1) OAuth2(13) Offline(20) Offline Apps(4) Offline Sync(5) Oracle(11) PKCE(2) Postgre SQL(1) PostgreSQL(2) PWA(2) QR codes(2) Rapid Application Development(5) Reading Pane(2) Release Notes(183) Reports(48) REST(29) RESTful(29) RESTful Workshop(15) RFID tags(1) SaaS(7) Security(81) SharePoint(12) SPA(6) SQL Anywhere(3) SQL Server(26) SSO(1) Stored Procedure(4) Teamwork(15) Tips and Tricks(87) Tools for Excel(2) Touch UI(93) Transactions(5) Tutorials(183) Universal Windows Platform(3) User Interface(338) Video Tutorial(37) Web 2.0(100) Web App Generator(101) Web Application Generator(607) Web Form Builder(40) Web.Config(9) Workflow(28)
Archive
Blog
Posts from March, 2012
Saturday, March 3, 2012PrintSubscribe
Virtualization of Data Controllers

Code On Time applications rely on data controller descriptors to determine the expected application behavior at runtime.  A typical data controller XML file packages information about fields, data views, action state machine, and optional linked business rules.

The application framework interprets this data. The server components of the framework parse the XML files and prepare internal structures to process requests to select, update, insert, and delete data. The AJAX client portion of the application framework renders data views and available state machine actions in web browsers.

From the application framework perspective, the physical location of data controller files is unknown. Business rules classes are not aware and don’t care about the location of data controllers.

Web Site Factory applications have all data controller files stored in ~/Controllers folder.

Data controllers in a Web Site Factory application

All other project types are implemented as a Visual Studio solution with several projects. The core application framework and data controller files are located in the Class Library project of the solution. The data controllers are marked as resources. The entire text of XML files is compiled into the DLL of the class library.

The next screen shot shows a class library in a SharePoint Factory project.

Data controllers in SharePoint Factory application

The application framework tries the following sequence while loading a data controller in memory.

  1. Ask the Controller class implementation to return the data controller content stream by name. The default implementation returns nothing.
  2. Try loading the data controller from the binary resources of the application. All project types other than Web Site Factory will return the resource stream of a data controller.
  3. Try loading the data controller file from ~/Controllers folder. This applies to Web Site Factory only.

Virtualization of a data controller is a replacement of an entire data controller or some parts of it at runtime.

If you want to store an application data controller outside of application code base then add the following partial class definition. Your class will complement the default application framework implementation of controller functionality.

C#:

using System;
using System.IO;

namespace MyCompany.Data
{

    public partial class Controller
    {
        public override Stream GetDataControllerStream(string controller)
        {
            string fileName = String.Format(@"c:\clients\Acme\{0}.xml", controller);
            if (File.Exists(fileName))
                return new MemoryStream(File.ReadAllBytes(fileName));
            return DefaultDataControllerStream;
        }
    }
}

Visual Basic:

Imports Microsoft.VisualBasic
Imports System.IO

Namespace MyCompany.Data

    Partial Public Class Controller
        Public Overrides Function GetDataControllerStream(controller As String) As Stream
            Dim fileName = String.Format("c:\\clients\Acme\{0}.xml", controller)
            If (File.Exists(fileName)) Then
                Return New MemoryStream(File.ReadAllBytes(fileName))
            End If
            Return DefaultDataControllerStream
        End Function
    End Class

End Namespace

The sample above loads a customized version of the data controller Customers if the file is found in C:\Clients\Acme folder.  Otherwise the application will proceed to load the default data controller defined in the application.

If you have a multi-tenant web application and desire to offer customized data controllers for a specific group of users then consider using this method of customization. You can inspect the incoming web request or session variables and identify the user group. Load the correct data controller based on that info.

We recommend using this method of data controller virtualization when you need to branch the data controller definition.

You can also customize the contents of a data controller after the file has been loaded by the application framework in the memory and is ready to be used in the web request processing. This method of virtualization involves changing a portion of a data controller definition while leaving the rest of it intact.

Implement either a dedicated business rules class for your controller or create a shared business rules class to have all application data controllers share a common functionality.

Let’s create a partial data controller virtualization sample with shared business rules.

Select your project on the start page of the web application generator and choose Settings, click Business Logic Layer, and select Shared Business Rules. Check the box that enables shared business rules. Click Finish and generate your project.

The class implementing shared business rules will either be in ~/App_Code/Rules folder of your project or in the ~/Rules folder of the solution class library.

Shared Business Rules class implementation in a Web Site Factory project

Make sure that any existing custom business rules inherit from the class [YourNamespace].Rules.SharedBusinessRules. Replace [YourNamespace] with the namespace of your project.

The following implementation of shared business rules alters data controller Customers based on the current user interface culture.

If the culture is “en-US” then the Region field label is changed to State; the Postal Code field label is changed to Zip Code; and all instances of Country data field are “hidden” from the application users.

Notice that the changes do not effect the actual file that contains the definition of Customers data controller.

C#:

using System;
using System.Data;
using System.Collections.Generic;
using System.Linq;
using MyCompany.Data;
using System.Xml.XPath;
using System.Xml;

namespace MyCompany.Rules
{
    public partial class SharedBusinessRules : MyCompany.Data.BusinessRules
    {
        public override bool SupportsVirtualization(string controllerName)
        {
            if (controllerName == "Customers")
                return true;
            else
                return false;
        }

        public override void VirtualizeController(string controllerName,
            XPathNavigator navigator, XmlNamespaceManager resolver)
        {
            if (controllerName == "Customers")
            {
                if (System.Threading.Thread.CurrentThread.CurrentUICulture.Name == "en-US")
                {
                    // Change "Region" label to "State" for users from the USA
                    XPathNavigator regionLabel = navigator.SelectSingleNode(
                        "/c:dataController/c:fields/c:field[@name='Region']/@label",
                        resolver);
                    if (regionLabel != null)
                        regionLabel.SetValue("State");
                    // Change "Postal Code" label to "Zip Code" for users from the USA
                    XPathNavigator postalCodeLabel = navigator.SelectSingleNode(
                        "/c:dataController/c:fields/c:field[@name='PostalCode']/@label",
                        resolver);
                    if (postalCodeLabel != null)
                        postalCodeLabel.SetValue("Zip Code");
                    // Mark all data fields named "Country" as "hidden"
                    XPathNodeIterator countryFieldIterator = navigator.Select(
                        "//c:dataField[@fieldName='Country']",
                        resolver);
                    while (countryFieldIterator.MoveNext())
                    {
                        XPathNavigator dataFieldNav = countryFieldIterator.Current;
                        if (dataFieldNav.MoveToAttribute("hidden", String.Empty))
                            dataFieldNav.SetValue("true");
                        else
                            dataFieldNav.CreateAttribute(
                                String.Empty, "hidden", String.Empty, "true");
                    }
                }
            }
        }
    }
}

Visual Basic:

Imports MyCompany.Data
Imports System
Imports System.Collections.Generic
Imports System.Data
Imports System.Linq
Imports System.Xml.XPath
Imports System.Xml

Namespace MyCompany.Rules

    Partial Public Class SharedBusinessRules
        Inherits MyCompany.Data.BusinessRules

        Public Overrides Function SupportsVirtualization(controllerName As String) As Boolean
            If controllerName = "Customers" Then
                Return True
            Else
                Return False
            End If

        End Function

        Public Overrides Sub VirtualizeController(controllerName As String,
                                                  navigator As XPathNavigator,
                                                  resolver As XmlNamespaceManager)
            If (controllerName = "Customers") Then
                If (System.Threading.Thread.CurrentThread.CurrentUICulture.Name = "en-US") Then
                    ' Change "Region" label to "State" for users from the USA
                    Dim regionLabel As XPathNavigator = navigator.SelectSingleNode(
                        "/c:dataController/c:fields/c:field[@name='Region']/@label",
                        resolver)
                    If (Not regionLabel Is Nothing) Then
                        regionLabel.SetValue("State")
                    End If
                    ' Change "Postal Code" label to "Zip Code" for users from the USA
                    Dim postalCodeLabel As XPathNavigator = navigator.SelectSingleNode(
                        "/c:dataController/c:fields/c:field[@name='PostalCode']/@label",
                        resolver)
                    If (Not postalCodeLabel Is Nothing) Then
                        postalCodeLabel.SetValue("Zip Code")
                    End If
                    ' Mark all data fields named "Country" as "hidden"
                    Dim countryFieldIterator As XPathNodeIterator = navigator.Select(
                        "//c:dataField[@fieldName='Country']",
                        resolver)
                    While (countryFieldIterator.MoveNext())
                        Dim dataFieldNav = countryFieldIterator.Current
                        If (dataFieldNav.MoveToAttribute("hidden", String.Empty)) Then
                            dataFieldNav.SetValue("true")
                        Else
                            dataFieldNav.CreateAttribute(
                                String.Empty, "hidden", String.Empty, "true")
                        End If
                    End While
                End If
            End If
        End Sub

    End Class
End Namespace

This is the screen shot that shows the effect of the shared business rules. User interface is altered at runtime when English (United States) localization is selected.

'Customers' data controller affected by virtualization

This screen shot shows localized version of the web application without dynamic customization of user interface.

'Customers' data controller not affected by virtualization

The portion of the data controller definition that is being changed at runtime is shown next.

<fields>
  . . . . .
  <field name="Region" type="String" label="Region" />
  <field name="PostalCode" type="String" label="Postal Code" />
  <field name="Country" type="String" label="Country" />
  . . . . . 
</fields>
<views>
  <view id="grid1" type="Grid" commandId="command1" label="Customers">
    <headerText>$DefaultGridViewDescription</headerText>
    <dataFields>
      . . . . .
      <dataField fieldName="Country" columns="15" />
      . . . . .
    </dataFields>
  </view>
  <view id="editForm1" type="Form" commandId="command1" label="Review Customers">
    <headerText>$DefaultEditViewDescription</headerText>
    <categories>
      <category id="c1" headerText="Customers" newColumn="true">
        <description><![CDATA[$DefaultEditDescription]]></description>
        <dataFields>
          . . . . .
          <dataField fieldName="Country" columns="15" />
          . . . . .
        </dataFields>
      </category>
    </categories>
  </view>
  <view id="createForm1" type="Form" commandId="command1" label="New Customers">
    <headerText>$DefaultCreateViewDescription</headerText>
    <categories>
      <category id="c1" headerText="New Customers" newColumn="true">
        <description><![CDATA[$DefaultNewDescription]]></description>
        <dataFields>
          . . . . .
          <dataField fieldName="Country" columns="15" />
          . . . . .
        </dataFields>
      </category>
    </categories>
  </view>
</views>
Friday, March 2, 2012PrintSubscribe
Understanding the Project

The Northwind database is a typical mail order management system.

The core of the database is composed of two tables: Orders and Order Details. Each sale is recorded in the Orders table. Items purchased are recorded in the Order Details table.

Orders and Order Details tables from Northwind database

Orders table has references to Customers, Employees, and ShippersOrders table also contains information about Order Date, Required Date, Shipped Date, Freight Amount, and the shipping details.

Orders table and foreign keys in Northwind database

Order Details table refers to Products and indirectly references Categories and Suppliers. Order Details will contain a few fields taken from all of these foreign key relationships.

image

This is the baseline version of the orders screen, created automatically by the web application generator.

Baseline version of Orders screen

We will create a custom version of this form, to make the order entry process as friendly as possible.

Friday, March 2, 2012PrintSubscribe
Adding the Order Form Page

Let’s create a new page in the application. Switch back to the web application generator, click on the “OrderForm” project name, and press Design.

The design environment allows you to change logical aspects of the project, and consists of two primary elements. The Project Designer, on the left side of the screen, displays a list of objects or properties of the selected object. On the right side of the screen, the Project Explorer allows you to browse and manipulate pages, controllers, and user controls. At the top of the screen, there are several buttons. The Generate button regenerates the web application and displays it in a built-in browser window, called Preview. The Browse button will regenerate the application and open it in your default browser. The Exit button goes back to the web application generator.

Code On Time Project Designer and Explorer

Let’s add a new page called Order Form. In the Designer’s action bar, press New | New Page. Give this page the following settings:

Property Value
Name OrderForm
Index 1005
Title Order Form
Path Order Form
Description This is the order management form.
Style Miscellaneous
About This Page This is the order management form.
Roles (blank)

Press OK to save the page.

Order Form Page in Code On Time web application generator

We’ll need to add a container to this page to store the data views. In the Explorer, right-click on the Order Form page, and press New Container. None of the default properties need to be changed, so just press OK to save the container.

New container in Code On Time Designer

Let’s add an Orders data view. Right-click on the newly created container in the Explorer, and press New Data View. Enter the following settings for the data view.

Property Values
Controller Orders
View grid1
Show Details in List Mode False

New Orders Data View in Code On Time Designer

Press OK to save the data view.

Let’s add another data view to show Order Details. Right-click on the container again and press New Data View. Enter the following settings:

Property Values
Controller Order Details
View grid1
Show View Description False
Show View Selector False
Show Pagers False
Page Size 300
Show Modal Forms True
Filter Source dv100
Filter Field OrderID
Auto-Hide Self

New Order Details Data View

Press OK to save the data view. Press Generate in the top left corner, and wait for the Preview window to open. Log in, and you will see Order Form page on the menu bar and the site map.

Code On Time Designer Preview of generated web application

Click on the Order Form link, and you will see a list of orders.

Order Form page in Code On Time Designer Preview

Select an order, and the order details will appear underneath.

Order and Order Details in Code On Time web application Preview