YndigoBlue · Velocity Framework

One unified API for cross-vendor .NET database access

Write your data layer once. Deploy it against DB2, MySQL, Oracle, PostgreSQL, SQL Server, SQLite or Teradata with zero code changes.

OrderRepository.cs
using YndigoBlue.Velocity.Engine;
using YndigoBlue.Velocity.Model;
using YndigoBlue.Velocity.Attributes;

public class Order
{
    [VelocityField("order_id")]   public int    OrderId   { get; set; }
    [VelocityField("customer")]   public string Customer  { get; set; }
    [VelocityField("total")]      public decimal? Total    { get; set; }
    [VelocityField("created_at")] public DateTime? Created { get; set; }

    // Datastore is your application's own static holder for the loaded Schema
    public static Table Table { get; } = Datastore.Schema["order"];
}

// Type-safe query, no raw SQL
var query = new Query()
    .Select([Order.Table["order_id"],
             Order.Table["customer"],
             Order.Table["total"]])
    .From(Order.Table)
    .Where(new Criterion<decimal>(Order.Table["total"], ConditionalType.GreaterThan, 100m))
    .OrderBy(Order.Table["created_at"], OrderClauseType.Descending);

// Works identically on every supported database
using var manager = new Manager(connection);
using var results = manager.Retrieve(query);
IList<Order> orders = results.BindResults<Order>(BindingType.Attribute);
7
Supported Databases
1
Unified API
.NET 10
Target Framework
100%
Type-Safe Queries
Capabilities

Everything your data layer needs

Velocity handles the complexity of cross-database access so you can focus on your application, not the plumbing.

Cross-Vendor Compatibility

Connect to DB2, MySQL, Oracle, PostgreSQL, SQL Server, SQLite and Teradata through one consistent API. Switch vendors by changing a single configuration line. Writing a Cross-Database .NET Application works through what that takes in practice.

Single Codebase

Write your data access layer once with no vendor-specific SQL dialects or separate repositories. One clean, maintainable codebase that runs everywhere.

Type-Safe Query Builder

Compose queries with a fluent, type-safe API, with no raw SQL strings anywhere in your application. Criteria, joins, aggregates and ordering all handled at the .NET layer, with results mapped straight onto your own classes. Data Binding for Relational Tables covers the mapping in full.

Declarative Schema Management

Define your database schema in XML, JSON or YAML and load it with a few lines of code. Version, migrate and refresh schemas across environments with confidence.

Batch Data Import

Import large datasets efficiently with Velocity's unified batch API. Handles bulk inserts and transactions consistently across all supported vendors.

Geospatial Support

First-class support for Geography and Geometry types. Store and query spatial data using familiar .NET types, with no vendor-specific spatial SQL required. See Spatial Data Across Relational Databases for how the same query behaves on each engine, and Importing and Exporting GeoJSON for moving features in and out.

Compatibility

Runs on every major database

Whether your organization standardizes on an enterprise warehouse or a lightweight embedded database, Velocity has you covered.

DB2
MySQL
Oracle
PostgreSQL
SQL Server
SQLite
Teradata
Code

Clean. Expressive. Familiar.

Velocity's API feels natural to any .NET developer. Attribute-based model mapping, a composable query builder and strongly-typed results, with no raw SQL anywhere.

using YndigoBlue.Velocity.Attributes;
using YndigoBlue.Velocity.Model;

/// <summary>
/// Decorate properties with [VelocityField] to map columns to .NET properties automatically.
/// </summary>
public class Product
{
    [VelocityField("product_id")]
    public int ProductId { get; set; }

    [VelocityField("product_name")]
    public string Name { get; set; }

    [VelocityField("unit_price")]
    public decimal Price { get; set; }

    [VelocityField("category")]
    public string Category { get; set; }

    [VelocityField("created_at")]
    public DateTime CreatedAt { get; set; }

    // Resolved once from the schema, then reused everywhere. Datastore is your
    // application's own static holder for the Schema it loaded at startup.
    public static Table Table { get; } = Datastore.Schema["product"];
}
using YndigoBlue.Velocity.Engine;
using YndigoBlue.Velocity.Model;

// Manager is the entry point and owns the connection for its lifetime
using var manager = new Manager(connection);

// Compose a type-safe query with no raw SQL strings in application code
// Multiple criteria are combined in an array with BooleanItem connectors
var query = new Query()
    .Select([Product.Table["product_id"],
             Product.Table["product_name"],
             Product.Table["unit_price"],
             Product.Table["category"]])
    .From(Product.Table)
    .Where([
        new Criterion<decimal>(Product.Table["unit_price"], ConditionalType.GreaterThan, 50.00m),
        new BooleanItem(BooleanType.And),
        new Criterion<string>(Product.Table["category"], "Electronics")
    ])
    .OrderBy(Product.Table["unit_price"], OrderClauseType.Descending);

// BindResults maps each result row to a typed object via [VelocityField]
IList<Product> products = manager
    .Retrieve(query)
    .BindResults<Product>(BindingType.Attribute);

// Update a record: pass the criterion to the constructor and use the typed Set methods
var update = new Update(Product.Table,
    new Criterion<int>(Product.Table["product_id"], 42));
update.SetFieldDecimal("unit_price", 45.00m);
manager.UpdateRecords(update);
using YndigoBlue.Velocity.Engine;
using YndigoBlue.Velocity.Model;

var orderTable = Datastore.Schema["order"];
var customerTable = Datastore.Schema["customer"];

