Blog: Posts from April, 2009

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(8) OAuth Scopes(1) OAuth2(11) Offline(20) Offline Apps(4) Offline Sync(5) Oracle(10) PKCE(2) PostgreSQL(2) PWA(2) QR codes(2) Rapid Application Development(5) Reading Pane(2) Release Notes(179) Reports(48) REST(29) RESTful(29) RESTful Workshop(15) RFID tags(1) SaaS(7) Security(80) 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 April, 2009
Friday, April 17, 2009PrintSubscribe
Creating Projects Outside “My Documents”

Code OnTime Generator will automatically generate projects to My Documents folder. You will find your projects code broken down by project type name under root  [My Documents]\Code OnTime.

image

This might introduce a problem with running your code under ASP.NET Development Server if your network policy has mapped this folder to a network drive.

Code OnTime Generator is built as an open code generation platform and allows free alterations to the code generation script.

For example, you can redirect the project output folder for Data Aquarium projects to you local folder C:\Data\MyProjects by modifying [My Documents]\Code OnTime\Projects\Data Aquarium\CodeOnTime.Projects.xml code generation script.

You can download the code generator script for Data Aquarium premium project at http://dev.codeontime.com/CodeOnTime.Project.zip. This script is current as of the date of publication of this article.

Open [My Documents]\Code OnTime\Projects\Data Aquarium\CodeOnTime.Projects.xml in your favorite XML editor and scroll all the way to the bottom.

        ........
        <load path="DataAquarium.Project.xml">
            <if test="a:project/a:webServer/@run='true'">
                <execute fileName="$CommonProgramFiles\microsoft shared\DevServer\9.0\WebDev.WebServer.EXE" arguments="/port:{a:project/a:webServer/@port} /path:&quot;$ProjectPath&quot; /vpath:&quot;/$ProjectName&quot;" mode="nowait"/>
                <execute fileName="http://localhost:{a:project/a:webServer/@port}/$ProjectName/default.aspx" arguments="-new" mode="nowait"/>
            </if>
        </load>
    </build>
    <actions>
        <action name="browse" toolTip="View &quot;{0}&quot; in a web browser.">
            <load path="DataAquarium.Project.xml">
                <execute fileName="$CommonProgramFiles\microsoft shared\DevServer\9.0\WebDev.WebServer.EXE" arguments="/port:{a:project/a:webServer/@port} /path:&quot;$ProjectPath&quot; /vpath:&quot;/$ProjectName&quot;" mode="nowait"/>
                <execute fileName="http://localhost:{a:project/a:webServer/@port}/$ProjectName/default.aspx" arguments="-new" mode="nowait"/>
            </load>
        </action>
    </actions>
</project>

Replace this code with the following:

    <load path="$ProjectPath">
        <variable name="MyProjectPath" select="'C:\data\MyProjects'"/>
        <forEach select="//file">
            <copy input="$ProjectPath\{@path}" output="$MyProjectPath\{@path}"/>
        </forEach>
    </load>
    <load path="DataAquarium.Project.xml">
        <if test="a:project/a:webServer/@run='true'">
            <execute fileName="$CommonProgramFiles\microsoft shared\DevServer\9.0\WebDev.WebServer.EXE" arguments="/port:{a:project/a:webServer/@port} /path:&quot;$MyProjectPath&quot; /vpath:&quot;/$ProjectName&quot;" mode="nowait"/>
            <execute fileName="http://localhost:{a:project/a:webServer/@port}/$ProjectName/default.aspx" arguments="-new" mode="nowait"/>
        </if>
    </load>
</build>
<actions>
    <action name="browse" toolTip="View &quot;{0}&quot; in a web browser.">
        <load path="DataAquarium.Project.xml">
            <variable name="MyProjectPath" select="'C:\data\MyProjects'"/>
            <execute fileName="$CommonProgramFiles\microsoft shared\DevServer\9.0\WebDev.WebServer.EXE" arguments="/port:{a:project/a:webServer/@port} /path:&quot;$MyProjectPath&quot; /vpath:&quot;/$ProjectName&quot;" mode="nowait"/>
            <execute fileName="http://localhost:{a:project/a:webServer/@port}/$ProjectName/default.aspx" arguments="-new" mode="nowait"/>
        </load>
    </action>
</actions>

The script in introducing a new variable $MyProjectPath, which is set to c:\data\MyProjects.

