A lightweight, high-performance micro-ORM for .NET that provides a fluent API for building type-safe SQL queries and mapping entities to database tables.
- 🚀 High Performance: Minimal overhead with direct ADO.NET access
- 🧵 Thread-Safe: Designed for concurrent operations in multi-threaded applications
- 🔒 Type-Safe Queries: Compile-time type checking for SQL expressions
- 🔄 Fluent API: Intuitive expression builder for complex queries
- ⚡ Full Async/Await Support: All database operations have async variants with cancellation token support
- ⚙️ Flexible Mapping: Attribute-based or fluent entity-to-table mapping
- ⏱️ Transaction Support: Full transaction management with commit/rollback
- 📊 Multiple Data Types: Support for all common SQL Server data types
- 🎯 Multi-Targeting: Supports .NET 6.0 through .NET 10.0
- 📚 Fully Documented: Comprehensive XML documentation for all public APIs
XpressData is a micro-ORM designed for developers who want:
- Fine-grained control over SQL queries
- Strong typing without the complexity of full ORMs
- High performance with minimal abstraction
- Easy integration with existing SQL Server databases
XpressData is not a replacement for Entity Framework or other full-featured ORMs. It does not:
- Provide automatic migration support
- Implement lazy loading or change tracking
- Generate database schemas automatically
- Support multiple database providers (SQL Server only)
Install via NuGet Package Manager:
dotnet add package XpressDataOr via Package Manager Console:
Install-Package XpressDatapublic class User
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string? Email { get; set; }
public DateTime CreatedAt { get; set; }
}using XpressData.Mapping;
using XpressData.Mapping.FieldTypes;
public class UserMap : EntityMap<User>
{
public FieldMap Id { get; private set; } = null!;
public FieldMap Name { get; private set; } = null!;
public FieldMap Email { get; private set; } = null!;
public FieldMap CreatedAt { get; private set; } = null!;
public UserMap() : base(null)
{
InitializeMap();
}
public override void InitializeMap()
{
base.InitializeMap();
PhysicalTableName = "Users";
Id = new FieldMap(this, new FieldTypeInteger(), "Id", "id", isPK: true);
Name = new FieldMap(this, new FieldTypeString(100), "Name", "name");
Email = new FieldMap(this, new FieldTypeString(255), "Email", "email", isNullable: true);
CreatedAt = new FieldMap(this, new FieldTypeDateTime(), "CreatedAt", "created_at");
}
}// In Program.cs or Startup.cs
services.AddXpressData(Configuration.GetSection("XpressData"));// In appsettings.json
{
"XpressData": {
"Server": "localhost",
"Database": "MyDatabase",
"UserId": "sa",
"Password": "YourPassword",
"TrustServerCertificate": true
}
}using XpressData.Datalayer;
using XpressData.Expressions;
public class UserService
{
private readonly IDatabase _database;
private readonly UserMap _userMap;
public UserService(IDatabase database)
{
_database = database;
_userMap = new UserMap();
}
public User[] GetActiveUsers()
{
using var connection = _database.CreateConnection();
connection.Open();
// Build type-safe filter expression
IExpressionString nameField = Builder.Field(_userMap.Name);
var filter = Builder.IsNotNull(nameField);
// Execute query
var users = _userMap.Get(connection, filterExpression: filter);
connection.Close();
return users;
}
}XpressData uses a type-safe expression builder. When comparing values, cast to the appropriate interface:
// Numeric comparison
IExpressionNumeric idField = Builder.Field(userMap.Id);
IExpressionNumeric idValue = Builder.Value(42);
var filter = Builder.Equal(idField, idValue);
// String comparison
IExpressionString nameField = Builder.Field(userMap.Name);
IExpressionString pattern = Builder.Value("%John%");
var likeFilter = Builder.Like(nameField, pattern);
// Combining conditions
var combinedFilter = Builder.And(filter, likeFilter);| Interface | Use Case |
|---|---|
IExpressionNumeric |
int, long, decimal comparisons |
IExpressionString |
String comparisons, LIKE |
IExpressionGuid |
GUID comparisons |
IExpressionDate |
Date-only comparisons |
IExpressionDateTime |
DateTime comparisons |
IExpressionLogical |
AND, OR, NOT operations |
IExpressionComperable |
IN, NOT IN operations |
using var connection = _database.CreateConnection();
connection.Open();
try
{
connection.BeginTransaction();
_userMap.Insert(connection, newUser);
_orderMap.Insert(connection, newOrder);
connection.Commit();
}
catch
{
connection.Rollback();
throw;
}
finally
{
connection.Close();
}// Order by Name ascending, then Id descending
var orderBy = Builder.OrderBy(userMap.Name, EOrder.Asc,
Builder.OrderBy(userMap.Id, EOrder.Desc));
var users = userMap.Get(connection, orderByInfo: orderBy);IExpressionComperable idField = Builder.Field(userMap.Id);
var values = Builder.Values(new[] { 1, 2, 3, 4, 5 });
var inFilter = Builder.In(idField, values);
var users = userMap.Get(connection, filterExpression: inFilter);var users = userMap.Get(
connection,
orderByInfo: Builder.OrderBy(userMap.Id),
limit: 10,
offset: 20);All database operations have async equivalents with full cancellation token support:
// Async with using declaration
await using var connection = new DatabaseConnection(dbInfo);
// Async SELECT
var result = await connection.SelectAsync(new Query("SELECT * FROM Users"));
// Async EntityMap operations
var users = await userMap.GetAsync(connection, filterExpression: filter);
var user = await userMap.GetByPrymaryKeyAsync(connection, 1);
await userMap.InsertAsync(connection, newUser);
await userMap.UpdateAsync(connection, existingUser);
await userMap.DeleteAsync(connection, userToDelete);
// With cancellation token
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var users = await userMap.GetAsync(connection, cancellationToken: cts.Token);| FieldType Class | SQL Type | .NET Type |
|---|---|---|
FieldTypeInteger |
[int] |
int |
FieldTypeBigInt |
[bigint] |
long |
FieldTypeString(length) |
[varchar](n) |
string |
FieldTypeFixedString(length) |
[char](n) |
string |
FieldTypeGuid |
[uniqueidentifier] |
Guid |
FieldTypeBoolean |
[bit] |
bool |
FieldTypeDateTime |
[datetime] |
DateTime |
FieldTypeDate |
[date] |
DateTime |
FieldTypeNumeric(p, s) |
[numeric](p,s) |
decimal |
FieldTypeBinary(length) |
[varbinary](n) |
byte[] |
FieldTypeFixedBinary(length) |
[binary](n) |
byte[] |
XpressData includes comprehensive unit and integration tests.
dotnet testdotnet test --filter "Category!=Integration"Integration tests require SQL Server LocalDB:
dotnet test --filter "Category=Integration"- API Reference - Complete API documentation
- Architecture Guide - Design and architecture overview
- Getting Started - Detailed getting started guide
- Testing Guide - How to run and write tests
- Known Issues - Current limitations and known issues
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Russlan Kafri
Company: Digixoil
For issues, questions, or suggestions:
- Open an issue on GitHub
- Check existing documentation in the
/docfolder - Review XML documentation in source code
Made with ❤️ by Digixoil