"Wisej Pubs Demo App", an application for beginners

by Gabriele del Giovine, translated by Julie Hirt

4.1 Introduction

In this chapter, we explore the structure of an application for beginners that presents concepts and functional characteristics typical of line-of-business (LOB) applications. The application includes login management, access to databases, and the corresponding presentation of data, as well as user interface management functions with search and reporting mechanisms. "Wisej Pubs Demo App" constitutes a simple starting point in code structure and at the same time introduces the necessary development concepts to tackle the world of web-based LOB. You can find the online demo here: https://wisejdemo.delgiovine.it/WisejPubsDemoApp, while the original source code can be found at https://github.com/gdelgiovine/WisejPubsDemoApp. A forked copy of the source code which has been improved in order to make it easier to run on a new machine can be found here: https://github.com/JulieHirt/WisejPubsDemoApp.

4.2 The structure of the application

The application implements the management of a slightly modified version of the sample database used in Microsoft SQL Server in its early days, the famous DB Pubs, which is the database of a book distribution organization. The application uses a mix of the Web Application template and the Web Desktop Application template. In terms of the role distribution patterns/methods, such as the popular MVC (Model, View, Controller) pattern, this application, due to the need for conceptual simplicity, uses a much simplified approach that involves a Model and ViewControllers (the classic forms of Windows Forms). In reality, there is nothing preventing you from organizing your projects according to preferred patterns, and Wisej.NET, unlike many typical development environments for LOB applications, does not restrict the developer to rigid architectural models.

Regarding data management, I employ a basic type of object-relational mapping (ORM) that I have written myself, which almost completely hides the programmer from the challenges of database access and management. This ORM, called BasicDAL, integrates with Wisej.NET in data presentation functions, as well as in search and reporting functions by integrating "SAP Crystal Reports for Visual Studio" (required to run the application). BasicDAL and the extensions for Wisej 3.0 are always available at https://github.com/gdelgiovine. I chose to use this ORM precisely because it is extremely concise and relatively simple compared to more powerful tools, which tend to be much more complex.

Below are some screenshots of the running application.

Below is the structure of the Visual Studio project as shown in the "Solution Explorer" section.

The "Default.json" file contains the full name of the method for starting the application- WisejPubsDemoApp.Program.Main - which indicates a namespace of WisejPubsDemoApp containing the Program class and the Main() method.

{
"url": "Default.html",
"startup": "WisejPubsDemoApp.Program.Main, WisejPubsDemoApp"
}

To see the code that runs when the application starts, we can open the "Program.cs" file, which contains the Main() method:

In the Main() method, we find the creation of an object of the frmLogin class and the invocation of its Show() method. It's worth noting that the prefix "frm" is used to identify all the Form objects that will be used in the application to manage user interaction.

4.4 The frmLogin object

The frmLogin object is an instance of the Wisej.Web.Form class. It implements the functionality that prompts the user to enter a username and password, verifies their correctness, and, in case of success, passes control of the flow to another object, this time of type Wisej.Web.Desktop. Let's take a look at the interesting elements of frmLogin.

NOTE: Wisej.Web.Form objects correspond to the "Empty Window" template.

When viewed in the designer, you can see that the frmLogin object is a Form with two textboxes and a button.

Shown below is the code for the frmLogin class:

//frmLogin.cs
using System;
using Wisej.Web;

namespace WisejPubsDemoApp
{
    public partial class frmLogin : Form
    {
        public AppConfig appConfig = new AppConfig();
        System.Collections.Generic.List<WebAppUser> _WebAppUsers;
        public frmLogin()
        {
            InitializeComponent();
            // create users list - this is only for demo purpose NEVER use in a real application
            _WebAppUsers = new System.Collections.Generic.List<WebAppUser>();
            _WebAppUsers.Add(new WebAppUser () { UserName = "User1" , Password = "Password1" });
            _WebAppUsers.Add(new WebAppUser() { UserName = "User2", Password = "Password2" });
            _WebAppUsers.Add(new WebAppUser() { UserName = "User3", Password = "Password3" });
        }
        private void btn_Login_Click(object sender, EventArgs e)
        {
            if (!IsValidLogin(ref this.appConfig.CurrentWebAppUser))
            {
                MessageBox.Show("UserName or Password not valid!");
            }
            else
            {
                PubsDesktop pubsDesktop = new PubsDesktop();
                pubsDesktop.appConfig = this.appConfig;
                pubsDesktop.Show();
                this.Close();
            }
        }
        private bool IsValidLogin(ref WebAppUser LoggedInWebAppUser)
        {
            LoggedInWebAppUser = new WebAppUser() ;
            WebAppUser UserToCheck = new WebAppUser();
            UserToCheck.UserName = this.txt_UserName.Text.Trim().ToLower();
            UserToCheck.Password = this.txt_Password.Text;
            bool UserOk = false;
            foreach (WebAppUser _WebAppUser in _WebAppUsers)
            {
                if (_WebAppUser.UserName.ToLower()  == UserToCheck .UserName)
                {
                    if (_WebAppUser.Password == UserToCheck.Password)
                    {
                        LoggedInWebAppUser.UserName  = _WebAppUser.UserName;
                        LoggedInWebAppUser.Password  = _WebAppUser.Password ;
                        this.appConfig.CurrentWebAppUser = LoggedInWebAppUser;
                        UserOk = true; 
                        break;
                    }
                }

            }
            return UserOk;
        }
    }
    public class WebAppUser
    {
        public string UserName;
        public string Password;
        public WebAppUser()
        {
        }
   
    }
}        