The variable is used to copy the generated code from [My Documents] location to the one specified by the variable and then ASP.NET Development Server is directed to use this location when executing the project.

Tuesday, April 14, 2009PrintSubscribe
Tracking User Actions

Tracking of user activities is a common requirement for many business applications. Data Aquarium Framework support Microsoft ASP.NET Membership via an advanced user management and login/logout user interface components. You can quickly create business rules to track user actions.

Sample Application

Generate a Data Aquarium project with the membership option enabled. Here is a typical screen shot of a Northwind database sample after the user with the name user has signed in.

image

Task 1

You want to keep a journal of user activities. The built-in .NET diagnostics facility will play a role of a journal where we will be keeping all activity records.

Solution

Create a business rules class Class1 and create method OrdersAfterUpdate as shown below. Link the business rules class to ~/Controllers/Orders.xml data controller as explained here.

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MyCompany.Data;

public class Class1 : BusinessRules
{
    [ControllerAction("Orders", "Update", ActionPhase.After)]
    protected void OrdersAfterUpdate(int orderId, FieldValue shipAddress)
    {
        System.Diagnostics.Debug.WriteLine(String.Format(
            "Order #{0} has been updated by '{1}' on {2}", 
            orderId, Context.User.Identity.Name, DateTime.Now));
        if (shipAddress.Modified) 
            System.Diagnostics.Debug.WriteLine(String.Format(
                 "Address has changed from '{0}' to '{1}.",
                 shipAddress.OldValue, shipAddress.NewValue));
    }
}

VB

Imports Microsoft.VisualBasic
Imports MyCompany.Data

Public Class Class1
    Inherits BusinessRules

    <ControllerAction("Orders", "Update", ActionPhase.After)> _
    Protected Sub OrdersAfterUpdate(ByVal orderId As Integer, _
                                    ByVal shipAddress As FieldValue)
        System.Diagnostics.Debug.WriteLine(String.Format( _
            "Order #{0} has been updated by '{1}' on {2}", _
            orderId, Context.User.Identity.Name, DateTime.Now))
        If (shipAddress.Modified) Then
            System.Diagnostics.Debug.WriteLine(String.Format( _
                 "Address has changed from '{0}' to '{1}.", _
                 shipAddress.OldValue, shipAddress.NewValue))
        End If
    End Sub

End Class

Open application in a web browser and select Orders data controller from the drop down in the top left corner. Start editing any order in the grid or form view and make sure to change Ship Address field. This field is the last visible field in the screen shot.

image

Hit OK button and the business method rule will intercept the action as soon as a successful database update has been completed. The first line of code will report the order ID and the user’s identity. The second line of code will detect the change in the address field.

image

Property Context provides business rules developers with the same Request, Response, User, Application, Session, and Server properties that are available  to web form developers.

The first two properties shall not be used since they provide information related to a current web service request and cannot be used to influence the user interface presentation.

Use the other properties as you if you were writing a typical web form.

Replace System.Diagnostics.Debug with a business object that is designed to keep track of user activities in a permanent data store such as a database table.

Task 2

All records in a database of orders must be marked with a reference to a user. User information will be utilized to filter data and for data analysis and reporting purposes.

Solution

Alter table [Northwind][.dbo].[Orders] to include new field UserName by executing the following SQL statement.

alter table Orders
add UserName varchar(50)
go

Modify command command1 in the data controller ~/Controllers/Orders.xml to select the new field twice. Once the field is selected under its own name and the other time we are selecting this very field under alias UserNameReadOnly. The reason for that is explained later.

        <command id="command1" type="Text">
            <text>
                <![CDATA[
select
    "Orders"."OrderID" "OrderID"
    ....................
    ,"Orders"."UserName" "UserName"
    ,"Orders"."UserName" "UserNameReadOnly"
from "dbo"."Orders" "Orders"
  ................
]]>
            </text>
        </command>

Add two field definitions for UserName instances in SQL statement to the list of data controller fields.

<fields>
    ...........
    <field name="UserName" type="String" label="User Name"/>
    <field name="UserNameReadOnly" type="String" label="User Name" readOnly="true"/>
</fields>

Let’s add the new read-only version of the field and a hidden version of the field to the list of data fields of views grid1 and editForm1.

<dataField fieldName="UserNameReadOnly"/>
<dataField fieldName="UserName" hidden="true"/>

