Tips and Tricks

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
Tips and Tricks
Tuesday, February 21, 2012PrintSubscribe
Wrapping Long Action Text in Action Column

If an action group with the scope of “Action Column” has a few actions with long header text then you may end up with the following presentation of actions.  Action buttons will not wrap and will take a significant amount of real estate on the page.

image

You can correct this situation by creating a custom CSS stylesheet.

Select the project name on the start page of the web application generator and choose Develop project action. Your development environment will start up.

Select ~/App_Themes folder in your project and add a new CSS stylesheet to the folder that has the same name as the namespace of your web application. The default web application namespace is MyCompany.

Right-click the folder name, select Add New Item… option in the context menu and proceed to add a new Style Sheet item to your project. Change the name of the item to anything your want or leave the default value in the Name input box. Click Add button and the new CSS stylesheet file will become a part of your project.

image

Enter the following CSS rules to the file.

table.DataView tr .Cell.ActionColumn
{
    white-space:normal!important;
}

table.DataView tr .Cell.ActionColumn a span
{
    display:inline-block;
    white-space:normal;
}

table.DataView tr .Cell.ActionColumn a
{
    margin-top:2px;
    display:inline-block;
}

Save the file and bring up the browser window, hit Refresh button of your browser and the presentation of the Actions column will change. Individual actions will be displayed as multi-line boxes and will wrap to the next line when needed.

image

Saturday, October 22, 2011PrintSubscribe
Wizards and Status Bars

Code On Time web applications offer powerful methods of presenting your data with very little effort. In this tutorial we will show you how to create a wizard form view with the context sensitive status bar.

image

Form views editForm1 and createForm1 are included with each data controller in a generated Code On Time web application.  The first form, editForm1, is typically used to present a data row in “view” or “edit” mode. The second form, createForm1, provides user interface for “new” data rows.

The picture below shows an example of  editForm1 rendering an employee record in “view” and “edit” modes.

image

image

Each form view must have at least one category that binds data controller fields to a view. A binding of a field to a view is called a “data field”. Only one binding of a field to a view can exist in any type of view.

For purposes of this tutorial, create your own Northwind sample application as explained at /blog/2011/10/northwind-sample.html.

Multiple Data Field Categories in Form Views

A simple list of business object data fields presented in a form view works well in many situations. On the other hand, business requirements might call for a grouping of related fields and even conditional display of field groups based on the user input.

Consider the following screenshot depicting automatically generated createForm1 of Employees data controller.

image

Let’s break this form in four categories presenting “New Employee” fields, “Address”, “Miscellaneous” information, and an overall “Summary” of a new employee record.

Start the web application generator, select the name of your project, and click the Design button.

Select Employees data controller on All Controllers tab.

Activate Views tab and select view createForm1.

Activate Categories tab, shown below.

image

Rename New Employees category to New Employee and clear the description.

Add three more categories with the following properties:

Header Text Visibility Description
Address true Enter address of {FirstName} {LastName}.
Miscellaneous true Enter {FirstName} {LastName}'s phone number, birthday, any relevant notes.
Summary true

Please review the summary of the new record.
<div style="margin:8px; padding:8px; height:220px; overflow:auto; border:solid 1px silver">
Last Name: {LastName}<br/>
First Name: {FirstName}<br/>
Title: {Title}<br/>
Title Of Courtesy: {TitleOfCourtesy}<br/>
Birth Date: {BirthDate}<br/>
Hire Date: {HireDate}<br/>
Address: {Address}<br/>
City: {City}<br/>
Region: {Region}<br/>
Postal Code: {PostalCode}<br/>
Country: {Country}<br/>
Home Phone: {HomePhone}<br/>
Extension: {Extension}<br/>
Notes: {Notes}<br/>
Reports To: {ReportsTo}<br/>
Photo Path: {PhotoPath}<br/>
</div>

The list of categories in Designer will look as follows.

image

All data fields are presently bound to the New Employee category.

image

Let’s change that. Select view createForm1 in the path at the top of the Designer page and activate Categories tab.

Select Address category, activate Data Fields tab and add new data fields referencing Address, City, Region, PostalCode, and Country fields of the controller.

You will notice that the Designer automatically copies properties of the fields from New Employee category. Designer also removes the fields from New Employee category to ensure that there are no duplicate field references.

Now follow the same routine and add TitleOfCourtesy, BirthDate, HomePhone, Notes, ReportsTo, and PhotoPath to Miscellaneous category of view createForm1.

The new layout of fields of the view createForm1 is presented in the screen shot.

