Building the software architecture from scratch is one of the most time-consuming parts of developing web-based or custom enterprise applications. Having a ready-made, up-to-date framework at hand turns this challenge into a significant advantage.

If a template that combines a C# library for auto-generating SQL behind your forms, HTML5/Bootstrap for page layout, pure JavaScript for front-end logic, Ajax 2 and JSON for data transfer, and Razor/PageModel for business logic sounds useful to you — this article is worth reading.

SELECT, INSERT, UPDATE, and DELETE SQL DML statements make up a significant share of any application's codebase. A library that builds those DML statements automatically — sdbcsapi — removes much of that repetition.

C# — Database Query Execution

The code snippet below handles a select operation in C#.

C# — tableAction / selectData
// For executing database query operations
dbQuery = p.executeTable.tableAction(p.action, p.formData, p.prepareQuery);

if (dbQuery)
    // If query is true get table data from api
    p.tableData = (string[][])p.executeTable.selectData(p.dataType);
else
    errorString = p.executeTable.getErrorString();

Razor — HTML Form Design

In Razor, a data-entry form can be structured as follows.

Razor — New Record Form
@(new HtmlString(Model.menuString))

<div class="container-fluid">
    <div class="row">
        @(new HtmlString(Model.leftSideStr))
        <div class="col-md-11 col-xl-10">
            <a href="#newForm" class="btn btn-outline-dark" data-toggle="collapse">
                New @Model.pageTitle
            </a>
            <form id="newForm" class="form collapse" enctype="multipart/form-data" method="post">
                <datalist id="paths"></datalist>
                <datalist id="types"></datalist>
                <datalist id="codes"></datalist>
                <div class="form-group">
                    <table class="table table-borderless">
                        <tbody>
                            <tr>
                                <td><label for="y_1">@Model.labelNames[1]:</label></td>
                                <td>
                                    <input type="@Model.inputTypes[1]"
                                           class="form-control form-control-sm"
                                           name="@Model.fieldNames[1]" id="y_1"
                                           placeholder="Enter @Model.labelNames[1]" required>
                                </td>
                                <td><label for="y_2">@Model.labelNames[2]:</label></td>
                                <td>
                                    <input type="@Model.inputTypes[2]"
                                           class="form-control form-control-sm"
                                           name="@Model.fieldNames[2]" id="y_2"
                                           placeholder="Enter @Model.labelNames[2]"
                                           list="paths" required>
                                </td>
                            </tr>
                            <tr>
                                <td><label for="y_4">Code:</label></td>
                                <td>
                                    <input type="file" class="form-control form-control-sm"
                                           name="@Model.fileField" id="y_4" required
                                           onchange="fileNameInfo('y_4','y_1')">
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    <button type="button" class="btn btn-outline-info btn-sm"
                                            onclick='sendInsert("@Model.pageName","newForm")'>
                                        Save
                                    </button>
                                </td>
                                <td>
                                    <button type="reset" class="btn btn-outline-secondary btn-sm">
                                        Cancel
                                    </button>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </form>
        </div>
    </div>

Razor — Data Table Section

Every page in the template also includes a sortable, paginated data table.

Razor — Listing Table
<div class="row" id="ldTable">
    <div class="col-md-12">
        <h2>@Model.pageTitle List</h2>
        <div class="table-responsive">
            <table class="table table-striped table-bordered table-sm">
                <thead>
                    <tr>
                        <th>#</th>
                        <th onclick="sortById()">@Model.labelNames[0]</th>
                        <th onclick="sortByProp('@Model.fieldNames[1]')">@Model.labelNames[1]</th>
                        <th onclick="sortByProp('@Model.fieldNames[2]')">@Model.labelNames[2]</th>
                        <th onclick="sortByProp('@Model.fieldNames[3]')">@Model.labelNames[3]</th>
                        <th onclick="sortByDate('@Model.fieldNames[5]', true)">@Model.labelNames[5]</th>
                    </tr>
                </thead>
                <tbody id="listTb"></tbody>
            </table>
        </div>

Razor — Pagination & Export Controls

Pagination controls and one-click export buttons for Excel, JSON, CSV and PDF are included.