Modify view createForm1 to include field UserName as a single hidden field.

<dataField fieldName="UserName" hidden="true"/>

We will silently assign a user name when a new record is created to the data controller field UserName. The captured value will be displayed when users review existing records but will be drawn from UserNameReadOnly for display purposes instead.

Add the following business rule to class Class1.

C#

[ControllerAction("Orders", "Update", ActionPhase.Before)]
[ControllerAction("Orders", "Insert", ActionPhase.Before)]
protected void OrdersBeforeUpdate(FieldValue userName)
{
    userName.NewValue = Context.User.Identity.Name;
    userName.Modified = true;
}

VB

    <ControllerAction("Orders", "Update", ActionPhase.Before)> _
    <ControllerAction("Orders", "After", ActionPhase.Before)> _
    Protected Sub OrdersBeforeUpdate(ByVal userName As FieldValue)
        userName.NewValue = Context.User.Identity.Name
        userName.Modified = True
    End Sub

This method will be automatically invoked whenever an order is about to be updated or inserted into database.

The first line will assign name of the currently logged-in user to the UserName field.

The second line will indicate that the field has actually changed. The framework is using Modified property of FieldValue instances to determine if a field shall be included in automatically generated SQL statement to update or insert a record.

You can also do an update on your own without relying on the framework. The best place for that sort of updates is in business rules methods with ActionPhase.After specifies as a parameter of ControllerAction attribute.

Here is how the grid of orders will look if you update a few records. The right-most column is displaying the name of the user.

image

The two fields UserName and UserNameReadOnly are required since read-only fields are never transferred to the server from the client web page. Hidden fields are never displayed but always travel from the client to the server and back. By introducing two versions of the same field we overcome this limitation imposed by the framework’s optimization logic.

Conclusion

Business rules in Data Aquarium Framework provide an excellent place to universally track user activities.

Saturday, April 11, 2009PrintSubscribe
Filtering And Business Rules

Data Aquarium Framework offers a unique approach to creating reusable business rules and logic for ASP.NET applications. Today we will explore filtering with business rules.

All code samples are built for a Data Aquarium application generated from Northwind database.

Task 1

You want to enhance the customer lookup capability of Northwind application to display only USA customers when users are editing orders in a grid view and have a UK customer list when users are editing orders in form view. This should not affect any other views that are presenting customers.

Solution

Create new class Class1 and add property Country as shown in example below.

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MyCompany.Data;

public class Class1 : BusinessRules
{
    public Class1()
    {
    }

    [RowFilter("Customers", "grid1", "Country")]
    public string Country
    {
        get
        {
            RowFilter.Canceled = String.IsNullOrEmpty(
                RowFilter.LookupContextController);
            if (RowFilter.LookupContextController == "Orders" && 
                    RowFilter.LookupContextView == "editForm1")
                return "UK";
            else
                return "USA";
        }
    }

}

VB

Imports Microsoft.VisualBasic
Imports MyCompany.Data

Public Class Class1
    Inherits BusinessRules

    <RowFilter("Customers", "grid1", "Country")> _
    Protected ReadOnly Property Country() As String
        Get
            RowFilter.Canceled = _
                String.IsNullOrEmpty(RowFilter.LookupContextController)
            If RowFilter.LookupContextController = "Orders" AndAlso _
                    RowFilter.LookupContextView = "editForm1" Then
                Return "UK"
            Else
                Return "USA"
            End If

        End Get
    End Property

End Class

This class is inherited from MyCompany.Data.BusinessRules base. 

Property Country is adorned with RowFilter attribute. This attribute will force the framework to evaluate the property whenever grid1 view of data controller Customers is expected to present data. The value of the property will be applied as a server-side filter.

The filtering property Country is notifying the framework that its value shall be ignored when property LookupContextController of RowFilter is null. This can be accomplished by assigning boolean value False to RowFilter.Canceled.

Row filter is constructed only once for each data page request received from the client. The framework will reset the cancellation flag of row filter prior to evaluating each business rules property matched to the requesting data controller view. If evaluation of the property has resulted in cancellation then property value is ignored. Otherwise the value is inserted as a parameterized SQL expression in the WHERE clause of SELECT statement constructed by the framework.

The third argument of RowFilter attribute  applied to Country property specifies the name of the field that must be filtered. This is useful if the property of the business rule is named differently than the actual field defined in the data controller view. It is redundant in our example.