When examining the "frmLogin.cs" code, note the use of a provider class called AppConfig. This class contains all the configuration objects and operational parameters of the application. Each class that is capable of receiving an instance of AppConfig has a public attribute called appConfig.

public AppConfig appConfig = new AppConfig();

AppConfig is propagated to all classes that need such information- frmLogin, frmTitles, frmStores, frmSales, frmRoySched, frmPubsInfo, frmPublishers, frmJobs, frmEmployee, frmDiscounts, and PubsDesktop. Shown below is the code where the instance of AppConfig is passed from frmLogin to PubsDesktop:

//frmLogin.cs
PubsDesktop pubsDesktop = new PubsDesktop();
pubsDesktop.appConfig = this.appConfig;

The AppConfig class contains an instance of the WebAppUser class within it, which will hold the username and password of the user who has successfully authenticated through the private method IsValidLogin(). If the user is authenticated correctly, the instance of AppConfig is passed to the PubDesktop object, which represents the Web Desktop that will host all the other forms of the application.

Please note that the authentication section mentioned here is for a demo application, and it's crucial NOT to hardcode usernames and their corresponding passwords in actual programs. Regarding authentication, Wisej.NET can utilize "anonymous" authentication or Windows authentication (in the case of applications developed for the .NET Framework). You can choose the authentication types and enable SSL usage in the Project's Properties.

4.5 The AppConfig object

In the"AppConfig.cs" flie,you will find the class responsible for retrieving some of the configuration parameters from the "Web.Config" file and passing the application's configuration to various components. Let's analyze it in detail.

//AppConfig.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace WisejPubsDemoApp
{
    public class AppConfig
    {
        public WebAppUser CurrentWebAppUser = null;
        public BasicDAL.DbConfig DbConfig;
        public bool IsPublicDemo = false;
        public string PublicDemoMessage = "";
        public AppConfig()
        {
            DbConfig = new BasicDAL.DbConfig();
        }

        public Boolean ReadWebConfigAppConfig()
        {
            bool _ok = false;
            if (System.Configuration.ConfigurationManager.AppSettings.Count > 0)
            {
                try
                {
                    this.IsPublicDemo  = Convert.ToBoolean (System.Configuration.ConfigurationManager
                                                                   .AppSettings["IsPublicDemo"]);
                    this.PublicDemoMessage = System.Configuration.ConfigurationManager
                                                                  .AppSettings["PublicDemoMessage"];
                    this.DbConfig.ServerName = System.Configuration.ConfigurationManager
                                                                    .AppSettings["DbConfigServerName"];
                    this.DbConfig.DataBaseName = System.Configuration.ConfigurationManager
                                                                    .AppSettings["DbConfigDataBaseName"];
                    this.DbConfig.UserName = System.Configuration.ConfigurationManager
                                                                    .AppSettings["DbConfigUserName"];
                    this.DbConfig.Password = System.Configuration.ConfigurationManager
                                                                    .AppSettings["DbConfigPassword"];
                    this.DbConfig.AuthenticationMode = Convert.ToInt32(
                                                                       System.Configuration.ConfigurationManager
                                                                       .AppSettings["DbConfigAuthenticationMode"]
                                                                       );
                    // AuthenticationMode = 0 (Zero) means DB Native Authentication
                    // AuthenticationMode = 1 (One) means Windows Authentication

                    _ok = true;
                }
                catch (Exception)
                {
                    Wisej.Web.MessageBox.Show("Error reading Web.Config");
                    _ok = false;
                }
            }
            return _ok;
        }

    }
}

The AppConfig class contains two objects: CurrentWebAppUser of type WebAppUser and DbConfig of type BasicDAL.DbConfig. The latter object is part of the ORM (Object-Relational Mapping) library "BasicDAL" and, specifically, is responsible for managing the connection context to an ADO.NET data source. To function correctly, it requires certain mandatory parameters such as the DB Server name, database name, authentication mechanism used, and optionally the username and password to access the database if our database server doesn't support Windows authentication. All of these parameters are used to generate the database connection string. In our implementation, these parameters are stored in the Web.Config file and are read by the ReadWebConfigAppConfig function. Below is the relevant snippet from Web.Config.

