Data Aquarium Framework

Labels
AJAX(112) App Studio(9) 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(178) 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(184) 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(3) 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
Data Aquarium Framework
Wednesday, March 23, 2011PrintSubscribe
Using $external() Function in Filter Expressions

Code On Time applications support custom functions in view filter expressions. The new function $external() has been introduced in the latest web application generator update. You can use this function to access values in the URLs of the pages and values of fields listed in Context Fields property of lookup fields.

Consider the EmployeeTerritories table from the Northwind sample database.

Any territory can be associated with an employee only once due to the primary key constraint.

image

Here is the screen shot of the form that allows creating new EmployeeTerritories records.

image

The employee last name is already selected. If a user clicks on (select) link in Territory Description field then an entire set of 53 territories will be presented.

image

Let’s make sure the the territories that are already assigned to a selected employee are not available in the list. For example, if we assign a new territory to employee Davolio then the last territory on the first page  in the list of territories in the screen shot above, Wilton from Eastern region, is not listed along with a couple of other territories.

Start the code generator, click on the project name, click Design button, and select EmployeeTerritories data controller, click Edit, and enter EmployeeID in the input labeled Context Fields under Dynamic Properties section. Save changes.

From now on, the application will pass the value of the selected EmployeeID to the lookup view Territories.grid1 attached to field TerritoryID. If the lookup view has the field EmployeeID then the rows of Territories will be automatically filtered to match the value of EmployeeID selected in the new record.

Well, there is no field named EmployeeID in the table Territories.

Let’s create a filter expression for grid1 view in data controller Territories. Click on Home link in the bread crumbs of the Designer at the top of the page. Select Territories data controller. Activate Views tab and choose grid1. Click Edit button.

Enter the following in Filter Expression property:

$external('EmployeeID') is null or not TerritoryID in (
   select "TerritoryID" from "EmployeeTerritories"
   where "EmployeeID" = $external('EmployeeID')
)

image

Save changes, exit Designer and generate the application, navigate to Employee Territories page, select Davolio in the Employee Last Name field, and click (select) in Territory Description input. You will see a lookup window with 50 records in it. The three records that are already assigned to Ms. Davolio are not presented in the list.

image

Navigate to dedicated Territories page and observe that 53 records are still available there. If you enter a URL parameter EmployeeID with value of “1” in the address bar of your browser and hit Enter key then 50 records will be displayed as well. Your URL may look as the one below.

http://localhost:36745/Pages/Territories.aspx?EmployeeID=1

The filter expression assigned to grid1 view does not work if there is no external filter passed in the page URL or in the Context Fields of the lookup view. This is guaranteed by the first comparison in the expression.

$external('EmployeeID') is null or . . .

The second part of the filter expression will test the TerritoryID to ensure that it is not matched to any territories that are already present in EmployeeTerritories and uses $external(‘EmployeeID’) to further limit the scope of test.

Notice the use of double quotations around field and table names. Please use the appropriate symbol that works with your database server to ensure that application will not be trying to resolve the name against the dictionary of fields of the data controller.

Here is physical SQL statement executed by your application. Note that parameters @p0 and @p1 will be replaced with the value of the EmployeeID passed in a URL or as a context field.

with page_cte__ as (
select
row_number() over (order by "Territories"."TerritoryID") as row_number__
,"Territories"."TerritoryID" "TerritoryID"
,"Territories"."TerritoryDescription" "TerritoryDescription"
,"Territories"."RegionID" "RegionID"
,"Region"."RegionDescription" "RegionRegionDescription"
from
"dbo"."Territories" "Territories"
    left join "dbo"."Region" "Region" on "Territories"."RegionID" = "Region"."RegionID"

where
(
(((@p0) is null or not "Territories"."TerritoryID" in (
   select "TerritoryID" from "EmployeeTerritories"
   where "EmployeeID" = (@p1)
))))
)
select * from page_cte__ where row_number__ > @PageRangeFirstRowNumber and row_number__ <= @PageRangeLastRowNumber
Monday, December 6, 2010PrintSubscribe
Conversion and Validation of User Input

Code On Time applications offer powerful methods of converting and validating field values entered by user. The unique business rules model allows elegant abstraction of the business logic required to validate and convert user values from the user interface elements responsible for presentation.