Razor — Pagination + Export
<div id="pgnId">
    <ul class="pagination">
        <li>
            @{ var lineCount = Model.sessionBean.getLineCount(); }
            <input id="p_size" type="text" class="form-control form-control-sm"
                   value=@lineCount size=3 onchange="setPage()">
        </li>
        <li>
            <button ... id="pageO" onclick="getPage('O')">&laquo;</button>
        </li>
        <li><button ... id="page1" onclick="paging(1)" value="1">1</button></li>
        <li><button ... id="page2" onclick="paging(2)" value="2">2</button></li>
        <li><button ... id="page3" onclick="paging(3)" value="3">3</button></li>
        <li><button ... id="page4" onclick="paging(4)" value="4">4</button></li>
        <li><button ... id="page5" onclick="paging(5)" value="5">5</button></li>
        <li>
            <button ... id="pageS" onclick="getPage('S')">&raquo;</button>
        </li>
        <li><button ... onclick="exportToExcel('@Model.pageTitle'+'List')">Excel</button></li>
        <li><button ... onclick="exportToJson('@Model.pageTitle'+'List')">Json</button></li>
        <li><button ... onclick="exportToCSV('@Model.pageTitle'+'List')">CSV</button></li>
        <li><button ... onclick="exportToPDF('@Model.pageTitle'+'List')">PDF</button></li>
    </ul>
</div>

Razor — Debug Alert Structure

The debug alert section should not be overlooked — it shows SQL, error strings and JSON data during development.

Razor — Debug Alerts
<div id="alerts">
    @if ((Model.tableData == null || Model.tableData.Length == 0) && Model.isDebug)
    {
        <div class="alert alert-warning">
            <strong>tableData</strong> is empty!
            <br /><strong>SQL0:</strong> @Model.qsql
            <br /><strong>SQL1:</strong> @Model.qsql2
        </div>
    }
    @if (Model.errorString != null && Model.errorString.Length > 0 && Model.isDebug)
    {
        <div class="alert alert-danger">
            <strong>errorString:</strong> @Model.errorString
        </div>
    }
    @if (Model.jsObjData != null && Model.jsObjData.Length > 0 && Model.isDebug)
    {
        <div class="alert alert-info">
            <strong>jsObjData:</strong> @Model.jsObjData
            <br />tableData[0][0]: @(Model.tableData != null ? Model.tableData[0][0] : "")
            <br />messageString: @(Model.messageString ?? "")
        </div>
    }
</div>

JavaScript — Initialising Variables

JavaScript variables and their initial values are assigned directly inside the Razor page.

