ORIGAM has its own database layer with a custom ORM — for both functional and historical reasons.
First, when ORIGAM development began in the early 2000s, ORMs were still a novelty. At the same time, Microsoft seemed to introduce a new database layer every two years. We needed something more stable than that.
Second, our MDA approach required different considerations for each layer — frontend, backend, and database. Since ORIGAM is all about interpreting a model, we decided to build our own version of what an ORM would look like if we were writing the code ourselves.
Model
The data layer model is defined in Origam.Schema.EntityModel.
The main components are:
- Entity model – describes most aspects of the data entities. These are typically mapped 1:1 to database tables or views, and can also include models for various filter expressions.
- Data Structures – define queries by combining entities into relationships and grouping predefined entity filters into filter sets (combinations of filters).
- Deployment – contains SQL scripts executed when updating an application with a new model (e.g., to create new tables).
With these advanced modeling elements, we can design all kinds of complex SQL queries and manage the database throughout its entire lifecycle.
Interpretation
The data layer interpreter is primarily contained in the Origam.DA namespace.
The main classes that handle data access are implementations of the IDataService interface:
- MsSqlDataService – for Microsoft SQL Server
- PgSqlDataService – for PostgreSQL
These classes are the only ones with direct access to the database. All database operations go through their methods:
- LoadDataSet – loads an entire dataset (a set of entities) based on a Data Structure and its filter set.
- StoreData – saves all changes in the previously loaded dataset (updates, inserts, deletes).
Generating SQL
Classes responsible for generating SQL from the model are located in the Origam.DA.Service.Generators folder.
SQL Injection
A great deal of care went into designing the engine to ensure it is safe from SQL injection attacks. No user input is ever inserted directly into generated statements — everything is passed through parameters.
The only exception is how we handle Data Constants.
Originally, these constants existed only in the model, so rendering them directly into SQL was safe: they were hardcoded by developers and never changed. Later, we introduced user-defined values (allowing administrators to override constant values stored in the database). However, the SQL rendering process remained the same.
As a result, these are the only places where we explicitly escape strings — see here.