image

We also recommend that you select data fields Title and TitleOfCourtesy and set their Auto Complete Prefix Length property to “1”. This will provide an auto complete option for both fields.

Generate web application and start creating a new Employee record. As you enter values in FirstName and LastName fields, the descriptions of categories will change. Notice that John Doe is displayed in the category descriptions in the picture below.

image

Dynamic Category Descriptions

References to the names of fields enclosed in curly brackets are automatically replaced with field values as soon as the field value is changed, provided that the category visibility is dynamic.

Descriptions are automatically formatted with field value injection when you open a form view. If the category visibility is not dynamic then the values will not change even if the user is editing the record.

We have entered “true” expression in Visibility property of Address, Miscellaneous, and Summary categories. The expression is written in JavaScript and evaluated whenever data values are changed by user. This will ensure that the categories will be permanently visible, as true tends to evaluate to true.

The following picture shows collapsed Address and Miscellaneous categories and fully expanded New Employee and Summary categories. Note that Summary  category has no fields bound to it but displays dynamic content thanks to the expressions embedded in the category description.

image

Converting Form View to a Wizard

Our form is quite lengthy and warrants some refinement and simplification.

We will change the form to present one category at any given time and move between categories upon request. This style of presentation is often referred as a wizard. Data field categories in view createForm1 will become “pages” of the wizard.

If  a category is visible then two buttons Back and Next will be visible as well to allow advancement to the next step or return to previous step of data collection.

We will rename OK button to Finish and have it available on the Summary page of New Employee wizard only. Button Cancel will remain visible at all times.

“Status” Field

The upcoming release of EASE (Enterprise Application Services Engine) will be available in Unlimited edition of Code On Time. It has also brought some enhancements to the application framework for other editions.

Various elements of your application pay special attention to the presence of a field named “Status”.

The column named Status is frequently found in database tables of a typical line-of-business application. This column generally contains a short phrase or a number reflecting the business state of a data row representing a business object. For example, an Order can have a status of Draft, Open, Cancelled, or Ready to Ship.

Code On Time application framework assumes that field Status exists in all data controllers. Client library will automatically add a virtual Status field to any business object if the physical field is not found.

Visibility of Categories

Implementation of a wizard calls for some sort of status that can be used to determine the active “page” of the wizard.

We will use the virtual “Status” field to determine the visibility of a category and have the value of the field change when a user moves from one “page” of the wizard to another.

Change the Visibility expression of categories in createForm1 view as follows.

Category Visibility Expression (JavaScript)
New Employee [Status] == null || [Status] == 'Step 1'
Address [Status] == 'Step 2'
Miscellaneous [Status] == 'Step 3'
Summary [Status] == 'Step 4'

The screenshot of Project Designer shows categories with modified Visibility expression.

image

The initial value of virtual field “Status” is null, which will guarantee that only New Employee category is visible when a user starts creating a new record.

image

“Status” Action

If you were to implement an order management system with each order having a certain status then it is possible that you would have used a drop down list of available statuses to present the current status value.

Most of the time, change of the internal status of a business object results in a change to the user interface.

A status change is better implemented as a user interface action. For example, a buyer will set the order status to Submitted by pushing a Submit button. An employee in the shipping department will select a menu option Ship to change the order status to Shipped.

Code On Time web applications offer different action scopes that result in clickable links and button rendering in forms, on action bars, in action column, and grid context menu. We have introduced a new standard action Status that will change the value of a virtual or physical field with the name “Status” to the argument of the action.

Click Employees controller in the path at top of the page in the project designer and activate Action Groups tab.

Select action group ag2 with scope of Form. Activate Actions tab of the group.

Filter available actions by When Last Command Name property with value New. You will see definitions of two actions with command names Insert and Cancel. These actions are rendered as push buttons with captions OK and Cancel in the previous picture.

image

Add seven actions defined below.

# Command Name Command Argument Header Text Causes Validation When Last Command Name When Client Script
1. None   Back No New [Status] == null || [Status] == 'Step 1'
2. Status Step 2 Next Yes New [Status] == null || [Status] == 'Step 1'
3. Status Step 1 Back No New [Status] == 'Step 2'
4. Status Step 3 Next Yes New [Status] == 'Step 2'
5. Status Step 2 Back No New [Status] == 'Step 3'
6. Status Step 4 Next No New [Status] == 'Step 3'
7. Status Step 3 Back No New [Status] == 'Step 4'

Open the context menu of Insert action and choose Make Last.