<appSettings>
    <add key="Wisej.LicenseKey" value=""/>
    <add key="Wisej.DefaultTheme" value="Bootstrap-4"/>
	<add key="IsPublicDemo" value="false"/>
	<add key="PublicDemoMessage" value="Sorry! This is a Public Demo. Add or Delete is not allowed!"/>
	<add key="DbConfigServerName" value="(local)"/>
	<add key="DbConfigDataBaseName" value="pubs"/>
	<!--DbConfigAuthenticationMode  0 = Native DB Auth (username/password) 1 = Windows Integrated Auth. -->
	<add key="DbConfigAuthenticationMode" value="0"/>
	<!-- DON'T DO THIS IN REAL WORLD!!! ALWAYS CRYPT VALUE THAT CONTAINS SENSIBLE DATA-->
	<add key="DbConfigUserName" value="WisejPubs"/>
	<add key="DbConfigPassword" value="pubs"/>
	<!-- DON'T DO THIS IN REAL WORLD-->
  </appSettings>

4.6 The PubDesktop object

This class serves two fundamental functions:

  1. Completing Configuration Retrieval: It completes the retrieval of configuration settings, such as those related to database access.

  2. Menu Management: It implements the management of the menu that allows the selection of interactive application functions available to the authenticated user.

NOTE: Desktop objects such as PubDesktop are instances of the Wisej.Web.Desktop class and correspond to the "Custom Desktop" template.

Scrolling through the class code, we can observe, highlighted in yellow, some aspects that will be subject to further examination

PubDesktop has a public instance of the AppConfig object. As mentioned previously, the AppConfig object has been passed from the Login window (frmLogin), and it contains the CurrentWebAppUser object populated with the username and password.

public AppConfig appConfig = new AppConfig();

The LoadAppConfig method is invoked by the event handler for the Desktop's Load event.

private void PubsDesktop_Load(object sender, EventArgs e)
        {
            this.LoadAppConfig();
            this.txt_CurrentUser.Text = this.appConfig.CurrentWebAppUser.UserName;
        }

The LoadAppConfig method sets a portion of the BasicDAL.DbConfig object related to the parameters of the Runtime UI environment in which the BasicDAL ORM will operate, specifically in the case of Wisej.NET.

private void LoadAppConfig()
        {
            this.appConfig.DbConfig.RuntimeUI = BasicDAL.RuntimeUI.Wisej;
            this.appConfig.DbConfig.RedirectErrorsNotificationTo = new BasicDALWisejControls.BasicDALMessageBox();
            this.appConfig.DbConfig.Provider = BasicDAL.Providers.SqlServer;
            if (!this.appConfig.ReadWebConfigAppConfig())
            {
                MessageBox.Show("Error reading Web.Config parameters!");
                Application.Exit();
            }
        }

This enables interactive handling of error situations and their corresponding messages, which will be redirected to the client's browser using a native Wisej.NET MessageBox object passed through the RedirectErrorsNotificationTo property.

this.appConfig.DbConfig.RedirectErrorsNotificationTo = new BasicDALWisejControls.BasicDALMessageBox();

Additionally, the type of database server being used, Microsoft SQL Server, is set via the Providers property.

this.appConfig.DbConfig.Provider = BasicDAL.Providers.SqlServer;

The configuration operation continues by reading the data present in the "Web.Config" file using the ReadWebConfigAppConfig method.

if (!this.appConfig.ReadWebConfigAppConfig())
            {
                MessageBox.Show("Error reading Web.Config parameters!");
                Application.Exit();
            }

Once this is completed, the TextBox containing the current user's name is populated, and the application waits for the user to select an item from the mnuBar1 menu. The selection is handled by the ManageMenuBar method.

ManageMenuBar(e.MenuItem.Name);

ManageMenuBar() receives the name of the selected menu item and, through a switch block, activates a new instance of the chosen Form. A utility function from the BasicDAL module is invoked, which implements Wisej.NET user controls (BasicDALWisejControls). Then, the configuration is passed, and finally, the Show() method is called.

 private void ManageMenuBar(string MenuItemName)
        {
            switch (MenuItemName)
            {
                case "mnuFile_Exit":
                    break;

                case "mnuDiscounts" :
                    frmDiscounts frmDiscounts = new frmDiscounts();
                    BasicDALWisejControls.Utilities.SetFixedWindowStyles(frmDiscounts);
                    frmDiscounts.appConfig = this.appConfig;
                    frmDiscounts.Show();
                    break;

                case "mnuAuthors" :
                    frmAuthors frmAuthors = new frmAuthors();
                    BasicDALWisejControls.Utilities.SetFixedWindowStyles(frmAuthors);
                    frmAuthors.appConfig = this.appConfig;
                    frmAuthors.Show();
                    break;
                    
                //more cases- one for each form type
                default:
                    break;
            }
        }

4.7 The application model and data management