Class BusinessRules features RowFilter property that gives you access to Controller, View, LookupContextController, LookupContextView, and LookupContextFieldName that are useful to determine if and how the filter shall be applied.  Lookup context properties are informing you if the data has been requested by the lookup field. You can examine lookup context field name to apply a server-side filter to the data that might be helpful if the same database lookup data is used to provide lookup values to more than one table in your database. If the data is requested by a standalone data view then you will find that all of the lookup context properties are equal to null.

Multiple RowFilter attributes can be applied to the same property.

Now you have to link the new business rules to Customers data controller defined in ~/Controllers/Customers.xml. This is done by adding attribute handler as shown here.

<dataController name="Customers" 
    conflictDetection="overwriteChanges" 
    label="Customers" xmlns="urn:schemas-codeontime-com:data-aquarium" 
    handler="Class1">
    ......

Run your application or navigate to the online demo at http://dev.codeontime.com/demo/filteringrules/?controller=Orders

Start editing any order in the grid view of orders.

image

Click on the link with the customer name.

image

Customer lookup window will display 13 records of customers from USA.

image

Press Escape key and click on any other link in Customer Company Name column. Click Edit button to start editing record in the form view editForm1.

image

Click on the lookup link in Customer Company Name field. Notice that only UK customers are presented.

image

Let’s see if our business rules have affected the global list of customers. Navigate to http://dev.codeontime.com/demo/filteringrules?controller=Customers. About ninety customer records shall be displayed. We have used RowFilter.Canceled property to prevent filtering when the data is no requested in the context of the lookup field.

image 

Task 2

You want to limit the list of employees to those born between 1/1/1950 and 11/1/1960.

Solution

Continue modifying our class and add BirthDate and BirthDate2 properties.

C#

[RowFilter("Employees", "grid1", "BirthDate", 
    RowFilterOperation.GreaterThanOrEqual)]
public DateTime BirthDate
{
    get
    {
        return new DateTime(1950, 1, 1);
    }
}

[RowFilter("Employees", "grid1", "BirthDate", 
    RowFilterOperation.LessThanOrEqual)]
public DateTime BirthDate2
{
    get
    {
        return new DateTime(1960, 1, 1);
    }
}

VB

<RowFilter("Employees", "grid1", "BirthDate", _
            RowFilterOperation.GreaterThanOrEqual)> _
            Protected ReadOnly Property BirthDate() As DateTime
     Get
         Return New DateTime(1950, 1, 1)
     End Get
End Property

<RowFilter("Employees", "grid1", "BirthDate", _
            RowFilterOperation.LessThanOrEqual)> _
           Protected ReadOnly Property BirthDate2() As DateTime
    Get
        Return New DateTime(1960, 1, 1)
    End Get
End Property

Link Class1 to ~/Controllers/Employees.xml data controller in the same fashion as we did for Customers data controller. Properties BirthDate and BirthDate2 are creating a range filter for employee field BirthDate.

You can see filtering by BirthDate in action at http://dev.codeontime.com/demo/filteringrules/?controller=Employees.

This filter is consistently applied whenever employee information is presented in data views.

Task 3

You want to filter data based on ASP.NET session variable.

Solution

Business rules have property Context that provide access to standard Request, Response, Session, Cache, and Application objects available in ASP.NET web forms. If you have a value stored in the session variable then you can easily apply its value as a filter.

<RowFilter("Customers", "grid1", "Country")> _
Protected ReadOnly Property CountryFilter() As String
    Get
        Return Context.Session("Country")
    End Get
End Property

Task 4

You want to filter data for a certain user roles.

Solution

The following code will inspect user role if a current user is not a member of Admin role then only customers from Finland are presented. Administrator’s view is not limited by a filter, which is accomplishing by cancelling filtering caused by CountryFilter property.

<RowFilter("Customers", "grid1", "Country")> _
Protected ReadOnly Property CountryFilter() As String
    Get
        If Context.User.IsInRole("Admin") Then
            RowFilter.Canceled = True
            Return String.Empty
        Else
            Return "Finland"
        End If
    End Get
End Property

Remember that if multiple filter properties are applicable to a give data page request then each filtering property must cancel row filter on its own.

Conclusion

Business rules in Data Aquarium Framework web applications allow efficient data filtering logic that is reused throughout your application. Subscribe to premium projects and start being productive today.

Continue to Present What You Want