image

Change its Header Text to “Finish and enter the following expression in When Client Script property.

[Status] == 'Step 4'

The list of actions will look as follows in the project designer.

image

Wizard in Action

Generate application and start entering a new Employee record.

User can cancel creation of a new record at any time by pushing Cancel button.

The first “page” of the wizard displays a disabled “Back” button representing the action with command name None. Action None is always displayed as disabled and provides a useful placeholder that gives the user interface consistency.

On this page, Status equals “Step 1”.

image

Press Next, and Status will change to “Step 2”.

image

Status equals “Step 3”.

image

Status equals “Step 4”.

image

Complex “When Client Script” Expressions

The configuration of navigational actions may seam to be quite a challenge at first. The truth is that there is not much value in a simple flow of wizard “pages” in our sample. A simple list of categories and ability of web pages to scroll do the job quite well.

In a real world your When Client Script expression will likely be more complex.

For example, one can imagine that different employee setup scenarios will be required, based on the employee job description or country.

You can add multiple Status actions activating various categories of the wizard with When Client Script expressions such as this.

[Status] == 'Step 4' && [Country] == 'USA' && [Title] =='Senior Manager'

The expression can manipulate any data field if you are referencing them in square brackets.

Status Bar

Business applications are designed to reflect complex processes of real life. Software developers and designers use the visual language of screens, menus, and various controls to approximate the business processes.

Naturally, the end users of your applications are true experts in their field and will evaluate any such approximation with a critical eye. It usually takes time for a user to understand the relationship of an application screen to an element of a real-world process.

Assigning a status to data representing a business process goes a long way towards making it easier for users to interact with your application.

The perfect example of a business process is an internet shopping cart. A seller has to collect enough information from a customer to ensure that the order is correctly placed, processed, and fulfilled. Internet customers are very impatient and will abandon their shopping cart if takes too long to complete the process or if the ordering process is confusing.

Sellers are “holding” the customer’s hand through the checkout by presenting information about the completed, current, and next steps that need to performed. Typically this is accomplished though a progress bar that indicates the current stage of the checkout process.

image

A progress bar gives customers a peace of mind and helps them better understand what is going on.

The same exact care must be exercised when programming any other business process.

Start web application generator and select your project. Click Design button and select Employees data controller on All Controllers tab.

Enter the following in the Status Bar property of the data controller and save the changes.

Employees.createForm1.Status: null
[New Employee] > Address > Miscellaneous > Final Review >

Status: Step 1
[New Employee] > Address > Miscellaneous > Final Review >

Status: Step 2
New Employee > [Address] > Miscellaneous > Final Review >

Status: Step 3
New Employee > Address > [Miscellaneous] > Final Review >

Employees.Status: Step 4
New Employee > Address > Miscellaneous > [Final Review] >

Generate your project and observe the status bar displayed just above the description of the view createForm1. This illustration shows the status bar detecting that value of the Status field is “Step 3”.

image

A status bar is defined by a collection of status values matched to the bar’s topology.

A status value is defined using one of the following methods:

  • Status: Value
  • ControllerName.Status: Value
  • ControllerName.ViewId.Status: Value

ControllerName and ViewId components of the status value are optional.

The second method is provided to support workflows of applications with EASE (Enterprise Application Services Engine). One workflow may define action groups, views, and virtual pages of multiple data controllers. A single status bar definition is defined per workflow.

Use the third method to create variations of status bars presented in different views. This method is also useful when there is no physical Status field.

If field Status in a given data row is empty then the status value is assumed to be null.

Status value is followed by status bar topology. Simply list logic definitions of the past, current and future statuses ending them with “greater than” character. The current logical status is wrapped in square brackets and separates past statuses from the future ones.

It is up to you to provide meaningful logical statuses. For example, our physical status values Step 1, Step 2, Step 3, and Step 4 are defined in status bar topology as New Employee, Address, Miscellaneous, and Final Review.

You can even make up your own logical statuses to help users establish a relationship of a visual presentation with the real world.

image

Note that Status Bar feature is available in Premium and Unlimited editions only.

Conclusion

Code On Time web applications offer first class high end features that require little or no programming. Wizards and Status Bars are a great example.

EASE workflows will make it possible creating user-specific views and pages enhanced with custom actions and status bars at runtime without changing the application.

Thursday, August 25, 2011PrintSubscribe
File Upload / Download (External Storage)

