"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.
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
frmLogin
objectThe 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:
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
.
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
:
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
AppConfig
objectIn 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.
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.
4.6 The PubDesktop object
This class serves two fundamental functions:
Completing Configuration Retrieval: It completes the retrieval of configuration settings, such as those related to database access.
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.
The LoadAppConfig
method is invoked by the event handler for the Desktop's Load
event.
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.
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.
Additionally, the type of database server being used, Microsoft SQL Server, is set via the Providers
property.
The configuration operation continues by reading the data present in the "Web.Config" file using the ReadWebConfigAppConfig
method.
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()
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.
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.
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:
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
)
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
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
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.
Looking specifically at the first two fields in frmSales, appConfig
and DbConfig
, we see the following:
appConfig
receives the complete application configuration from the PubDesktop
object:
DbConfig
is populated with a copy of the BasicDAL.DbConfig database context present in the AppConfig
object:
Next, let's look at the fields dbT_dbo_salesdetails
, dbT_dbo_titles
, dbT_dbo_stores
, and 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:
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.
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.
The frmSales_Load()
event invokes the InitDataContext()
method. Therefore, the InitDataContext()
method runs when the frmSales
form is initially loaded.
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:
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.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.
Last updated