QSAppContext

QSAppContext is a built‑in framework object that provides centralised access to data operations, dynamic function execution, and controller loading. It is available directly in code blocks and inside dynamic controllers without any manual instantiation or dependency injection.

Through this object you can:

  • Access registered data tables (Tables)
  • Execute custom business logic functions (ExecuteFunctionAsync)
  • Run ad‑hoc SQL queries or stored procedures
  • Save pending model changes (SaveChangesAsync)
  • Commit specific batches (SaveBatchOnlyAsync, SaveBatchAndQssAsync)
  • Call dynamic functions by ID or name
  • Load dynamic controllers by name

All methods are asynchronous and return Task‑based results, designed for the QS framework’s data‑centric and plugin‑based architecture.

Tables Property

Tables

public IQSDataTableCollection Tables => _dataTableCollection;

Provides access to the collection of all registered data tables. Use it to obtain an IQSDataTable instance by name, which then allows you to call data retrieval and modification methods.

Example

// Access the Customers table
IQSDataTable customers = QSAppContext.Tables["Customers"];
DataTable dt = await customers.GetByIdAsync("CUST-001");

ExecuteFunctionAsync (Overloaded)

Executes a custom business‑logic function on a specific record. Four overloads are available, depending on whether you identify the function and base type by Guid or by name. The recommended overload is the one that uses Guid for both function and base type, as it avoids extra lookups and ambiguity.

Overload 1 (All Guids) ⭐ Recommended

public async Task<Dictionary<string, object>> ExecuteFunctionAsync(
    Guid functionId,
    Guid baseTypeId,
    string recordId
)

Uses Guids for both function and table, most direct.

Overload 2 (All Names)

public async Task<Dictionary<string, object>> ExecuteFunctionAsync(
    string functionName,
    string baseTypeName,
    string recordId
)

Uses names for both; table is looked up via Tables[baseTypeName].

Overload 3 (Function Guid, Base Type Name)

public async Task<Dictionary<string, object>> ExecuteFunctionAsync(
    Guid functionId,
    string baseTypeName,
    string recordId
)

Combines function Guid with table name.

Overload 4 (Function Name, Base Type Guid)

public async Task<Dictionary<string, object>> ExecuteFunctionAsync(
    string functionName,
    Guid baseTypeId,
    string recordId
)

Combines function name with table Guid.

Example (Recommended Overload)

// Recommended overload: all Guids
Guid functionId = Guid.Parse("a1b2c3d4-...");
Guid customersTableId = Guid.Parse("e5f6a7b8-...");
string recordId = "CUST-001";

Dictionary<string, object> result = await QSAppContext
    .ExecuteFunctionAsync(functionId, customersTableId, recordId);

ExecuteSelectCommandToDictionaryAsync

public async Task<List<Dictionary<string, object>>> ExecuteSelectCommandToDictionaryAsync(SqlCommandDto command)

Executes a SELECT command and returns the result as a list of dictionaries (column name → value). Ideal for data transfer or dynamic processing.

Example

SqlCommandDto cmd = new SqlCommandDto
{
    CommandText = "SELECT Id, Name FROM QS.Customers WHERE Status = @Status",
    Parameters = new SqlCommandParameterDtoCollection()
};
cmd.Parameters.AddString("Status", "Active");
List<Dictionary<string, object>> rows = await QSAppContext.ExecuteSelectCommandToDictionaryAsync(cmd);

ExecuteSelectCommandToDataTableAsync

public async Task<DataTable> ExecuteSelectCommandToDataTableAsync(SqlCommandDto command)

Executes a SELECT command and returns a full DataTable. The command is automatically configured with safety checks enabled and CRUD operations not skipped.

Example

SqlCommandDto cmd = new SqlCommandDto
{
    CommandText = "SELECT Id, Name, TotalSales FROM QS.Customers WHERE TotalSales > @MinSales"
};
cmd.Parameters.AddDecimal("MinSales", 10000M, SqlDbType.Decimal);
DataTable dt = await QSAppContext.ExecuteSelectCommandToDataTableAsync(cmd);

ExecuteSelectStoredProcedureToDictionaryAsync