Our previous article has covered the subject of File Upload / Download. We have discussed storing and retrieving of files in binary database table columns. Web apps created with Code On Time can greatly benefit from automatic capturing of content type, file name, and length in the specially named table columns that compliment a binary column. This approach requires no programming and works great if the database is expected to store the binary content.

Recent innovations in data storage methods make it highly efficient and cost-effective to externalize binary content from the database. The database becomes much more compact and is easier to backup and manage.

Online storage systems such as Amazon S3 and Microsoft Azure Storage provide a robust and cheap storage for large data files. A web application can also benefit if the binary content does not leave the premises of the data center and is stored in Network Attached Storage.

Creating a Sample Web App

Let’s create a sample web application to illustrate the mechanism of externalizing binary data that remains associated with table rows in a database.

Start Code On Time, select “Create new web application task”, and choose Web Site Factory type for our web application.

Enter UploadDownload in Project Name and make sure to select the programming language that you are comfortable to use. Click Create button.

Leave the namespace and framework unchanged and click Next.

In this tutorial we will be using Microsoft SQL Server 2008. Other types of supported database will involve exactly the same implementation techniques.

Select SQL Server Data Provider and click the button located next to Connection String input field.

Enter the name of your local server or SQL Azure server and specify login credentials if needed.

Enter My Upload Download Demo in Database input field and click Create. Confirm that you want to create a database by press Yes button.

Select Northwind in Sample Tables drop down and click Install button. Wait for the installation script to finish. You will see a popup message indicating a successful installation.

image

The next step is optional. We suggest implementing a built-in membership system in your application. Click Add button shown in the picture. Wait for ASP.NET Membership installation script to finish.

image

Click OK button at the bottom of connection configuration screen to save the database connection string.

Click Next twice to reach Reporting section. Select “Enable dynamic and static reports in my application”.

Click Next button to get to Features page of the project wizard. Enter Upload / Download Demo (External) in Page Header box.

Continue clicking Next until you arrive to the summary page presenting a list of project data controllers. Proceed to generate a project by clicking Next. The web app will start in your default browser shortly thereafter.

Login using admin/admin123% user account. Select Categories tab to activate the list of product categories stored in the sample database.

image

Next we will change the structure of the database and create a virtual binary field that will have its content stored in the file system on your computer.

Preparing the Database

The following picture shows the structure of Categories table.

image

Table column Picture has been automatically processed by web app generator and implemented as a file upload field. The previous screen shot shows the thumbnails of the images stored in the database.

Our intent is to implement a virtual field that will coexist alongside the field Picture but will have its content stored outside of the application database. Think of it as if there in invisible column that looks exactly as Picture but is not actually present in the database table.

Let’s call this field ExternalDoc (short for External Document). The field implementation will require a few utility columns even though the binary field itself will not be present in the database .

These special utility columns must have their name start with the name of the binary column/field.  Execute the following query in SQL Management Studio or SQL Azure Database Manager against My Upload Download Demo database.

alter table dbo.Categories
add ExternalDocFileName nvarchar(150) null
go

alter table dbo.Categories
add ExternalDocLength integer null
go 

alter table dbo.Categories
add ExternalDocContentType nvarchar(150) null
go

The scrip will add three utility columns to the structure of Categories table to allow capturing and storing of the uploaded file name, its length, and content type.

This is how the table structure will look now.

image

Implementing ExternalDoc Field in the Project

Let’s incorporate the new columns and the corresponding virtual field in our project.

Bring forward the web app generator window and click on the project name.

Our database has changed but the code generator is not aware of that. Database metadata (tables, columns, indexes, etc.) are cached in the project files. Click Refresh button to refresh the metadata. The code generator will bring you straight to Data Controller summary page.

Click Start Designer button to activate Project Designer. Select Categories data controller on All Controllers tab.

Enter CategoriesBusinessRules in Handler input and click OK button.

Select Categories data controller one more time. Activate Fields tab and select New | New Field on the action bar.

Enter ExternalDoc as Name. Change the type of the field to Byte[]. Select check box  “Allow null values”.

Our field is virtual. We need to specify a SQL expression that will be evaluated by the database server when the field value is about to be retrieved. Select “The value of this field is computed at run-time by SQL expression” check box and enter NULL in SQL Formula text box.

Our field will store large binary objects (BLOB). This type of field requires special handling by the application. Select “Value is retrieved on demand” check box. On Demand Properties section will become visible in the designer.

Enter the name of the primary key column of the Categories table in the Source Fields input. The name of the column is CategoryID.

Your web application will also need a special construct that will help handling details of basic uploading/downloading just before/after you save the file to the external storage or have it retrieved.