As mentioned in section 4.2, the application architecture follows a very simple architectural pattern, typical of the world of simple Windows Forms, using a Model-View approach where the View also implements some business logic controller functions. In the real world, this approach is usable for moderately complex applications when data management is delegated to an ORM (Object-Relational Mapping) that can handle almost all aspects of interaction with the database. In our case, the ORM BasicDAL is capable of performing this task, completely abstracting the database management from the developer and allowing the complete avoidance (if desired) of using SQL language within the application.

4.7.1 BasicDAL in summary

The following is a simple explanation of BasicDAL's structure in terms of constituent objects and main functionalities. BasicDAL is an ORM that, in addition to ORM functions, also incorporates data binding functions to presentation objects with properties that can be displayed in a UI. Its default operating pattern is that of the Active Record; however, in situations where the memory allocated by the record set is considered excessive or when it is not necessary to keep active records in memory, it is possible to switch to DataReader mode with a forward-only cursor.

The main objects are:

BasicDAL.DbObject requires an association with a BasicDAL.DbConfig to access the data

Hierarchy of main object compositions:

4.7.2 The Model

In our application, the model is represented by the file "BasicDALPubsModel.cs." The model is made up of a set of objects, derived from the base class BasicDAL.DbObject:

  • DbT_dbo_authors

  • DbT_dbo_discounts

  • DbT_dbo_employee

  • DbT_dbo_jobs

  • DbT_dbo_pub_info

  • DbT_dbo_publishers

  • DbT_dbo_roysched

  • DbT_dbo_salesmaster

  • DbT_dbo_salesdetails

  • DbT_dbo_stores

  • DbT_dbo_titleauthor

  • DbT_dbo_titles

  • DbV_dbo_titleview

  • DbSP_dbo_byroyalty

  • DbSP_dbo_reptq1

  • DbSP_dbo_reptq2

  • DbSP_dbo_reptq3

  • DbV_dbo_titles_publishers

  • DbV_dbo_AuthorsFullname

  • DbV_dbo_pub_info_publisher

The purpose of these objects is to map Database objects (Tables, Views, Stored Procedures, Functions). Let's take a look at the class DbT_dbo_authors, which is related to a Table.

public class DbT_dbo_authors : BasicDAL.DbObject
{
    private BasicDAL.DbColumn _DbC_au_id = new BasicDAL.DbColumn("[au_id]", System.Data.DbType.String, true, "");
    private BasicDAL.DbColumn _DbC_au_lname = new BasicDAL.DbColumn("[au_lname]", System.Data.DbType.String, false, "LAST");
    private BasicDAL.DbColumn _DbC_au_fname = new BasicDAL.DbColumn("[au_fname]", System.Data.DbType.String, false, "FIRST");
    private BasicDAL.DbColumn _DbC_phone = new BasicDAL.DbColumn("[phone]", System.Data.DbType.String, false, "PHONE");
    private BasicDAL.DbColumn _DbC_address = new BasicDAL.DbColumn("[address]", System.Data.DbType.String, false, "ADDRESS");
    private BasicDAL.DbColumn _DbC_city = new BasicDAL.DbColumn("[city]", System.Data.DbType.String, false, "CITY");
    private BasicDAL.DbColumn _DbC_state = new BasicDAL.DbColumn("[state]", System.Data.DbType.String, false, "ST");
    private BasicDAL.DbColumn _DbC_zip = new BasicDAL.DbColumn("[zip]", System.Data.DbType.String, false, "ZIP");
    private BasicDAL.DbColumn _DbC_contract = new BasicDAL.DbColumn("[contract]", System.Data.DbType.Boolean, false, false);
    private BasicDAL.DbColumn _DbC_email = new BasicDAL.DbColumn("[email]", System.Data.DbType.String, false, "EMAIL");
    
    public BasicDAL.DbColumn DbC_au_id
    {
        get { return _DbC_au_id; }
        set { _DbC_au_id = value; }
    }

    public BasicDAL.DbColumn DbC_au_lname
    {
        get { return _DbC_au_lname; }
        set { _DbC_au_lname = value; }
    }

    public BasicDAL.DbColumn DbC_au_fname
    {
        get { return _DbC_au_fname; }
        set { _DbC_au_fname = value; }
    }

    public BasicDAL.DbColumn DbC_phone
    {
        get { return _DbC_phone; }
        set { _DbC_phone = value; }
    }

    public BasicDAL.DbColumn DbC_address
    {
        get { return _DbC_address; }
        set { _DbC_address = value; }
    }

    public BasicDAL.DbColumn DbC_city
    {
        get { return _DbC_city; }
        set { _DbC_city = value; }
    }

    public BasicDAL.DbColumn DbC_state
    {
        get { return _DbC_state; }
        set { _DbC_state = value; }
    }

    public BasicDAL.DbColumn DbC_zip
    {
        get { return _DbC_zip; }
        set { _DbC_zip = value; }
    }

