- A few words on LINQ
You can use LINQ in MDriven code to query objects already loaded in memory or to let MDriven translate a supported query into an OCLps query that retrieves matching objects from persistent storage.
- Choose the right kind of query
MDriven objects are .NET objects. This means you can use ordinary LINQ over objects that are already available in your `EcoSpace`.
When the possible result set is large, use `PSQuery<T>()`. MDriven interprets the LINQ expression, translates it to OCLps (Object Constraint Language for Persistent Storage), and sends the query to the persistence storage. For a relational database, this means MDriven translates the query to SQL.
| Your situation | Use | What happens | |---|---|---| | The objects are already loaded and you want to filter or project them in code | Standard in-memory LINQ | .NET evaluates the query over the objects in memory. | | You need matching objects from persistent storage without loading every candidate object | `EcoSpace.PSQuery<T>()` | MDriven translates the supported LINQ expression to OCLps and retrieves the identities of matching objects. MDriven then resolves those identities to objects. | | You are writing model rules, ViewModel expressions, derivations, or constraints | OCL in the model | MDriven type-checks the expression when the model is loaded, saved, or checked. |
- Use standard LINQ for objects in memory
Use normal LINQ when the collection you query is already in memory. For example, this query starts with all loaded instances of `Class1`, filters them by `Attribute1`, and returns the attribute values:
```csharp var standardLinq =
from f in EcoSpace.Extents.AllInstances<Class1>() where f.Attribute1 == "1" select f.Attribute1;
```
This query does **not** ask persistent storage to find the matching objects. If `Class1` contains many objects that have not been loaded, this approach can require MDriven to fetch objects while the query runs.
Use this approach when you already have the required objects or when the collection is small enough to process in memory.
- Use `PSQuery<T>()` to filter in persistent storage
Use `EcoSpace.PSQuery<T>()` when you want persistent storage to identify the matching objects before MDriven loads them.
```csharp var matchingClass1 =
from x in EcoSpace.PSQuery<Class1>() where x.Attribute1 == "1" select x;
foreach (Class1 item in matchingClass1) {
// Work with each fetched Class1 object.
}
int count = matchingClass1.ToArray().Length; ```
In this example, the query asks persistent storage for `Class1` objects where `Attribute1` is `"1"`. It returns `Class1` objects, not an untyped database result set.
This avoids loading every `Class1` object only to discard most of them in a `where` clause. It is particularly important when the candidate set contains hundreds, thousands, or more objects.
- Why this can be faster
A loop that accesses an unloaded relation can cause repeated lazy fetch operations. For example, if code iterates through many objects and accesses an unloaded relation for each one, MDriven may need to fetch data repeatedly.
A declarative query states the result you need. With `PSQuery<T>()`, MDriven can evaluate the supported filter in persistent storage and retrieve the matching object identities first. MDriven then performs its normal fetch operations to resolve those identities to objects.
- What MDriven translates
A `PSQuery<T>()` expression is translated to OCLps, not directly written as database-specific SQL. OCLps is the persistence-storage subset of OCL. This lets the same model-oriented query approach work with the supported persistence mappers, including relational databases, XML, and memory-based persistence.
You can inspect the OCL expression produced by a persistence query:
```csharp var matchingClass1 =
from x in EcoSpace.PSQuery<Class1>() where x.Attribute1 == "1" select x;
string ocl =
(matchingClass1 as EcoQuery<Class1>)
.GetResultingOclExpression();
```
For this example, the resulting OCL is similar to:
```ocl Class1.allInstances.select(GenSym_0|GenSym_0.Attribute1 = '1') ```
The generated variable name, such as `GenSym_0`, is an implementation detail. Focus on the query meaning: select all `Class1` instances whose `Attribute1` equals `'1'`.
- Persistence-query limits
`PSQuery<T>()` is intended to find model objects in persistent storage. Its translated OCLps expression is a subset of OCL.
Keep these limits in mind:
- Select objects. Persistence queries retrieve object identities and MDriven resolves them to objects. - Do not expect tuple-producing operations to work. OCLps does not support `collect`, `groupby`, or other operators that return tuples. - Do not use operations with side effects. OCLps is a query language. - Do not expect every SQL function or every LINQ expression to be translatable. - Do not rely on your own model methods in OCLps, even if a method is marked `IsQuery`.
For example, this pattern stays within the intended use because it returns `Class1` objects:
```csharp var matchingClass1 =
from x in EcoSpace.PSQuery<Class1>() where x.Attribute1 == "1" select x;
```
By contrast, a projection that attempts to return a custom tuple is not a suitable persistence query result. Retrieve the matching objects first, then perform any object-level projection in memory.
For the complete OCLps rules and supported usage, see Documentation:OCLps.
- Filter by subtype in persistent storage
Use `OfType<T>()` after a persistence query when you need persistent storage to restrict the result to a subclass.
```csharp var matchingSubclasses =
(from v in EcoSpace.PSQuery<Class2>()
where v.Class1.Attribute1 == "5" && v.Name == "5A"
select v.Class1)
.OfType<SomeSubClass>();
```
This query returns `SomeSubClass` objects whose related `Class2` has the requested values. The subtype filter is part of the translated persistence query, so the filtering occurs before MDriven returns the objects.
See Documentation:Further Linq enhancements for the background and additional detail.
- Do not use `MemQuery<T>()` when standard LINQ is enough
MDriven also provides `EcoSpace.MemQuery<T>()`:
```csharp var memoryQuery =
from x in EcoSpace.MemQuery<Class1>() where x.Attribute1 == "1" select x;
```
For ordinary in-memory work, prefer standard LINQ over the objects you already have. The standard LINQ form is clearer and does not require MDriven-specific query infrastructure.
- LINQ in code; OCL in the model
Use LINQ when you need a query in C# code. LINQ is compiled and strongly typed, which helps you catch errors during compilation.
Use OCL when the rule belongs in the model. OCL is declarative: you describe the result or rule rather than the steps required to calculate it. MDriven uses OCL for model-level concepts such as constraints, derived attributes, derived associations, ViewModel columns and nestings, presentation expressions, action enable expressions, and state-machine guards.
For example, an OCL constraint or derivation can express a rule once in the model rather than repeating equivalent C# code across clients. MDriven dynamically type-checks OCL, EAL, and OCLps when the model is loaded, saved, or checked with **ModelCheck**.
OCL expressions have no side effects. When you need to change data in model-defined behavior, MDriven uses EAL (Extended Action Language), which uses the same syntax family for actions.
Read Documentation:OCL Expressions and Documentation:Learn OCL for OCL usage, and Training:Certain important constructs for methods and the `IsQuery` distinction.
- Derived values and persistence queries
A derivation defined in the model is not itself available as a database column. When a LINQ or OCL query is translated for persistent storage, MDriven expands derivations into their persistent members so that the query can be evaluated.
For example, if `Apartment.TheDogs` is derived from occupants and pets, a persistence query for apartments with a dog named `Benji` must ultimately query the underlying `Occupants`, `Pets`, and `PetType` members.
See Documentation:Derivation is not available in the database for the derivation-expansion behavior and examples.
- When to use other persistent-storage features
Use `PSQuery<T>()` when your code needs a set of model objects matching a supported predicate. If you need database-oriented aggregate work over very large data sets, or direct persistent-storage access from a ViewModel, review Documentation:PSExpression , or how to do things in the DB from MDriven. That page also points to the newer `PSEval`, `PSEvalValue`, and `PSEvalTuple` approaches.
- Summary
1. Use standard LINQ for objects already in memory. 2. Use `EcoSpace.PSQuery<T>()` to have persistent storage filter a large candidate set and return matching objects. 3. Keep persistence queries within the OCLps subset: query-only, object-oriented, and without tuple-producing operations. 4. Use LINQ for C# code and OCL for rules that belong in the model. 5. Inspect the generated OCL when you need to understand how a `PSQuery<T>()` expression is interpreted.
