Skip to content

Latest commit

Β 

History

History
Β 
Β 

README.md

πŸ’Ύ Sphere10.Framework.Data

Universal data access abstraction layer providing ADO.NET enhancements, schema management, transaction scopes, and database-agnostic persistence.

Sphere10.Framework.Data enables vendor-independent database access through a unified API while supporting SQLite, SQL Server, Firebird, and NHibernate. Core abstractions handle connection pooling, transactions, type mapping, and bulk operations.

⚑ 10-Second Example

using Sphere10.Framework.Data;

// SQLite in-memory database
var dac = Tools.Sqlite.Open(":memory:");

// Insert records
dac.Insert("Users", new[] {
    new ColumnValue("ID", 1),
    new ColumnValue("Name", "Alice")
});

// Query with parameters (safe from injection)
dac.ExecuteScalar<int>("SELECT COUNT(*) FROM Users WHERE Name = @name",
    new ColumnValue("@name", "Alice"));  // Returns 1

// Transactional scope
using (var scope = dac.BeginTransactionScope()) {
    dac.Update("Users", new[] { new ColumnValue("Name", "Alice Smith") }, "WHERE ID = 1");
    scope.Commit();  // Atomic persist
}

πŸ—οΈ Core Concepts

Data Access Context (DAC): Wrapper around IDbConnection providing transaction scoping, parameterized queries, and connection reuse.

Connection Pooling: Connections are automatically managed and reused within a scope for efficiency.

Column Values: Named parameter approach using ColumnValue objects prevents SQL injection.

Transaction Scopes: Atomic boundaries for ACID operations; auto-rollback on exception.

πŸ”§ Core Examples

Connection & Basic Operations

using Sphere10.Framework.Data;

// Open SQLite database
var dac = Tools.Sqlite.Open("mydata.db");

// Create and open connection
using (var conn = dac.CreateOpenConnection()) {
    var state = conn.State;  // ConnectionState.Open
}

// Create unopened connection (for lazy initialization)
using (var conn = dac.CreateConnection()) {
    var state = conn.State;  // ConnectionState.Closed
}

Insert & Retrieve

using Sphere10.Framework.Data;

var dac = Tools.Sqlite.Open(":memory:");

// Insert with named columns
dac.Insert("BasicTable", new[] {
    new ColumnValue("ID", 1),
    new ColumnValue("Text", "Hello World")
});

// Read all records
var results = dac.ExecuteQuery("SELECT * FROM BasicTable");

// Count records with parameterized safety
int count = dac.ExecuteScalar<int>(
    "SELECT COUNT(*) FROM BasicTable WHERE Text = @text",
    new ColumnValue("@text", "Hello World")
);  // Returns 1

Transactions with Scopes

var dac = Tools.Sqlite.Open(":memory:");

// Create table
dac.ExecuteNonQuery(@"CREATE TABLE Accounts (
    ID INTEGER PRIMARY KEY,
    Name TEXT,
    Balance REAL
)");

// Transactional scope ensures atomicity
using (var scope = dac.BeginTransactionScope()) {
    dac.Insert("Accounts", new[] {
        new ColumnValue("ID", 1),
        new ColumnValue("Name", "Alice"),
        new ColumnValue("Balance", 1000.0)
    });
    
    dac.Insert("Accounts", new[] {
        new ColumnValue("ID", 2),
        new ColumnValue("Name", "Bob"),
        new ColumnValue("Balance", 500.0)
    });
    
    // Transfer: withdraw from Alice
    dac.Update("Accounts",
        new[] { new ColumnValue("Balance", 900.0) },
        "WHERE ID = 1");
    
    // Transfer: deposit to Bob
    dac.Update("Accounts",
        new[] { new ColumnValue("Balance", 600.0) },
        "WHERE ID = 2");
    
    // Commit atomically (rollback auto-occurs on exception)
    scope.Commit();
}

// Verify final state
var aliceBalance = dac.ExecuteScalar<double>(
    "SELECT Balance FROM Accounts WHERE ID = 1");
Console.WriteLine($"Alice: {aliceBalance}");  // 900.0

Multi-Database Support

// SQLite (embedded, in-memory)
var sqliteDac = Tools.Sqlite.Open(":memory:");

// SQL Server
var sqlServerDac = Tools.MSSQL.Open(
    "Server=localhost;Database=mydb;Trusted_Connection=true");

// Firebird
var firebirdDac = Tools.Firebird.Open(
    "ServerType=1;DataSource=localhost;Database=mydb.fdb;User=sysdba;Password=masterkey");

// All use same DAC interface
sqliteDac.ExecuteScalar("SELECT COUNT(*) FROM Users");
sqlServerDac.ExecuteScalar("SELECT COUNT(*) FROM Users");
firebirdDac.ExecuteScalar("SELECT COUNT(*) FROM Users");

Type Mapping & GUIDs

var dac = Tools.Sqlite.Open(":memory:");

// Define table with GUID column
dac.ExecuteNonQuery(@"CREATE TABLE [Items] (
    ID INTEGER PRIMARY KEY,
    UniqueKey UNIQUEIDENTIFIER NOT NULL
)");

// Write GUID
var guid = Guid.NewGuid();
dac.Insert("Items", new[] {
    new ColumnValue("ID", 1),
    new ColumnValue("UniqueKey", guid)
});