    public BasicDAL.DbColumn DbC_contract
    {
        get { return _DbC_contract; }
        set { _DbC_contract = value; }
    }

    public BasicDAL.DbColumn DbC_email
    {
        get { return _DbC_email; }
        set { _DbC_email = value; }
    }

    public DbT_dbo_authors()
    {
        this.InterfaceMode = BasicDAL.InterfaceModeEnum.Private;
        this.DbObjectType = BasicDAL.DbObjectTypeEnum.Table;
        this.DbTableName =  "authors";
    }
}

The DbT_dbo_authors class derives from the base class BasicDAL.DbObject and maps the "author" table. Each column is mapped with a BasicDAL.DbColumn class field, which uses a part of the table name as its name. The constructor of BasicDAL.DbColumn expects the following mandatory parameters: column name, column data type, primary key membership, default value. In the previous code sample of the DbT_dbo_authors class, the DbColumn fields are declared as private and associated with a public property to access the customization of the Get and Set methods. If there is no need to access these methods, the class could be composed as follows:

public class DbT_dbo_authors_public : BasicDAL.DbObject
{
    public BasicDAL.DbColumn _DbC_au_id = new BasicDAL.DbColumn("[au_id]", System.Data.DbType.String, true, "");
    public BasicDAL.DbColumn _DbC_au_lname = new BasicDAL.DbColumn("[au_lname]", System.Data.DbType.String, false, "LAST");
    public BasicDAL.DbColumn _DbC_au_fname = new BasicDAL.DbColumn("[au_fname]", System.Data.DbType.String, false, "FIRST");
    public BasicDAL.DbColumn _DbC_phone = new BasicDAL.DbColumn("[phone]", System.Data.DbType.String, false, "PHONE");
    public BasicDAL.DbColumn _DbC_address = new BasicDAL.DbColumn("[address]", System.Data.DbType.String, false, "ADDRESS");
    public BasicDAL.DbColumn _DbC_city = new BasicDAL.DbColumn("[city]", System.Data.DbType.String, false, "CITY");
    public BasicDAL.DbColumn _DbC_state = new BasicDAL.DbColumn("[state]", System.Data.DbType.String, false, "ST");
    public BasicDAL.DbColumn _DbC_zip = new BasicDAL.DbColumn("[zip]", System.Data.DbType.String, false, "ZIP");
    public BasicDAL.DbColumn _DbC_contract = new BasicDAL.DbColumn("[contract]", System.Data.DbType.Boolean, false, false);
    public BasicDAL.DbColumn _DbC_email = new BasicDAL.DbColumn("[email]", System.Data.DbType.String, false, "EMAIL");
    
    public DbT_dbo_authors_public()
    {
        this.InterfaceMode = BasicDAL.InterfaceModeEnum.Private;
        this.DbObjectType = BasicDAL.DbObjectTypeEnum.Table;
        this.DbTableName =  "authors";
    }
}
    

The BasicDAL.DbObject and BasicDAL.DbColumn objects have dozens and dozens of properties, fields, methods, and events. The complete documentation is available on the GitHub website at https://github.com/gdelgiovine.

BasicDAL also provides a tool for generating BasicDAL.DbObject classes. The tool is called BasicDAL ClassBuilder and is available on the GitHub website.

4.8 The forms (Wisej.Web.Form)

In the application, forms are characterized by the suffix "frm" in their names and all share the same basic structure, derived from a template. Therefore, we will only analyze some forms that implement interesting functionality. To do this, we will examine frmSales and frmAuthors, which offer some interesting insights.

4.8.1 The form fmSales

In Wisej.NET, forms are preferably designed using a drawing surface (designer) without the need to manually write HTML or CSHTML code, which is necessary for ASP.NET MVC, ASP.NET Core, Angular, or React applications. This is the designer for the frmSales form:

As seen, the form in the design phase closely resembles its runtime appearance.

The files that make up the form look like this in the "Solution Explorer." Note that this is the same structure that we saw in section 3.4.2 for the "Page1.cs" file.

4.8.2 The code of frmSales

The vast majority of the code found in the two files implementing the frmSales class, namely "frmSales.cs" and "frmSales.Designer.cs", is located in the latter file. The code in the "frmSale.Designer.cs" file is typically generated and managed automatically by the Wisej.NET designer. The code present in the "frmSales.cs" file is kept to a minimum and primarily consists of declarations of objects related to the BasicDAL ORM and UI object event handlers. Due to the use of a simplified version of the Model-ViewController pattern, the frmSales.cs file also contains fragments of business logic. It's important to emphasize that Wisej.NET does not impose any specific architectural pattern.

This section contains relevant code snippets from frmSales.cs, but does not contain frmSales.cs in its entirety. The full contents of the frmSales.cs class can be found on github here: https://github.com/JulieHirt/WisejPubsDemoApp/blob/main/WisejPubsDemoApp/WisejPubsDemoApp/frmSales.cs

4.8.2.1 frmSales.cs - fields