Conversion

Let’s consider a simple conversion scenario that requires automatic conversion of a customer contact name to upper case as soon as user has finished typing. We will use Northwind database for this sample.

Generate your project, activate designer and select All Controllers tab. Select Customers data controller, switch to Fields tab and select the ContactName field. Select the check box “The value of the field is calculated by a business rule expression”.

Enter the following Code Formula if your programming language is C#:

!String.IsNullOrEmpty(contactName) ? contactName.ToUpper() : String.Empty

Visual Basic programmers should use the following instead:

IIf(Not (String.IsNullOrEmpty(contactName)), contactName.ToUpper(), String.Empty)

Next scroll the designer page down and enter ContactName in the Context Fields property. This is very important step that will ensure that the business rules formula is executed as soon as user leaves the field.

Here is the screen short of Designer that shows the partial configuration of the ContactName field.

image

Try running the program and observe the contact name convert to upper case as soon as you enter a value.

image

Note that this works in all views without you doing anything about it. You change the model and all views automatically engage the business rules.

Here is the actual file that is automatically produced to implement this calculation. This code is placed into ~/App_Code/Rules/Customers.Generated.cs file

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

namespace MyCompany.Rules
{
    public partial class CustomersBusinessRules : 
        MyCompany.Data.BusinessRules
    {
        
        [ControllerAction("Customers", "Calculate", "ContactName")]
        public void CalculateCustomers(string customerID, 
            string companyName, string contactName, string contactTitle, 
            string address, string city, string region, 
            string postalCode, string country, string phone, string fax)
        {
            UpdateFieldValue("ContactName", 
                !String.IsNullOrEmpty(contactName) ? 
                    contactName.ToUpper() : 
                    String.Empty);
        }
    }
}

Note that the class is partial. You can implement you own class with the same name and offer a method that performs a more complex conversion using database or any other resources required for successful conversion calculation. Make sure not to change the file directly since the changes will be lost during next code generation. Instead use Designer to change the Code Formula of the corresponding field.

Here is the Visual Basic version of the same automatically generated method.

Imports MyCompany.Data
Imports System
Imports System.Collections.Generic
Imports System.Data
Imports System.Linq
Imports System.Text.RegularExpressions
Imports System.Web

Namespace MyCompany.Rules
    
    Partial Public Class CustomersBusinessRules
        Inherits MyCompany.Data.BusinessRules
        
        <ControllerAction("Customers", "Calculate", "ContactName")> _
        Public Sub CalculateCustomers(ByVal customerID As String, _
                                      ByVal companyName As String, _
                                      ByVal contactName As String, _
                                      ByVal contactTitle As String, _
                                      ByVal address As String, _
                                      ByVal city As String, _
                                      ByVal region As String, _
                                      ByVal postalCode As String, _
                                      ByVal country As String, _
                                      ByVal phone As String, _
                                      ByVal fax As String)
            UpdateFieldValue("ContactName", _
                             IIf(Not (String.IsNullOrEmpty(contactName)), _
                                 contactName.ToUpper(), _
                                 String.Empty))
        End Sub
    End Class
End Namespace

Accessing field values

Many scenarios of validation may be narrowed performed as silent conversion using the method described above.  The business rules methods offer every single field of the field dictionary of the data controller and you can use values of all fields to produce the value.

You can also use methods SelectFieldValue and SelectFieldValueObject and retrieve a field value required for conversion/validation.

The first method will return the untyped object representing the value of the field or external URL parameter. It is your responsibility to convert the value to use it in a calculation. For example,

Convert.ToString(SelectFieldValue("ContactName")).ToUpper()

The second method returns only the value objects that correspond to the fields of the data controller. The advantage of using SelectFieldValueObject is the ability to access the “Old” and “New” values and availablility of  Modified  property that tells if the field value has changed.

Convert.ToString(SelectFieldValueObject("ContactName").NewValue).ToUpper())

Validation

Validation is usually performed just before the standard logic of Code On Time application is about to be executed. User has completed input and initiated a command that will result in INSERT, UPDATE, or DELETE statement execution.

Let’s consider another example. Let’s prevent posting of invalid values to the Order Details table.