public async Task<List<Dictionary<string, object>>> ExecuteSelectStoredProcedureToDictionaryAsync(SqlCommandDto command)

Executes a stored procedure and returns the result as a list of dictionaries. The CommandType must be set to StoredProcedure.

Example

SqlCommandDto cmd = new SqlCommandDto
{
    CommandText = "GetActiveCustomersByMinSales",
    CommandType = CommandType.StoredProcedure
};
cmd.Parameters.AddDecimal("MinSales", 10000M, SqlDbType.Decimal);
List<Dictionary<string, object>> rows = await QSAppContext.ExecuteSelectStoredProcedureToDictionaryAsync(cmd);

ExecuteSelectStoredProcedureToDataTableAsync

public async Task<DataTable> ExecuteSelectStoredProcedureToDataTableAsync(SqlCommandDto command)

Executes a stored procedure and returns a DataTable. The stored procedure name and parameters are passed via SqlCommandDto.

Example

SqlCommandDto cmd = new SqlCommandDto
{
    CommandText = "GetActiveCustomersByMinSales",
    CommandType = CommandType.StoredProcedure
};
cmd.Parameters.AddDecimal("MinSales", 10000M, SqlDbType.Decimal);
DataTable dt = await QSAppContext.ExecuteSelectStoredProcedureToDataTableAsync(cmd);

SaveChangesAsync

public async Task<int> SaveChangesAsync()

Persists all pending changes made to the screen model (Qss) back to the database. Does not depend on any batch ID – it saves the current model state.

Example

Qss["NameAr"] = "الاسم الجديد";
Qss["Email"] = "updated@example.com";
int rows = await QSAppContext.SaveChangesAsync();

SaveBatchOnlyAsync

public async Task<int> SaveBatchOnlyAsync(Guid BatchId)

Commits only the operations queued under the specified BatchId, ignoring any Qss model changes.

Example

Guid batchId = Guid.NewGuid();
IQSDataTable orders = QSAppContext.Tables["Orders"];
await orders.AddRecordByDictionaryAsync(
    new Dictionary<string, object?> { {"OrderId", "ORD-100"} },
    batchId
);
int rows = await QSAppContext.SaveBatchOnlyAsync(batchId);

SaveBatchAndQssAsync

public async Task<int> SaveBatchAndQssAsync(Guid BatchId)

Saves both the Qss model changes and the queued batch in a single call. Ideal for “save everything” scenarios.

Example

Qss["NameAr"] = "مؤسسة النجاح";
Guid batchId = Guid.NewGuid();
IQSDataTable contacts = QSAppContext.Tables["Contacts"];
await contacts.AddRecordByDictionaryAsync(
    new Dictionary<string, object?> { {"Id", "CNT-001"} },
    batchId
);
int totalRows = await QSAppContext.SaveBatchAndQssAsync(batchId);

Dynamic Function Execution

Two methods to execute dynamic functions: by ID and by name. They return dynamic results and throw exceptions on error.

ExecuteDynamicFunctionByIdAsync

public async Task<dynamic> ExecuteDynamicFunctionByIdAsync(string functionId, object[] parameterValues)
string functionId = "b7f8c9e0-1234-4a5b-9c8d-1e2f3a4b5c6d";
object[] args = new object[] { 25, "VIP", true };
dynamic result = await QSAppContext.ExecuteDynamicFunctionByIdAsync(functionId, args);

ExecuteDynamicFunctionByNameAsync

public async Task<dynamic> ExecuteDynamicFunctionByNameAsync(string functionName, object[] parameterValues)
string functionName = "CalculateCustomerDiscount";
object[] args = new object[] { 1500.75M, "Gold" };
dynamic result = await QSAppContext.ExecuteDynamicFunctionByNameAsync(functionName, args);

LoadDynamicController

public async Task<dynamic> LoadDynamicController(string controllerName)

Dynamically loads a controller assembly by name and returns the controller instance as dynamic.

Example

string controllerName = "CustomerReportController";
dynamic controller = await QSAppContext.LoadDynamicController(controllerName);
var report = controller.GenerateReport("CUST-001");