Let's begin by looking at the fields declared in frmSales. We'll talk about what values they are assigned.

//the fields of FrmSales
public partial class frmSales : Form
    {
        public AppConfig appConfig = new AppConfig();
        private BasicDAL.DbConfig DbConfig = new BasicDAL.DbConfig();
        private BasicDALWisejControls.BasicDALMessageBox BasicDALMessageBox = new BasicDALWisejControls.BasicDALMessageBox();
        private DbT_dbo_salesdetails dbT_Dbo_SalesDetails = new DbT_dbo_salesdetails();
        private DbT_dbo_titles dbT_Dbo_Titles = new DbT_dbo_titles();
        private DbT_dbo_stores dbT_Dbo_Stores = new DbT_dbo_stores();
        private DbT_dbo_salesmaster dbT_Dbo_SalesMaster = new DbT_dbo_salesmaster();
        private BasicDAL.DbLookUp dbl_stor_id = new BasicDAL.DbLookUp();
        private bool FormInit = false;
        private bool DataNavigatorHandlerInitialized = false;
        
        //...
        
    }

Looking specifically at the first two fields in frmSales, appConfig and DbConfig, we see the following:

// frmSales.cs
public AppConfig appConfig = new AppConfig();
private BasicDAL.DbConfig DbConfig = new BasicDAL.DbConfig();

appConfig receives the complete application configuration from the PubDesktop object:

// PubsDesktop.cs
frmSales frmSales = new frmSales();
//...
frmSales.appConfig = this.appConfig;
frmSales.Show();

DbConfig is populated with a copy of the BasicDAL.DbConfig database context present in the AppConfig object:

//code from InitDataContext() in frmSales.cs
this.DbConfig = this.appConfig.DbConfig.Clone();

Next, let's look at the fields dbT_dbo_salesdetails, dbT_dbo_titles, dbT_dbo_stores, and dbT_dbo_salesmaster:

// frmSales.cs
private BasicDALWisejControls.BasicDALMessageBox BasicDALMessageBox = new BasicDALWisejControls.BasicDALMessageBox();
private DbT_dbo_salesdetails dbT_Dbo_SalesDetails = new DbT_dbo_salesdetails();
private DbT_dbo_titles dbT_Dbo_Titles = new DbT_dbo_titles();
private DbT_dbo_stores dbT_Dbo_Stores = new DbT_dbo_stores();
private DbT_dbo_salesmaster dbT_Dbo_SalesMaster = new DbT_dbo_salesmaster();

The classes DbT_dbo_salesdetails, DbT_dbo_titles, DbT_dbo_stores, and DbT_dbo_salesmaster are declared in "BasicDALPubsModel.cs" (see section 4.7.2). All of these classes derive from BasicDAL.DbObject. They are the entities that map the corresponding Table objects in the Pubs database. (For example, DbT_dbo_titles maps to the 'titles' table).

Next, observe the declaration and instantiation of dbl_stor_id, which is a BasicDAL.DbLookUp object:

// frmSales.cs
private BasicDAL.DbLookUp dbl_stor_id = new BasicDAL.DbLookUp();

This object implements a LookUp function towards a BasicDAL.DbObject by adding BasicDAL.DbFilters to its BasicDAL.DbFilters collection and associating UI controls for data binding through its BasicDAL.BoundControls collection.

// InitDataContext() in frmSales.cs
// Declare of DbLookUp 
this.dbl_stor_id.DbObject = this.dbT_Dbo_Stores;
this.dbl_stor_id.Filters.AddBoundControl(this.dbT_Dbo_Stores.DbC_stor_id, 
    BasicDAL.ComparisionOperator.Equal,
    this.txt_stor_id,
    "text");
this.dbl_stor_id.BoundControls.Add(this.dbT_Dbo_Stores.DbC_stor_name,this.lbl_stor_name, "text");

Note for Domain-Driven Design (DDD) purists: BasicDAL also allows the use of Plain Old Class Objects (POCO) as the basis for all presentation and business logic. This enables an additional level of code abstraction, gradually decoupling it from the specific ORM. This is made possible through the concurrent use of an attribute class for defining POCO classes that describe application entities (components and aggregates) and the presence of the .ToList() and .FromList() methods of the BasicDAL.DbObject. These two methods allow mapping the data set of the BasicDAL.DbObject entity to a POCO List, whose properties are decorated with BasicDAL attributes.

4.8.2.2 frmSales.cs - Code that runs when the form is initially loaded

The InitDataNavigatorHandler() method is responsible for creating and associating event handlers for the BasicDALWisejControl.DataNavigator object used in the form.