// Read GUID back (automatic type conversion)
var retrieved = dac.ExecuteScalar<Guid>(
    "SELECT UniqueKey FROM Items WHERE ID = 1");

Console.WriteLine($"Written: {guid}");
Console.WriteLine($"Retrieved: {retrieved}");
Console.WriteLine($"Match: {guid == retrieved}");  // true

Parameterized Queries (SQL Injection Safe)

var dac = Tools.Sqlite.Open(":memory:");

dac.ExecuteNonQuery(@"CREATE TABLE [Users] (
    ID INTEGER PRIMARY KEY,
    Name TEXT,
    Email TEXT
)");

dac.Insert("Users", new[] {
    new ColumnValue("ID", 1),
    new ColumnValue("Name", "Alice"),
    new ColumnValue("Email", "alice@example.com")
});

// Safe parameterized query - prevents SQL injection
string userInput = "Alice"; // Could be malicious
var results = dac.ExecuteQuery(
    "SELECT * FROM Users WHERE Name = @name",
    new ColumnValue("@name", userInput));

// Multiple parameters
results = dac.ExecuteQuery(
    "SELECT * FROM Users WHERE Name = @name AND Email = @email",
    new ColumnValue("@name", "Alice"),
    new ColumnValue("@email", "alice@example.com"));

πŸ“Š Database-Specific Implementations

Sphere10.Framework.Data provides database-specific projects with optimized implementations:

Corresponding WinForms projects available for GUI applications:

πŸ”§ Usage

πŸ“¦ Dependencies

  • Sphere10 Framework: Core framework
  • System.Data: ADO.NET abstraction (.NET built-in)
  • Database-specific drivers: Installed by platform-specific packages (SQLite, SQL Server, Firebird, NHibernate)

⚠️ Best Practices

  • Always use using statements for scopes to ensure proper connection cleanup and transaction handling
  • Use ColumnValue for all parameters to prevent SQL injection
  • Keep transaction scopes small - only operations that must be atomic
  • Parameterized queries only - never concatenate user input into SQL strings
  • Handle exceptions properly - transaction scopes auto-rollback on exception; explicit Commit() required for success
  • Test with multiple databases - Sphere10.Framework.Data is database-agnostic, but specific DBMS edge cases may exist

🧬 Architecture Layers

  • ADO.NET Extensions: IDbConnection, IDbCommand, IDbReader helpers
  • DAC (Data Access Context): Transaction and connection management (IDataAccessContext)
  • Schema Support: Database introspection and DDL operations
  • Type Mapping: CLR ↔ Database type conversion
  • Bulk Operations: Efficient batch insert/update patterns
  • CSV/XML Support: Data import/export utilities

πŸ› οΈ Tools.* Namespace

This project extends the global Tools namespace with database-specific utilities:

Tools.Data

Generic database operations across all providers.

var connection = Tools.Data.CreateConnection(connectionString);
var adapter = Tools.Data.CreateDataAdapter(connection);

Tools.Sqlite (via Sphere10.Framework.Data.Sqlite)

SQLite-specific utilities.

var dac = Tools.Sqlite.Open(":memory:");
var dac = Tools.Sqlite.Create(filename, pageSize: 4096);
Tools.Sqlite.Drop(Tools.Sqlite.GetFilePathFromConnectionString(connStr));
bool exists = Tools.Sqlite.Exists(connectionString);

Tools.MSSql (via Sphere10.Framework.Data.MSSQL)

SQL Server-specific utilities.

var adapter = Tools.MSSql.CreateAdapter(connectionString);
var connection = Tools.MSSql.CreateConnection(connectionString);

Tools.Json & Tools.Xml

Data format conversion utilities.

string json = Tools.Json.Serialize(data);
var deserialized = Tools.Json.Deserialize<T>(json);

string xml = Tools.Xml.Serialize(data);
var deserialized = Tools.Xml.Deserialize<T>(xml);

For complete Tools reference, see docs/Tools-Reference.md

πŸ”Œ Extensibility

Implement IDataAccessContext to create custom DAC implementations for unsupported databases or specialized persistence needs. All database-specific projects (SQLite, MSSQL, Firebird) extend the abstract DataAccessContext base class.

⚑ Performance Considerations

  • Connection Pooling: Reuse connections within transaction scopes for efficiency
  • Batch Operations: Use bulk insert patterns for large datasets
  • Parameterized Queries: Cached query plans improve execution speed
  • Type Mapping: Constant-size types (int, long, GUID) faster than variable-size (string, byte[])
  • Database Selection: SQLite optimal for single-process/embedded; MSSQL/Firebird for multi-user/distributed

βœ… Status & Compatibility

  • Maturity: Production-tested, core abstraction stable
  • .NET Target: .NET 8.0+ (primary), .NET Standard 2.0 compatibility for some components
  • Thread Safety: Each DAC instance should be used single-threaded; create separate instances for concurrent access
  • Backward Compatibility: Database schemas may require migration between Sphere10 Framework versions

πŸ“– Related Projects

βš–οΈ License

Distributed under the MIT NON-AI License.

See the LICENSE file for full details. More information: Sphere10 NON-AI-MIT License

πŸ‘€ Author

Herman Schoenfeld - Software Engineer