Your application uses the term On Demand Handler to describe this construct in the source code. Any unique name will do. You can use the same on-demand handler name if a binary column is displayed in the views of other data controllers

We will call our handler CategoriesExtenalDoc, which combines the table name and the name of the virtual column. This is the default convention used for all on-demand handlers of your web application.

Set On Demand Style to Thumbnail to have a preview thumbnail displayed when the field is rendered.

Finally enter the field label as External Doc.

The project designer property page will look as follows. Click OK button to save the field.

image

Changing Visual Properties of ExternalDoc and Utility Fields

Select Data Fields tab to activate bindings of data controller fields to presentation views. Data fields allow controlling many presentation aspects of your application.

Let’s bind the virtual field External Doc to editForm1 and grid1 views.

The first binding will insert the field ExternalDoc in the view grid1. Select New | New Data Field on action bar. Also make sure to enter 15 in the columns property of the data field. This will limit the space taken by the field on automatically produced reports if you choose an option to print the list of categories when running the application.

image

Next bind our virtual field to the view editForm1. Make sure to select a category for the field. Category selection is mandatory if the view is a form.

image

Save the second binding and enter External in the Quick Find area on the action bar.

Delete all bindings of utility fields to the view createForm1. Uploading of binary content is only supported for existing records.  Select each binding and click Delete button or select Delete option from the row context menu in the list of bindings.

Change Text Mode property of all utility fields from Text to Static. This will make the field values read-only from the user standpoint but will allow application to make changes to the values when the files are uploaded. Optionally assign n0 as a data format string for both bindings of ExternalDocLength to improve readability of the field value for very long files.

The list of ExternalDocXXX  bindings will looks as follows when you finish making changes.

image

Click Exit button to exit the project designer and proceed to generate the web application.

Navigate to Categories tab. The screen will look similar to the screen shot.

image

Select any category and observe that we now have the virtual field and utility fields presented at the bottom of the form view.

image

Implementing Upload / Download Business Rules

If you try to upload a file in External Doc field then you will see that utility fields are acquiring the properties of the file but the actual data is no saved and an error message is displayed stating “Error: failed to upload categories external doc. Invalid column name \u0027ExternalDoc\u0027.”

You can edit the record again and this time push Clear button instead of performing an upload.

image

The same error message will be displayed at the top of the page but the utility fields will get cleared.

You have probably guessed already that we need to get involved in the process and save the file when the browser has finished uploading the file.

Bring up the code generator and click develop link under Actions column next to the name of your project. Visual Studio or Visual Web Developer will start.

Press Ctrl+Alt+L to activate Solution Explorer and find CategoriesBusinessRules.cs(vb) in the project tree. Double-click the file to open the text editor.

image

The business rules class will require three methods to support an externally stored binary content.

  • The first method will need to intercept the event of uploading, prevent the application framework from trying to save the file, and persist the file contents to the file system.
     
    We accomplish that by implementing a method with ControllerAction attribute. The parameters of the attribute indicate the data controller, the name of the action, and the name of the field.
     
    Method arguments include CategoryID and ExternalDoc stream to let us interact with the uploaded content. You can also include optional arguments with names ExternalDocFileName, ExternalDocLength, and ExternalDocContentType if you need this information to correctly externalize the file.
     
    Our implementation is simply storing the file in the predefined folder in My Documents. We use the category ID and generic “.bin” extension to persist the file
  • The second method will be invoked when the binary content needs to be streamed to the client browser.  Make sure to prevent the default processing logic from being executed by calling PreventDefault method.
     
    Again we are using the category ID to find the location of the previously saved file and this time we write the contents of the file to the stream passed as externalDoc argument.  The application framework will automatically take care of supplying the file name and content type to the browser.
     
  • The purpose of the third method is t0 let the application know if there is a value in a given binary column of a given row.
     
    If an external file exists then the method will update the value of the binary field with the primary key of the record. The client library will use this ID to communicate with the server components of your application and to display a thumbnail if needed.
     
    If the binary field is empty then the method will return the same category ID converted to a string and preceded by the word null in lower case and the symbol “|”. For example, if the category ID is 7491 then the value stored in ExternalDoc will be null|7491
     
    We are not actually probing if the file exists in the designated folder. Instead we make an assumption that if ExternalDocFileName field value is not blank then the file has been previously uploaded without errors. Your own implementation may do whatever it takes including possible communicating with external systems to verify that the file does exist.