//frmSales.cs
private void InitDataNavigatorHandler()
        {
            // Declare Events Handlers for DataNavigator
            if (DataNavigatorHandlerInitialized == true)
            {
                return;
            }
            this.dataNavigator1.eAddNew -= new BasicDALWisejControls.DataNavigator.eAddNewEventHandler(this.dataNavigator1_eAddNew);
            this.dataNavigator1.eAddNew += new BasicDALWisejControls.DataNavigator.eAddNewEventHandler(this.dataNavigator1_eAddNew);
            this.dataNavigator1.ePrint -= new BasicDALWisejControls.DataNavigator.ePrintEventHandler(this.dataNavigator1_ePrint);
            this.dataNavigator1.ePrint += new BasicDALWisejControls.DataNavigator.ePrintEventHandler(this.dataNavigator1_ePrint);
            this.dataNavigator1.eDelete -= new BasicDALWisejControls.DataNavigator.eDeleteEventHandler(this.dataNavigator1_eDelete);
            this.dataNavigator1.eDelete += new BasicDALWisejControls.DataNavigator.eDeleteEventHandler(this.dataNavigator1_eDelete);
            this.dataNavigator1.eRefresh -= new BasicDALWisejControls.DataNavigator.eRefreshEventHandler(this.dataNavigator1_eRefresh);
            this.dataNavigator1.eRefresh += new BasicDALWisejControls.DataNavigator.eRefreshEventHandler(this.dataNavigator1_eRefresh);
            this.dataNavigator1.eClose -= new BasicDALWisejControls.DataNavigator.eCloseEventHandler(this.dataNavigator1_eClose);
            this.dataNavigator1.eClose += new BasicDALWisejControls.DataNavigator.eCloseEventHandler(this.dataNavigator1_eClose);
            this.dataNavigator1.eFind -= new BasicDALWisejControls.DataNavigator.eFindEventHandler(this.dataNavigator1_eFind);
            this.dataNavigator1.eFind += new BasicDALWisejControls.DataNavigator.eFindEventHandler(this.dataNavigator1_eFind);
            this.dataNavigator1.eSave -= new BasicDALWisejControls.DataNavigator.eSaveEventHandler(this.dataNavigator1_eSave);
            this.dataNavigator1.eSave += new BasicDALWisejControls.DataNavigator.eSaveEventHandler(this.dataNavigator1_eSave);
            this.dataNavigator1.eMovePrevious -= new BasicDALWisejControls.DataNavigator.eMovePreviousEventHandler(this.dataNavigator1_eMovePrevious);
            this.dataNavigator1.eMovePrevious += new BasicDALWisejControls.DataNavigator.eMovePreviousEventHandler(this.dataNavigator1_eMovePrevious);
            this.dataNavigator1.eMoveFirst -= new BasicDALWisejControls.DataNavigator.eMoveFirstEventHandler(this.dataNavigator1_eMoveFirst);
            this.dataNavigator1.eMoveFirst += new BasicDALWisejControls.DataNavigator.eMoveFirstEventHandler(this.dataNavigator1_eMoveFirst);
            this.dataNavigator1.eMoveLast -= new BasicDALWisejControls.DataNavigator.eMoveLastEventHandler(this.dataNavigator1_eMoveLast);
            this.dataNavigator1.eMoveLast += new BasicDALWisejControls.DataNavigator.eMoveLastEventHandler(this.dataNavigator1_eMoveLast);
            this.dataNavigator1.eMoveNext -= new BasicDALWisejControls.DataNavigator.eMoveNextEventHandler(this.dataNavigator1_eMoveNext);
            this.dataNavigator1.eMoveNext += new BasicDALWisejControls.DataNavigator.eMoveNextEventHandler(this.dataNavigator1_eMoveNext);
            this.dataNavigator1.eUndo -= new BasicDALWisejControls.DataNavigator.eUndoEventHandler(this.dataNavigator1_eUndo);
            this.dataNavigator1.eUndo += new BasicDALWisejControls.DataNavigator.eUndoEventHandler(this.dataNavigator1_eUndo);

            DataNavigatorHandlerInitialized = true;
        }

The frmSales_Load() event invokes the InitDataContext() method. Therefore, the InitDataContext() method runs when the frmSales form is initially loaded.

