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");