Note that method names are absolutely arbitrary and play no role in application execution. ControllerAction and RowBuilder attributes turn these methods in the special sauce of your business rules class.

Before you proceed any further make sure to fire up Windows Explorer. Go to Documents folder and create My External Doc Files folder referenced in the implementation below. The folder must exist by the time you try uploading the content.

C#:

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

namespace MyCompany.Rules
{
    public partial class CategoriesBusinessRules : MyCompany.Data.BusinessRules
    {
        [ControllerAction("Categories", "UploadFile", "ExternalDoc")]
        protected void SaveExternalDocToFileSystem(int categoryID, Stream externalDoc)
        {
            PreventDefault();
            string fileName = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
                String.Format(@"My External Doc Files\{0}.bin", categoryID));
            Stream output = File.Create(fileName);
            externalDoc.CopyTo(output);
            output.Close();
        }

        [ControllerAction("Categories", "DownloadFile", "ExternalDoc")]
        protected void LoadExternalDocFromFileSystem(int categoryID, Stream externalDoc)
        {
            PreventDefault();
            string fileName = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
                String.Format(@"My External Doc Files\{0}.bin", categoryID));
            Stream input = File.OpenRead(fileName);
            input.CopyTo(externalDoc);
            input.Close();
        }

        [RowBuilder("Categories", RowKind.Existing)]
        public void VerifyPresenceOfExternalFile()
        {
            int categoryID = Convert.ToInt32(SelectFieldValue("CategoryID"));
            string externalDocFileName = Convert.ToString(
                SelectFieldValue("ExternalDocFileName"));
            // update ExternalDoc field to reflect existence of a file
            if (!String.IsNullOrEmpty(externalDocFileName))
                UpdateFieldValue("ExternalDoc", categoryID);
            else
                UpdateFieldValue("ExternalDoc", String.Format("null|{0}", categoryID));
        }
    }
}

Visual Basic:

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

Namespace MyCompany.Rules

    Partial Public Class CategoriesBusinessRules
        Inherits MyCompany.Data.BusinessRules

        <ControllerAction("Categories", "UploadFile", "ExternalDoc")> _
        Protected Sub SaveExternalDocToFileSystem(ByVal categoryID As Int32, _
                                                  ByRef externalDoc As Stream)
            PreventDefault()
            Dim fileName As String = Path.Combine( _
                Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), _
                String.Format("My External Doc Files\\{0}.bin", categoryID))
            Dim output As Stream = File.Create(fileName)
            externalDoc.CopyTo(output)
            output.Close()
        End Sub

        <ControllerAction("Categories", "DownloadFile", "ExternalDoc")> _
        Protected Sub LoadExternalDocFromFileSystem(ByVal categoryID As Int32, _
                                                  ByRef externalDoc As Stream)
            PreventDefault()
            Dim fileName As String = Path.Combine( _
                Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), _
                String.Format("My External Doc Files\\{0}.bin", categoryID))
            Dim input As Stream = File.OpenRead(fileName)
            input.CopyTo(externalDoc)
            input.Close()
        End Sub

        <RowBuilder("Categories", RowKind.Existing)> _
        Protected Sub VerifyPresenceOfExternalFile()
            Dim categoryId As Integer = Convert.ToInt32(SelectFieldValue("CategoryID"))
            Dim externalDocFileName As String = Convert.ToString( _
                SelectFieldValue("ExternalDocFileName"))
            ' update ExternalDoc field to reflect existence of a file
            If Not String.IsNullOrEmpty(externalDocFileName) Then
                UpdateFieldValue("ExternalDoc", categoryId)
            Else
                UpdateFieldValue("ExternalDoc", String.Format("null|{0}", categoryId))
            End If

        End Sub
    End Class
End Namespace

Upload a few files to the ExternalDoc column. You should be able to see the file thumbnail if the upload was successful or the file type displayed in a white box if the file is not an image.

image

Here is how the content of the folder that we use to store the uploaded content may look like.

image

Your externalized content will also print on reports. For example, select Categories tab and choose Report|PDF Document option on the action bar.

image

A prompt to download a PDF document will be displayed. Open the file and you should be able to see the uploaded content on it.

image

Conclusion

Code On Time offers an excellent mechanism of implementing storage of a binary content outside of the database.

We expect that future updates to the framework will allow file uploading not only when users are editing existing records but also when a new record is being created.

Upcoming releases will also support code-free uploading of binary content through built-in business rules to external file system and Microsoft Azure Storage.

Project Azure Factory available with Code On Time will support popular annotation feature as well.

Continue to Access Control Rules