Razor + JavaScript — Variable Initialisation
<script>
  var jsObjData, dataList, selectLists, fieldNames, selectNames, labelNames,
      inputTypes, readOnlyFM, invisibleFM, requiredFM,
      isModal, isWithRefs, selInpIds, listN4Page, listN4Modal,
      dateF, timeF, pageNo, totRow, lineCount, mCols, isLocale, lastProp;

  document.addEventListener("DOMContentLoaded", function(event) {

    jsObjData  = @(new HtmlString(Model.jsObjData ?? ""));
    dataList   = jsObjData.data;

    selectLists = @(new HtmlString(Model.selectLists ?? "''"));

    fieldNames  = @(new HtmlString(ArrayToString.getStringWithComma(Model.fieldNames)));
    selectNames = @(new HtmlString(ArrayToString.getStringWithComma(Model.selectFields)));
    labelNames  = @(new HtmlString(ArrayToString.getStringWithComma(Model.labelNames)));
    inputTypes  = @(new HtmlString(ArrayToString.getStringWithComma(Model.inputTypes)));
    readOnlyFM  = @(new HtmlString(ArrayToString.getStringWithComma(
                      Model.readOnlyFM.Select(x => x.ToString()).ToArray())));
    invisibleFM = @(new HtmlString(ArrayToString.getStringWithComma(
                      Model.invisibleFM.Select(x => x.ToString()).ToArray())));
    requiredFM  = @(new HtmlString(ArrayToString.getStringWithComma(
                      Model.requiredFM.Select(x => x.ToString()).ToArray())));
    isModal    = @(Model.isModal ? "true" : "false");
    isWithRefs = @(Model.isWithRefs ? "true" : "false");
    isLocale   = @(Model.isLocale ? "true" : "false");
</script>

JavaScript — Table Data Configuration

JavaScript — Page Variables
    excelCols = @(new HtmlString(Model.excelCols));
    lastProp  = "@Model.fieldNames[0]";
    pageNo    = 1;

    selInpIds = {
      newForm:    [{ id:"y_2", name:"adr_path" }, { id:"y_3", name:"code_type" }],
      searchForm: [{ id:"s_3", name:"adr_path" }, { id:"s_4", name:"code_type" }, { id:"s_5", name:"rec_user" }],
      updateForm: [{ id:"g_3", name:"adr_path" }, { id:"g_4", name:"code_type" }]
    };

    listN4Page  = [{ id:"paths", name:"adr_path" }, { id:"types", name:"code_type" }, { id:"codes", name:"rec_user" }];
    listN4Modal = [{ id:"paths", name:"adr_path" }, { id:"types", name:"code_type" }];

    totRow    = @(Model.tableData != null ? Model.tableData.Length : 0);
    dateF     = "@Model.dateFormatter.getStringDF()";
    timeF     = "@Model.dateFormatter.getStringTF()";
    lineCount = @Model.sessionBean.getLineCount();
    mCols     = 2;

    setList(listN4Page, selectLists);
    setPage();
Razor — Bootstrap Modal
<div class="modal fade" id="updateModal" role="dialog">
    <div class="modal-dialog modal-lg">
        <div class="modal-content">
            <div class="modal-header">
                <h4 class="modal-title">Update @Model.pageTitle</h4>
                <button type="button" class="close" data-dismiss="modal">&times;</button>
            </div>
            <form id="updateForm" class="form" enctype="multipart/form-data" method="post">
                <div class="modal-body">
                    <table class="table table-borderless">
                        <tbody id="modalTb"></tbody>
                    </table>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-outline-info btn-sm"
                            onclick='sendUpdate("@Model.pageName","updateForm")'>Save</button>
                    <button type="button" class="btn btn-outline-info btn-sm"
                            onclick='sendDownload("@Model.pageName","updateForm","g_2",false)'>
                        Download File
                    </button>
                    <button type="button" class="btn btn-outline-danger btn-sm"
                            onclick='sendDelete("@Model.pageName","updateForm")'>Delete</button>
                    <button type="button" class="btn btn-secondary btn-sm"
                            data-dismiss="modal">Close</button>
                </div>
            </form>
        </div>
    </div>
</div>

JavaScript — Pagination Functions

Excerpts from the csbca.js pagination logic:

JavaScript — setPage / paging / getPage
// For setting html table line size and calling getPage function
function setPage() {
    if (window.dataList !== "") {
        window.lineCount = document.getElementById("p_size").value;
        getPage(window.pageNo);
    }
}

// For setting html table page value and calling getPage function
function paging(pageB) {
    let page = document.getElementById("page" + pageB).value;
    logStr("page: " + page);
    getPage(page);
}

// For setting html table page lines
function getPage(page) {
    let i = 0, j = 0, order = 0, elem, c;
    let k = 0, n = 0;
    let tableStr = "";

    if (page == "O") {
        if (window.pageNo > 1) page = window.pageNo - 1;
        else page = 1;

        if (document.getElementById("page1").value > 1) {
            for (i = 1; i <= 5; i++) {
                elem = document.getElementById("page" + i);
                c = elem.value - 1;
                elem.value = c;
                elem.innerHTML = c;
            }
        }
    } else if (page == "S") {
        if (window.pageNo <= ((window.totRow / window.lineCount) - 1) + 2)
            page = (window.pageNo - 1) + 2;

        if (document.getElementById("page5").value <= ((window.totRow / window.lineCount) - 1) + 2) {
            for (i = 1; i <= 5; i++) {
                elem = document.getElementById("page" + i);
                c = (elem.value - 1) + 2;
                elem.value = c;
                elem.innerHTML = c;
            }
        }
    }
    window.pageNo = page;

JavaScript — Building Table Rows

JavaScript — Row Generation
if (k > 0 && window.totRow > 0) {
    order = 1;
    for (i = n; i < k; i++) {
        for (j = 0; j < window.fieldNames.length; j++) {
            if (j == 0) {
                if (window.isModal)
                    tableStr += "<tr><td>" + order + "</td><td onclick=\"getModal("
                                + window.dataList[i][fieldNames[j]] + ")\"";
                else
                    tableStr += "<tr><td>" + order + "</td><td onclick=\"getUpdateForm("
                                + window.dataList[i][fieldNames[j]] + ")\"";
            } else {
                tableStr += "<td";
            }

            if (j == window.invisibleFM[j])
                tableStr += " style=\"display:none;\"";

            tableStr += ">" + window.dataList[i][window.fieldNames[j]] + "</td>";
        }
        tableStr += "</tr>";
        order++;
    }
    document.getElementById("listTb").innerHTML = tableStr;
}

JavaScript — Building Modal Fields

JavaScript — Modal Field Generation
if ((j % mCols) == 0)
    tableStr += "<tr>";

tableStr += "<td><label for=\"g_" + (j+1) + "\">" + window.labelNames[j] + ":</label></td>"
          + "<td><input class=\"form-control form-control-sm\" id=\"g_" + (j+1) + "\"";

if (window.inputTypes[j] == "datetime-local")
    tableStr += " value=\"" + dateTimeFormat(window.dataList[i][window.fieldNames[j]], true)
              + "\" name=\"" + window.fieldNames[j] + "\"";
else if (window.inputTypes[j] == "date")
    tableStr += " value=\"" + dateTimeFormat(window.dataList[i][window.fieldNames[j]], false)
              + "\" name=\"" + window.fieldNames[j] + "\"";
else
    tableStr += " value=\"" + window.dataList[i][window.fieldNames[j]]
              + "\" name=\"" + window.fieldNames[j] + "\"";

if (j == window.readOnlyFM[j])  tableStr += " readonly";
if (j == window.requiredFM[j])  tableStr += " required";
tableStr += " type=\"" + window.inputTypes[j] + "\"";

isList = false;
if (window.listN4Modal) {
    for (h = 0; h < n; h++) {
        if (window.listN4Modal[h].name == window.fieldNames[j]) {
            tableStr += " list=\"" + window.listN4Modal[h].id + "\"></td>"
                      + "<datalist id=\"" + window.listN4Modal[h].id + "\"></datalist>";
            isList = true;
        }
    }
}
if (!isList) tableStr += "></td>";

JavaScript — Inline Update Form (Modal Alternative)

JavaScript — getUpdateForm()
// For getting update form instead of modal
function getUpdateForm(id) {
    let k = window.fieldNames.length;
    let m = window.dataList ? window.dataList.length : 0;

    for (let i = 0; i < m; i++) {
        if (window.dataList[i][window.fieldNames[0]] == id) {
            for (let j = 0; j < k; j++) {
                if (window.inputTypes[j] == "datetime-local")
                    document.getElementById("g_" + (j+1)).value =
                        dateTimeFormat(window.dataList[i][window.fieldNames[j]], true);
                else if (window.inputTypes[j] == "date")
                    document.getElementById("g_" + (j+1)).value =
                        dateTimeFormat(window.dataList[i][window.fieldNames[j]], false);
                else
                    document.getElementById("g_" + (j+1)).value =
                        window.dataList[i][window.fieldNames[j]];
            }
        }
    }
}

JavaScript — Ajax Search & JSON Parsing

JavaScript — sendSelect / selectResponse
// For sending selection form data with ajax
function sendSelect(url, formId) {
    let formElement = document.getElementById(formId);
    let formData    = new FormData(formElement);

    if (window.isWithRefs)
        setSelectData(formData, formId);

    formData.append("action",   "select");
    formData.append("isLocale", window.isLocale);

    logFD(formData);
    sendForm(url, selectResponse, formData);
}

// For setting selection response data
function selectResponse(xhr) {
    if (xhr.responseText) {
        logStr(xhr.responseText);

        window.jsObjData = JSON.parse(xhr.responseText);
        window.dataList  = window.jsObjData.data;
        window.pageNo    = 1;

        logJD(window.jsObjData);

        if (window.dataList)
            window.totRow = window.dataList.length;

        putMessages();
    }
}

JavaScript — File Download

JavaScript — sendDownload()
// For sending download form data to url with ajax
function sendDownload(url, formId, fileId, isPage) {
    let formElement = document.getElementById(formId);
    let formData    = new FormData(formElement);
    var fileName    = document.getElementById(fileId).value;

    if (isPage === true) fileName += ".html";

    if (isWithRefs)
        setSelectData(formData, formId);

    formData.append("action",   "download");
    formData.append("isLocale", window.isLocale);

    logFD(formData);

    let xhr = window.XMLHttpRequest
        ? new XMLHttpRequest()
        : new ActiveXObject("Microsoft.XMLHTTP");

    xhr.open("POST", url, true);
    xhr.responseType = "blob";

    xhr.onload = function() {
        if (xhr.readyState == 4 && xhr.status == 200)
            downloadResponse(fileName, xhr.response);
    };
    xhr.send(formData);
}

JavaScript — Excel / JSON / CSV Export

JavaScript — exportToExcel / exportToJson / exportToCSV
// For exporting to excel file
function exportToExcel(file = "") {
    file = file ? file + ".xlsx" : "excel_list.xlsx";

    var workbook  = new ExcelJS.Workbook();
    var worksheet = workbook.addWorksheet("excel_list");

    worksheet.columns = excelCols;
    worksheet.addRows(dataList);

    workbook.xlsx.writeBuffer().then(function(data) {
        var blob = new Blob([data], {
            type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
        });
        saveAs(blob, file);
    });
}

// For exporting to json file
function exportToJson(file = "") {
    let jsonList = getJSList();
    file = file ? file + ".json" : "json_list.json";
    let data = "application/json;charset=utf-8," + jsonList;
    exportData(data, file);
}

// For exporting to csv text file
function exportToCSV(file = "") {
    let csvList = getList();
    file = file ? file + ".csv" : "csv_list.csv";
    let data = "text/csv;charset=utf-8," + encodeURIComponent(csvList);
    exportData(data, file);
}

C# — PageModel Initialisation & Authorisation

C# — PageModel.OnPost()
if (process == null) process = new AdminProcess();

HttpContext.Request.Headers["Content-Type"] = "charset=utf-8";

if (httpContextA == null)
    httpContextA = new HttpContextAccessor();

httpContextA.HttpContext = HttpContext; // Transfer HttpContext to process

initModel();                                // Model initialisation
process.initProcess(httpContextA, p);       // Process initialisation
process.isDebug = p.isDebug;

process.checkSession();                     // Session control
process.checkAuthorized();                  // User authorisation check

procData = process.doPost();                // Execute page process / dispatch actions to API

C# — HttpContext Initialisation

C# — initProcess / initContext
public void initProcess(IHttpContextAccessor httpContextA, BaseParameters p)
{
    initContext(httpContextA);
    initParameters(p);
}

// For initializing HttpContext, Request, Response and Session
public void initContext(IHttpContextAccessor httpContextA)
{
    this.httpContextA = httpContextA;
    request  = httpContextA.HttpContext.Request;
    response = httpContextA.HttpContext.Response;
    session  = httpContextA.HttpContext.Session;
}

C# — Search Action & JSON Response

C# — select Action
if (p.action.Equals("select"))
{
    formSize = p.selectFields.Length;
    p.formData = new string[formSize];
    p.dateFormatter.setIsForm(true);
    p.dateFormatter.setTimeFormat(BaseParameters.NO);

    for (int i = 0; i < formSize; i++)
        p.formData[i] = request.Form[p.selectFields[i]].ToString();

    dbQuery = p.execTableWR.tableAction(p.action, p.formData, p.prepQueryWR);

    if (dbQuery)
        p.tableData = (string[][])p.execTableWR.selectData(p.dataType);
    else
        p.tableData = null;

    if (p.tableData != null)
    {
        p.jsonConverter = new JsonConverter(p.fieldNames, p.tableData, p.execTableWR);
        jsonData  = p.jsonConverter.convert2Json();
        tableData = p.tableData;
    }
    messages = p.execTableWR.getMessages();

    return jsonData;
}

C# — INSERT / UPDATE / DELETE & JSON Response

C# — DML Operations
formSize = p.fieldNames.Length;
p.formData = new string[formSize];
p.dateFormatter.setIsForm(true);

if (isInsert) { p.formData[0] = "0"; n = 1; }

for (int i = n; i < formSize - 2; i++)
    p.formData[i] = request.Form[p.fieldNames[i]].ToString();

p.formData[formSize - 2] = p.sessionBean.getCode();
p.formData[formSize - 1] = now.ToString(dateTF);

dbQuery = p.execTableWR.tableActionWR(p.action, p.formData, p.prepQueryWR);

if (dbQuery)
{
    if (!isInsert) p.logBean.setLog(p, formSize);

    p.tableData = new string[1][];

    if (isInsert)
    {
        int k = 0;
        p.unqFieldData = new string[p.unqFields.Length];

        for (int i = 0; i < formSize; i++)
            if (i == p.unqMatchs[i]) { p.unqFieldData[k] = p.formData[i]; k++; }

        p.tableData[0] = p.execTableWR.getNewRecord(
            p.unqFields, p.unqFieldData, p.unqFieldTypes, p.prepQueryWR);
        p.formData[0] = p.tableData[0][0];
        p.logBean.setLog(p, formSize);
    }
    else
    {
        p.tableData[0] = p.formData;
        if (p.tableData[0] != null && p.tableData[0][5] != null)
            p.tableData[0][5] = p.dateFormatter.changeDateFormat(p.tableData[0][5]);
    }
    tableData = p.tableData;
}
else
{
    p.tableData   = null;
    messageString = "p.tableData is null!";
    errorString   = p.execTableWR.getErrorString();
    Console.WriteLine(messageString + "\n" + errorString);
}

p.jsonConverter = new JsonConverter(p.fieldNames, p.tableData, p.execTableWR);
jsonData = p.jsonConverter.convert2Json();
return jsonData;

Application Screenshots

📷 Application menu overview
CSBCA Menu
📷 HTML5 required-field validation on new record entry
HTML5 Required Field
📷 HTML5 date picker used for search filtering
HTML5 Date Picker
📷 Code form — full list view (English)
Code Form List
📷 Code form — modal update view
Code Form Modal

C# — Defining Form Parameters

Before anything else, the parameters for each page form must be defined.

C# — initParams()
public new void initParams()
{
    base.initParams();
    isDebug     = false;
    tableNames  = new string[] { "csbc_user", "vt_csbc_user" };
    fieldNames  = new string[] { "id","user_name","user_code","user_psw","user_group",
                                  "e_mail","rec_status","page_size","rec_user","rec_date" };
    inputTypes  = new string[] { "number","text","text","password","text",
                                  "email","text","number","text","datetime-local" };
    selectFields = new string[] { "id","user_name","user_group","e_mail","rec_user","rec_date1","rec_date2" };
    selectITs    = new string[] { "number","text","text","email","text","date","date" };
    setLabels();

    colWidths    = new int[] { 10, 30, 10, 10, 10, 30, 10, 10, 10, 15 };  // Excel column widths
    readOnlyFM   = new int[] { 0, -1, -1, -1, -1, -1, -1, -1, 8, 9 };    // Read-only field map
    invisibleFM  = new int[] { -1, -1, -1, 3, -1, -1, -1, -1, -1, -1 };  // Invisible field map
    requiredFM   = new int[] { -1, 1, 2, 3, 4, 5, -1, -1, -1, -1 };      // Required field map
    equalityInt  = new int[] { EQ, LK, EQ, LK, EQ, BGE, SME };
    unqFields    = new string[] { "user_code" };
    unqFieldTypes = new int[] { STRING };
    unqMatchs    = new int[] { -1, -1, 2, -1, -1, -1, -1, -1, -1, -1 };
    fieldTypes   = new int[] { INTEGER, STRING, STRING, STRING, STRING, STRING, STRING, INTEGER, STRING, DATE };
    selectFTs    = new int[] { INTEGER, STRING, STRING, STRING, STRING, DATE, DATE };
    dataType     = STRING;
    setExcelCols();
    orderBy      = "id";

    refTableNames  = new string[] { "csbc_group", "csbc_status", "csbc_user" };
    refFieldNames  = new string[] { "group_name", "status_name", "user_code" };
    refFieldMatchs = new int[] { -1, -1, -1, -1, 4, -1, 6, -1, 8, -1 };

    dateFormatter = new DateFormatter();
    dateFormatter.setTimeFormat(HM);

    prepQueryWR = new PrepQueryWithRef();
    prepQueryWR.convert2EQLs(equalityInt);

    execTableWR = new ExecTableWithRef(runDB, tableNames, fieldNames, fieldTypes, orderBy,
                        selectFields, selectFTs, dateFormatter,
                        refTableNames, refFieldNames, refFieldMatchs, messageBean);

    passwordBean   = new PasswordBean(runDB, tableNames[0], fieldNames[0], fieldNames[3]);
    managePassword = new ManagePassword(defPasw, passwordBean, defMask);
    manageSelList  = new ManageSelList(runDB, refTableNames, refFieldNames);

    execTableWR.isAfterCommit = false;
    execTableWR.isDebug       = isDebug;
    authorized  = new string[] { "Admin" };
    isModal     = true;   // true = modal dialog, false = inline update form
    isWithRefs  = true;   // true = use reference/lookup tables
}
💡 After solid analysis and a well-designed relational schema, adopting a strong architectural framework like CSBCA can save 30–40% of development time on enterprise software projects.