- Introduction
- Philosophy & Design Goals
- Supported Databases
- Feature Overview
- Installation & Setup
- Model Definition
- Querying & CRUD
- Update & Patch Examples
- Batch Operations & Bulk Insert
- Partial Loading
- Unit of Work & Transactions
- Stored Procedures & Scalar Functions
- Virtual Views & Custom SQL
- Domain Primitives
- Serialization: JSON & XML
- SQL Compression/Decompression
- Change Tracking & Concurrency
- Logging & Exception Handling
- Extensibility & Advanced Configuration
- Enum Storage: Store Enums as Strings
- Contributing
- License
- Contact
- FAQ
AltaSoft.Storm is a lightning-fast, source-generator-powered ORM for .NET, designed to bring the best of type safety, performance, and developer ergonomics to your data layer. Storm eliminates runtime reflection, automates change tracking, and supports rich features for modern enterprise applications.
- Performance First: Uses C# source generators for compile-time bindings; no runtime reflection.
- Developer Happiness: Clear, type-safe APIs and auto-generated helpers for all CRUD and advanced scenarios.
- Extensibility: Easily supports new databases, serialization providers, and custom behaviors.
- Transparency: No magic or “black box” behavior; everything is open and documented.
- MSSQL — Fully supported, production-ready.
- Other DBs: PostgreSQL, MySQL, etc., planned for future releases. Community contributions welcome!
- Source Generator Bindings — Tables, views, stored procedures, functions, virtual views, custom SQL.
- Automatic Change Tracking — Efficient IL weaving for property change detection.
- Batch Operations & Bulk Insert — High-speed batch updates/inserts.
- Partial Loading — Load only the fields you need, including nested/detail tables.
- Unit of Work & Transactions — Robust transaction management.
- Stored Procedures & Scalar Functions — Strongly-typed execution and result mapping.
- Virtual Views & Custom SQL — Map models to SQL views, virtual views, or arbitrary SQL.
- Domain Primitives Support — Seamless integration with AltaSoft.DomainPrimitives.
- Serialization (JSON/XML) — Save/load complex properties as JSON (preferred) or XML.
- SQL Compression/Decompression — Efficiently store large strings as compressed data.
- Change Tracking & Concurrency — Optimistic concurrency and dirty-checking.
- Logging & Error Handling — Plug in your own logger, all errors use StormException.
- Table Hints, Schema Customization, Connection Management — Advanced configuration options.
- Open Source, MIT Licensed, Community-Driven.
- .NET 8 or higher
Add the package to your project:
<ItemGroup>
<PackageReference Include="AltaSoft.Storm.MsSql"/>
<PackageReference Include="AltaSoft.Storm.Generator.MsSql">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup>
<DefineConstants>$(DefineConstants);STORM_MSSQL</DefineConstants>
</PropertyGroup>
public sealed class MyAppContext:StormContext;
if (!StormManager.IsInitialized)
{
StormManager.Initialize(new MsSqlOrmProvider(), configuration =>
{
configuration.AddStormContext<MyAppContext>(dbConfig =>
{
dbConfig.UseConnectionString("your-connection-string");
dbConfig.UseDefaultSchema("dbo");
});
});
}
Bind C# classes to DB objects with rich attributes:
[StormDbObject<MyAppContext>(SchemaName = "dbo", ObjectName = "Users", DisplayName = "User Entity")]
public sealed partial class User
{
[StormColumn(ColumnType = ColumnType.PrimaryKeyAutoIncrement)]
public int Id { get; set; }
[StormColumn(DbType = UnifiedDbType.String, Size = 200)]
public string Name { get; set; }
public DateOnly BirthDate { get; set; }
[StormColumn(DbType = UnifiedDbType.Json)]
public List<Role> Roles { get; set; }
// Domain primitive support (see below)
public UserId UserId { get; set; }
}
- StormDbObject: Binds to table/view/SP/function; customize schema, name, context, display name, update mode.
- StormColumn: Controls DB type, size, precision, scale, name, save/load options, detail table name, concurrency, tracking, etc.
All CRUD and query methods are auto-generated by Storm:
await using var context = new MyAppContext("your-connection-string");
// Get by primary key
var user = await context.SelectFromUsersTable(1).GetAsync();
// List all
var users = await context.SelectFromUsersTable().ListAsync();
// Filtering, ordering, pagination
var filtered = await context.SelectFromUsersTable()
.Where(x => x.BirthDate < new DateOnly(2010, 1, 1))
.OrderBy(User.OrderBy.BirthDate_Desc)
.Top(10)
.ListAsync();
// Partial projection
var names = await context.SelectFromUsersTable().ListAsync(x => x.Name, x => x.BirthDate);
// Change tracking update
var user = await context.SelectFromUsersTable(1).WithTracking().GetAsync();
user.Name = "Updated Name";
await context.UpdateUsersTable().Set(user).GoAsync();
// Delete by key
await context.DeleteFromUsersTable(1).GoAsync();
AltaSoft.Storm supports expressive update and patch operations. You can update specific fields directly:
// Update a single field by key
await context.UpdatePersonTable(10).Set(x => x.Name, "NewName").GoAsync();
// Update multiple fields by key
await context.UpdatePersonTable(10)
.Set(x => x.Name, "NewName")
.Set(x => x.Age, 42)
.GoAsync();
// Patch by condition
await context.UpdatePersonTable()
.Where(x => x.Age < 18)
.Set(x => x.IsMinor, true)
.GoAsync();
// Set by key property
await context.UpdatePersonTable().Set(x => x.Id, 10).Set(x => x.Name, "Updated").GoAsync();
var batchUsers = new[] { user1, user2, user3 };
await context.UpdateUsersTable().Set(batchUsers).GoAsync();
await context.BulkInsertIntoUsersTable().Values(batchUsers).GoAsync();
Use partial load flags to optimize queries:
var userList = await context.SelectFromUsersTable()
.Partially(User.PartialLoadFlags.FullName)
.OrderBy(User.OrderByKey)
.ListAsync();
Load detail tables or nested objects as needed.
Manage complex operations atomically:
using var uow = UnitOfWork.Create();
await using var tx = await uow.BeginAsync("your-connection-string", CancellationToken.None);
var context = new MyAppContext("your-connection-string");
// Batch update
await context.UpdateUsersTable().Set(usersToUpdate).GoAsync();
await tx.CompleteAsync(CancellationToken.None);
Call stored procedures and functions with type safety:
// Scalar function
var result = await context.ExecuteScalarFunc(userId, branchId).GetAsync();
// Stored procedure
var procResult = await context.ExecuteInputOutputProc(userId, ioValue).GoAsync();
// procResult contains output parameters, rows affected, etc.
Parameters and results are mapped automatically.
Map models to SQL views, virtual views, or custom SQL statements:
// Virtual view
var specialUsers = await context.SelectFromUsersVirtualView().ListAsync();
// Custom SQL
var customSql = "SELECT * FROM dbo.Users WHERE IsActive = 1";
var activeUsers = await context.SelectFromUsersCustomSql(customSql).ListAsync();
Storm seamlessly supports AltaSoft.DomainPrimitives:
public class User
{
public UserId Id { get; set; } // auto-mapped to underlying type
}
No extra configuration needed.
JSON is preferred for complex object storage:
[StormColumn(DbType =
8000
UnifiedDbType.Json)]
public List<Role> Roles { get; set; }
XML is also supported:
[StormColumn(DbType = UnifiedDbType.Xml)]
public Profile ProfileXml { get; set; }
Plug in custom serialization providers if needed:
StormManager.Initialize(
new MsSqlOrmProvider(),
configuration => { /* ... */ },
jsonSerializationProvider: new MyJsonProvider(),
xmlSerializationProvider: new MyXmlProvider()
);
Efficiently store large strings as compressed binary:
[StormColumn(DbType = UnifiedDbType.VarBinary, SaveAs = SaveAs.CompressedString)]
public string BigString { get; set; }
Storm will compress on save and decompress on retrieval automatically.
Change tracking is automatic (IL weave); use it for dirty-checking and efficient updates.
var user = await context.SelectFromUsersTable(1).WithTracking().GetAsync();
user.Name = "New Name";
if (user.IsDirty())
await context.UpdateUsersTable().Set(user).GoAsync();
Support for concurrency check and optimistic locking:
[StormColumn(ColumnType = ColumnType.ConcurrencyCheck | ColumnType.RowVersion)]
public byte[] RowVersion { get; set; }
Plug in any ILogger
implementation for full logging. All ORM errors use StormException
for clear diagnostics.
StormManager.SetLogger(myLogger);
- Custom Table/View Names: Use
SchemaName
,ObjectName
, andDisplayName
in[StormDbObject]
. - Detail Table Mapping: Use
DetailTableName
in[StormColumn]
for one-to-many relationships. - Concurrency & Tracking: Use appropriate column types.
- Custom Exception Handling: All ORM errors throw
StormException
. - Custom Serialization Providers: Plug in your own.
- Source Generators: Easily extend for new DBs and behaviors.
Storm supports storing enums as strings in the database for better readability and schema evolution. To enable this, use the [StormStringEnum<EnumType, ConverterType>(maxLength)]
attribute on your enum definition, and annotate your model property with [StormColumn(SaveAs = SaveAs.String)]
:
[StormStringEnum<RgbColor, RgbColorExt>(16)]
public enum RgbColor : sbyte {
Red,
Green,
Blue,
White,
Black
}
public sealed class RgbColorExt : IStormStringToEnumConverter<RgbColor>
{
public static string ToDbString(RgbColor value)
{
return value switch
{
RgbColor.Red => "#FF0000",
RgbColor.Green => "#00FF00",
RgbColor.Blue => "#0000FF",
RgbColor.White => "#FFFFFF",
RgbColor.Black => "#000000",
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null)
};
}
public static RgbColor FromDbString(string value)
{
return value.ToUpper() switch
{
"#FF0000" => RgbColor.Red,
"#00FF00" => RgbColor.Green,
"#0000FF" => RgbColor.Blue,
"#FFFFFF" => RgbColor.White,
"#000000" => RgbColor.Black,
_ => throw new ArgumentOutOfRangeException(nameof(value), value, null)
};
}
}
public class Car {
[StormColumn(SaveAs = SaveAs.String)]
public RgbColor Color { get; set; } = RgbColor.Blue;
}
This configuration ensures that the enum values are stored as strings in the database, rather than their underlying integer values. This approach improves schema clarity and makes future changes to enum values safer.
AltaSoft.Storm is MIT licensed and welcomes all contributions! Open issues, submit PRs, and help us build the future of .NET ORM.
MIT
For support, questions, or additional info:
- GitHub Issues
- Discussions tab on GitHub
Currently, only MSSQL is fully supported. Other databases are planned for future releases. Contributions are welcome!
Just use [StormDbObject<MyContext>]
and Storm will generate all extension methods and helpers automatically.
Any type implementing the domain primitive pattern is auto-mapped to its underlying DB type.
Use the generated BulkInsertInto...
or batch update methods—see code samples above.
Use UnitOfWork.Create()
and transaction helpers.
Open an issue or discussion on GitHub!
AltaSoft.Storm — Fast, Modern, and Open Source ORM for .NET