// Aggregates: count, sum, avg, min, max
var revenueExpr = new Expression("total_revenue", new Aggregate(AggregateType.Sum, orderTable["amount"]));
var countExpr = new Expression("order_count", new Aggregate(AggregateType.Count, orderTable["order_id"]));

// Join on customer_id and group by customer. The constructor defaults to an inner
// equality join; other overloads take the join type and operator explicitly.
var query = new Query()
    .Select([customerTable["name"], revenueExpr, countExpr])
    .From(new Join(orderTable, customerTable, "customer_id"))
    .GroupBy(customerTable["name"])
    .OrderBy(revenueExpr);

// Bind to a dedicated result model
IList<CustomerRevenue> topCustomers = manager
    .Retrieve(query)
    .BindResults<CustomerRevenue>(BindingType.Attribute);
using YndigoBlue.Velocity.Engine;
using YndigoBlue.Velocity.Enums;
using YndigoBlue.Velocity.Model;

// Load schema from a versioned XML file and cache it globally
Datastore.Schema = manager.ReadSchemaFromFile("Config/myapp-v3.xml", ConfigType.Xml);

// Verify the live database matches, and fall back to the previous version if not
Schema liveSchema = manager.LoadSchema("myapp");
if (!liveSchema.Equals(Datastore.Schema))
{
    Datastore.Schema = manager.ReadSchemaFromFile("Config/myapp-v2.xml", ConfigType.Xml);
}

// Deploy a schema update (e.g. new columns, new tables) in a transaction
string newSchemaXml = File.ReadAllText("Config/myapp-v4.xml");
Datastore.Schema = manager.ReadSchema(newSchemaXml, ConfigType.Xml);

manager.BeginTransaction();
manager.UpdateSchema(Datastore.Schema);
manager.CommitTransaction();

// Rebuild the schema from scratch, dropping and recreating every table
manager.BuildSchema(Datastore.Schema, overwrite: true);
using YndigoBlue.Velocity.Engine;
using YndigoBlue.Velocity.Model;

// ── CSV import ──
// ImportConfig takes the target table, date format, and whether to retain identity values from file
var config = new ImportConfig(Product.Table, DateFormatType.Iso, keepIdentity: true);
manager.ImportData(Product.Table, "data/products.csv", config);

// ── Export a schema's definition and every table's data to a folder ──
manager.ExportSchema(
    Datastore.Schema,
    ConfigType.Xml,
    "data/export/",
    new ExportConfig(DateFormatType.Iso));

// ── Insert a single record using typed field setters ──
var record = new Record(Product.Table);
record.SetFieldInteger("product_id", 1001);
record.SetFieldVarChar("product_name", "Widget Pro");
record.SetFieldDecimal("unit_price", 49.99m);
record.SetFieldVarChar("category", "Electronics");
record.SetFieldDateTime("created_at", DateTime.UtcNow);
manager.AddRecord(record);
using YndigoBlue.Velocity.Engine;
using YndigoBlue.Velocity.Enums;
using YndigoBlue.Velocity.Functions;
using YndigoBlue.Velocity.Model;

// ── Store a geography value ──
Geography london = GeospatialUtils.GeographyFromText("POINT (-0.1276 51.5074)");

var record = new Record(Location.Table);
record.SetFieldVarChar("name", "London");
record.SetFieldGeography("coordinates", london);
manager.AddRecord(record);

// ── Proximity query: find all locations within 50 km of a reference point ──
Geography origin = GeospatialUtils.GeographyFromText("POINT (-1.8904 52.4862)");

// SpatialDistance computes ST_Distance between the column and the reference point
var distanceFn = new SpatialDistance(
    Location.Table["coordinates"],
    origin,
    UnitOfMeasure.Kilometer);

// Wrap in an Expression so it can appear in both SELECT and WHERE
var distanceExpr = new Expression("distance_km", distanceFn);

var query = new Query()
    .Select([Location.Table["name"], Location.Table["coordinates"], distanceExpr])
    .From(Location.Table)
    .Where(new Criterion<double>(distanceExpr, ConditionalType.LessThan, 50.0))
    .OrderBy(distanceExpr, OrderClauseType.Ascending);

IList<Location> nearby = manager
    .Retrieve(query)
    .BindResults<Location>(BindingType.Attribute);
How It Works

One API between your code and every database

Your application describes what it wants with Velocity's types. Velocity runs it directly on the database and hands the results back as your own objects.

Your .NET application
ASP.NET, Azure Functions or any other .NET code
Model classes

Your own classes, with [VelocityField] attributes mapping properties to columns.

Queries and records

Built with Query, Record and Update instead of SQL strings.

Schema definitions

XML, JSON or YAML files that describe your tables, views and indexes.

queries in,
objects out
Velocity Framework
One API for every supported database
  • Works the same on every database
  • Maps results onto your classes
  • Builds and updates schemas
  • Imports and exports data
  • Stores and queries spatial data
No vendor-specific SQL in your code
Foundry runs on
the same engine
Foundry

Velocity from the command line, for schema builds, data loads and deployments in your CI/CD pipeline.

runs directly
on each engine
Your databases
Switch by changing the connection, not your code
  • DB2
  • MySQL
  • Oracle
  • PostgreSQL
  • SQL Server
  • SQLite
  • Teradata

Ready to get started?

The full Velocity Framework documentation covers installation, connection configuration, schema management, query building, joins, aggregates, geospatial data, and batch import.