Select your project on the start page of the code generator, activate designer, find Order Details data controller, select the data controller and edit the controller to have OrderDetailsBusinessRules as business rules Handler. See the screen shot below.

image

Save the changes, exit the designer and generate the project.

Open the project in Visual Studio using File | Open Web Site option and double click the ~/App_Code/Rules/OrderDetaulsBusinessRules.cs file to open it in the editor. Enter the following method if your project language is C#.

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

namespace MyCompany.Rules
{
    public partial class OrderDetailsBusinessRules : MyCompany.Data.BusinessRules
    {
        [ControllerAction("OrderDetails", "Update", ActionPhase.Before)]
        [ControllerAction("OrderDetails", "Insert", ActionPhase.Before)]
        public void ValidateInput(float? discount, short? quanity, decimal? price)
        {
            if (quanity.HasValue && quanity > 10)
                if (!Controller.UserIsInRole("Administrators"))
                    throw new Exception("You are not authorized to sell more then 10 items.");
            if (discount.HasValue && discount.Value > 0.15)
                throw new Exception("The discount cannot be more than 15%.");
            if (!price.HasValue || price.Value == 0.0m)
            {
                Result.ExecuteOnClient("this._focus('UnitPrice', 'The price must be greater than zero.')");
                throw new Exception("Please validate the entered unit price.");
            }
        }

    }
}

Here is the Visual Basic version.

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

Namespace MyCompany.Rules

    Partial Public Class OrderDetailsBusinessRules
        Inherits MyCompany.Data.BusinessRules

        <ControllerAction("OrderDetails", "Update", ActionPhase.Before)> _
        <ControllerAction("OrderDetails", "Insert", ActionPhase.Before)> _
        Public Sub ValidateInput(ByVal discount As Nullable(Of Single), _
                                 ByVal quantity As Nullable(Of Short), _
                                 ByVal unitPrice As Nullable(Of Decimal))
            If (quantity.HasValue AndAlso quantity > 10) Then
                If (Not Controller.UserIsInRole("Administrators")) Then
                    Throw New Exception("You are not authorized to sell more then 10 items.")
                End If
            End If
            If (discount.HasValue AndAlso discount.Value > 0.15) Then
                Throw New Exception("The discount cannot be more than 15%.")
            End If
            If (Not (unitPrice.HasValue) Or (unitPrice.HasValue AndAlso unitPrice.Value = 0)) Then
                Result.ExecuteOnClient("this._focus('UnitPrice', 'The price must be greater than zero.')")
                Throw New Exception("Please validate the entered unit price.")
            End If

        End Sub

    End Class
End Namespace

Notice that the order of the arguments in the validation method is absolutely irrelevant. The same method is handling both Insert and Update actions. You can implement a dedicated method to handle each situation differently. You can use a Shared Business Rules method to create a handler for multiple data controllers.

Runt he program, select Customers | Order Details page and try entering the order details records while leaving blank Discount, Unit Price, and Quantity fields. The Unit Price validation will detect the problem and will result in the following. The message bar at the top of the page indicates that there is a problem. The inline error message explains what the problem in with more details.

image

The future versions of the code generation library will offer an alternative method of indicating the error using the business rules method to eliminate the need to use a snippet of JavaScript to report the validation error.

Monday, August 2, 2010PrintSubscribe
Disabling Action on Specific Pages

Q.  How can I disable the New and Delete button on any particular page.

A.

Consider using WhenHRef property of data controller actions. Open the data controller definition in Designer, select Actions tab and activate the action. The action property WhenHRef must represent a valid regular expression.

For example, If you want to use New and Delete actions on pages ~/Pages/Page1.aspx and ~/Pages/Page2.aspx only then the following syntax will work.

Page1|Page2

The assumption is made that the same data controller is present on both pages. You can use any valid regular expression to create a URL test.  The expression will be tested against the URL in the web browser address box. A more precise and complex expression may look as the one below:

/Pages/Page(1|2)\.aspx

If there are other pages that use the same data controller and you want to prevent New and Delete actions from displaying on Page1 and Page2 but have them available on all other pages then we recommend to use an extension to regular expression syntax. Simply put "false:" in front of your expression:

false:Page1|Page2

The prefix in the example above is not a part of regular expression definition and is used to make it easier writing negative regular expression then would have been required otherwise.

Continue to Permalinks