🚀 Welcome to MDriven Learn –  MDriven is now on Discord!  Don’t miss the latest Release Notes.
PSExpression , or how to do things in the DB from MDriven
This page was created by Alexandra on 2018-01-09. Last edited by Wikiadmin on 2026-07-29.

You can use the legacy PSExpression_ ViewModel-column pattern to calculate database-backed values without loading every matching object; for new work, use PSEval operators instead.

Status: use PSEval for new development

PSExpression_ columns are the older way to evaluate a database-translated OCL (OCLps) expression from a ViewModel. The newer PSEval, PSEvalValue, and PSEvalTuples operators provide direct database access without relying on a column-name convention.

Use this page when you maintain an existing ViewModel that has columns named PSExpression_.... For a new ViewModel expression, start with PSEval.

Why evaluate in the database?

MDriven normally evaluates OCL against objects. When an expression reaches an association that has not yet been loaded, MDriven can fetch the related objects. This is appropriate for ordinary object navigation and UI work.

For a large query, loading objects before calculating an aggregate can be unnecessary. OCLps is the subset of OCL that MDriven translates to SQL and evaluates in the persistence storage (the relational database). The database returns the result, or, for object-result queries, the identities that MDriven then resolves to objects.

For example, a normal object-oriented expression such as:

Order.allInstances->select(o | o.Customer = self)->size

can be used as a database-backed count. This lets the database calculate the count instead of transferring every matching Order object to calculate size in memory.

Use database evaluation when:

  • You need a count, sum, minimum, maximum, or average over a large number of rows.
  • You need to find a small result, such as one object, in a very large table.
  • An existing ViewModel uses a PSExpression_ column.
  • You need to display the result of sqlPassthrough.

Do not use it as a replacement for all OCL. OCLps is intentionally limited; use normal OCL after MDriven has loaded the object identities when you need operations that OCLps does not support.

How the legacy PSExpression_ pattern works

A legacy PSExpression_ column is a ViewModel column whose expression is evaluated through OCLps when you call selfVM.PSExpression_Refresh().

  1. Add a column to the ViewModel for the value you want to show.
  2. Name the column with the prefix PSExpression_. For example, PSExpression_TotalOrders.
  3. Enter an OCLps-compatible expression in the column expression.
  4. In the ViewModel column properties, select IsExp. The prefix identifies the column for refresh; the expression setting enables MDriven to treat it as a database-driven expression.
  5. Add an action that calls selfVM.PSExpression_Refresh() when the values must be recalculated.
  6. Bind the column in the ViewModel UI as you would any other ViewModel value.

When the action runs, MDriven finds the current ViewModel's columns whose names start with PSExpression_, evaluates their expressions in the database, writes the scalar results back to the columns, and updates the UI. Columns without that prefix are ignored.

Refresh all legacy database-expression columns

Call the refresh method from an EAL action on the current ViewModel:

selfVM.PSExpression_Refresh()

The method takes no arguments and returns no value. It refreshes all qualifying PSExpression_ columns in the current ViewModel, not one named column.

Example: refresh an aggregate after an update

Assume a Customer ViewModel needs to show how many orders belong to the current customer.

ViewModel column Expression Purpose
PSExpression_TotalOrders o.Customer = self)->size Returns the number of orders for the current customer.

After an action creates or changes an order, refresh the database-computed values:

let newOrder = Order.Create() in newOrder.Customer := self;
selfVM.PSExpression_Refresh()

The field bound to PSExpression_TotalOrders then receives the refreshed count. The database performs the count; MDriven does not need to load all matching Order objects to calculate it.

Use raw SQL only when OCLps is not enough

sqlPassthrough lets an OCLps expression send SQL to the database. Use it when the database operation is practical in SQL but not expressible, or not appropriate, in OCLps.

For example, a stored procedure call can be expressed from a class context:

Anvandare.sqlpassthrough('sp_Upd_Losen ' + self.AnvId.asString + ', \'\' + self.Anvandarnamn + '\', @aNyttLosenord', Int32)

In this example:

  • The expression starts with a class, not an object.
  • String literals passed to SQL must be quoted. In OCL, use escaped quotes (\').
  • @aNyttLosenord is available because it is a variable in scope when the expression is called.
  • Int32 specifies the SQL Server return-code type.

A sqlPassthrough expression can also return tuple data. For example, SQL can return a key and two aggregates, which an OCL expression can map into transient objects:

AccountPlan.SQLPassthrough('select somekey,sum(somestuff),sum(someotherstuff) from table1,2,3 where ...',String,Integer,Integer)->collect(xtuple|let xobject=SomeNewTransient.Create in (xobject.Key:=xtuple.Part1;xobject.SomeSum:=xtuple.Part2))

Raw SQL couples the expression to the physical database schema. Keep that trade-off explicit: prefer OCLps where it expresses the query, and use sqlPassthrough only where database-specific SQL is required. See sqlPassthrough for its syntax and additional examples.

OCLps limits and data freshness

OCLps is not full OCL. It has no side effects, and it cannot call your own methods, including methods marked IsQuery. OCLps also does not support collect, groupBy, and other operators that return tuples when used as ordinary OCLps queries. Its primary purpose is to identify database objects or calculate database values; continue with normal OCL after the required objects are loaded.

Database evaluation sees saved database data. If you change objects in memory and have not saved those changes, a subsequent database query can return the old database value. Save the changes before refreshing a PSExpression_ value when the result must include those changes.

For validation rules that must run against stored data, see Validate data in the database.

Choosing the right approach

Need Use Example
Navigate and work with already loaded objects Normal OCL Follow an Order's Customer and show its name.
Filter a large database-backed object set OCLps Find matching object identities without loading the whole table.
Return a scalar or tuple directly from a database expression in new work PSEval operators Calculate a count or value for a ViewModel expression.
Maintain a legacy ViewModel database-computed column PSExpression_ plus selfVM.PSExpression_Refresh() Refresh PSExpression_TotalOrders after an update.
Run database-specific SQL or a stored procedure sqlPassthrough Return an SQL Server procedure return code.

See also