//frmSales.cs
private void InitDataContext(bool ForceFormInit = false)
        {
            if (ForceFormInit == true)
            {
                FormInit = false;
            }
            if (this.FormInit)
            {
                return;
            }
     
            this.DbConfig = this.appConfig.DbConfig.Clone();
            this.DbConfig.RedirectErrorsNotificationTo = this.BasicDALMessageBox;
            this.dbT_Dbo_SalesDetails.Init(this.DbConfig);
            this.dbT_Dbo_Stores.Init(this.DbConfig);
            this.dbT_Dbo_Titles.Init(this.DbConfig);
            this.dbT_Dbo_SalesMaster.Init(this.DbConfig);
            this.dbT_Dbo_SalesMaster.DataBinding = BasicDAL.DataBoundControlsBehaviour.BasicDALDataBinding;
            //binding for BasicDAL DbObject columns into UI objects.
            this.dbT_Dbo_SalesMaster.DbC_ord_date.BoundControls.Add(this.dtp_ord_date, "value");
            this.dbT_Dbo_SalesMaster.DbC_ord_num.BoundControls.Add(this.txt_ord_num, "text");
            this.dbT_Dbo_SalesMaster.DbC_stor_id.BoundControls.Add(this.txt_stor_id, "text");
            this.dbT_Dbo_SalesMaster.DbC_stor_ord_date.BoundControls.Add(this.dtp_stor_ord_date, "value");
            this.dbT_Dbo_SalesMaster.DbC_stor_ord_num.BoundControls.Add(this.txt_stor_ord_num, "text");
            this.dbT_Dbo_SalesMaster.DbC_payterms.BoundControls.Add(this.cmb_payterms, "text");
            // Declare of DbLookUp 
            this.dbl_stor_id.DbObject = this.dbT_Dbo_Stores;
            this.dbl_stor_id.Filters.AddBoundControl(this.dbT_Dbo_Stores.DbC_stor_id, 
                                                     BasicDAL.ComparisionOperator.Equal,
                                                     this.txt_stor_id,
                                                     "text");
            this.dbl_stor_id.BoundControls.Add(this.dbT_Dbo_Stores.DbC_stor_name,this.lbl_stor_name, "text");
            this.dataNavigator1.DbObject = this.dbT_Dbo_SalesMaster;
            this.dataNavigator1.ManageNavigation = false;
            this.dataNavigator1.DataGrid = this.dgv_SalesDetails;
            this.dataNavigator1.DataGridActive = false;
            this.dataNavigator1.DataGridListView = this.dgvList;
            this.dataNavigator1.ListViewColumns.Add(this.dbT_Dbo_SalesMaster.DbC_ord_num , "Order Number", "");
            this.dataNavigator1.ListViewColumns.Add(this.dbT_Dbo_SalesMaster.DbC_ord_date, "Order Date", "dd/MM/yyyy");
            this.dataNavigator1.ListViewColumns.Add(this.dbT_Dbo_SalesMaster.DbC_stor_id , "Store Id", "");
            this.dataNavigator1.DataNavigatorInit(true);
           
        }

The connection context to the local database DbConfig is cloned from the one present in the received configuration class. (this.DbConfig = this.appConfig.DbConfig.Clone();)This cloning operation allows for having a context separate from the one received, enabling independent transaction handling within your code without affecting operations on other objects not involved in the transaction. The use of connection context cloning is an additional option available to the developer.

Next, the message window used by the ORM is then redirected to report any unhandled exceptions to the user.

By using the .Init() method of the various DbObject entities that map database objects, they are associated with the specific BasicDAL.DbConfig connection context of the form. All BasicDAL.DbObject entities associated with the context are available in the BasicDAL.DbConfig.ManagedDbObjects collection.

By default, BasicDAL.DbObject objects do not perform data binding for the Value property of BasicDAL.DbColumn objects that map database columns. The binding mode is set/read through the DataBinding property, which can have these values:

// BasicDAL.cs
public enum DataBoundControlsBehaviour
{
    WindowsFormsDataBinding = 2,
    BasicDALDataBinding = 1,
    NoDataBinding = 0
}

In our frmSales form, the only BasicDAL.DbObject object that requires data binding to multiple user interface controls is the instance dbT_Dbo_SalesMaster of the DbT_dbo_salesmaster class, which represents the sales master table. For each of the BasicDAL.DbColumn objects, their corresponding UI controls are added to their own BasicDAL.BoundControls collection with an indication of whether they are meant to be used for setting or reading the user-entered values. Data binding can be set to write-only to the control, read from the control, read from the control only during the creation of a new data row, or a combination of these values. The direction of data binding is managed by the BindingBehaviour property of the BoundControl object.

Here's an example:

this.dbT_Dbo_SalesMaster.DbC_ord_num.BoundControls.Add(this.txt_ord_num, "text");

this.txt_ord_num refers to the control that contains the order number in the frmSales form. In this context, "text" is the name of the property from which values are read (using the control's property's getter to update DbColumn.Value) or to which values are written (using the control's property's setter with the value of the DbColumn.Value property).

Next, we proceed to populate the dbl_stor_id instance by associating it with the dbT_Dbo_Stores object, which is the DbObject representing the database object that we will use as a data source for performing a lookup. It is necessary to provide the value or values for filtering that will be entered by the user in various controls. This is done by creating a filter based on a BoundControl. In the BoundControl, you specify the BasicDAL.DbColumn of the table for the lookup query, the comparison operator, the UI control, and the name of the property of this control from which to retrieve the value.

this.dbl_stor_id.Filters.AddBoundControl(this.dbT_Dbo_Stores.DbC_stor_id, 
                                                     BasicDAL.ComparisionOperator.Equal,
                                                     this.txt_stor_id,
                                                     "text");

Last updated

Logo

© Ice Tea Group LLC, All Rights Reserved.