Linger.DataAccess
1.1.0
See the version list below for details.
dotnet add package Linger.DataAccess --version 1.1.0
NuGet\Install-Package Linger.DataAccess -Version 1.1.0
<PackageReference Include="Linger.DataAccess" Version="1.1.0" />
<PackageVersion Include="Linger.DataAccess" Version="1.1.0" />
<PackageReference Include="Linger.DataAccess" />
paket add Linger.DataAccess --version 1.1.0
#r "nuget: Linger.DataAccess, 1.1.0"
#:package Linger.DataAccess@1.1.0
#addin nuget:?package=Linger.DataAccess&version=1.1.0
#tool nuget:?package=Linger.DataAccess&version=1.1.0
Linger.DataAccess
A core data access library that provides database abstraction and common database operations.
Features
- Database Abstraction: Provider-agnostic database access
- CRUD Operations: Complete Create, Read, Update, Delete operations
- Async Support: Full async/await support for modern applications
- Multiple Data Types: Support for DataTable, DataSet, Entity objects, and Hashtable
- Transaction Support: Built-in transaction management
- SQL Builder: Helper for dynamic SQL generation
- Bulk Operations: Interface for high-performance bulk data insertion
- Batch Query: Large parameter list splitting (default batchSize = 1000) with parameterized & raw variants
Supported .NET Versions
- .NET 9.0
- .NET 8.0
- .NET Framework 4.6.2+
Installation
This library is typically not installed directly, but is automatically referenced when installing specific database implementation packages:
# For SQL Server
dotnet add package Linger.DataAccess.SqlServer
# For Oracle Database
dotnet add package Linger.DataAccess.Oracle
# For SQLite
dotnet add package Linger.DataAccess.Sqlite
Core Interfaces
IDatabase
The main interface providing comprehensive database operations:
// Execute operations
int ExecuteBySql(string sql);
int ExecuteByProc(string procName, DbParameter[] parameters);
// Query operations
List<T> FindListBySql<T>(string sql);
DataTable FindTableBySql(string sql, DbParameter[] parameters);
DataSet FindDataSetBySql(string sql, DbParameter[] parameters);
// Batch query operations
DataTable QueryInBatches(string sql, List<string> parameters, int batchSize = 1000);
Task<DataTable> QueryInBatchesAsync(string sql, List<string> parameters, int batchSize = 1000, CancellationToken cancellationToken = default);
DataTable QueryInBatchesRaw(string sql, List<string> values, int batchSize = 1000, bool quote = true);
Task<DataTable> QueryInBatchesRawAsync(string sql, List<string> values, int batchSize = 1000, bool quote = true, CancellationToken cancellationToken = default);
// Async operations
Task<DataTable> FindTableBySqlAsync(string sql);
Task<DataSet> FindDataSetBySqlAsync(string sql, DbParameter[] parameters);
Task<int> FindCountBySqlAsync(string sql);
// Entity operations
T FindEntityBySql<T>(string sql, DbParameter[] parameters);
Hashtable FindHashtableBySql(string sql, DbParameter[] parameters);
// Bulk operations
bool BulkInsert(DataTable dt);
IProvider
Database provider abstraction for different database engines.
Basic Usage
using Linger.DataAccess;
// Execute queries
var users = database.FindListBySql<User>("SELECT * FROM Users WHERE Active = 1");
var userTable = await database.FindTableBySqlAsync("SELECT * FROM Users");
// Batch query (IDs split automatically, default batchSize 1000)
var ids = Enumerable.Range(1, 5000).Select(i => i.ToString()).ToList();
var dt = database.QueryInBatches("SELECT * FROM Users WHERE Id IN ({0})", ids);
// Custom batch size
var dt500 = database.QueryInBatches("SELECT * FROM Users WHERE Id IN ({0})", ids, 500);
// Raw version (trusted numeric IDs only)
var dtRaw = database.QueryInBatchesRaw("SELECT * FROM Users WHERE Id IN ({0})", ids, 800, quote: false);
// Async parameterized version
var dtAsync = await database.QueryInBatchesAsync("SELECT * FROM Users WHERE Id IN ({0})", ids, 750);
// Execute commands
int affected = database.ExecuteBySql("UPDATE Users SET LastLogin = GETDATE()");
// Count operations
int userCount = await database.FindCountBySqlAsync("SELECT COUNT(*) FROM Users");
Batch Query
When dealing with very large IN lists (thousands of IDs) a single SQL statement may exceed length limits or degrade performance. The batch query helpers automatically split the list and concatenate the results.
// Parameterized (safe)
var result = database.QueryInBatches(
"SELECT * FROM Orders WHERE OrderId IN ({0})",
orderIds); // default batchSize = 1000
// Raw (only for trusted constant values)
var resultRaw = database.QueryInBatchesRaw(
"SELECT * FROM Orders WHERE OrderId IN ({0})",
orderIds, 500, quote: false);
Guidelines:
- Use {0} in sql where the batch placeholder will be injected.
- Prefer parameterized methods for security (prevents SQL injection).
- Raw methods are only for fully trusted data (e.g., internally generated numeric IDs).
- Adjust batchSize to balance network round-trips and SQL size limits.
Return Type:
- All batch methods merge rows into a single DataTable preserving schema of the first non-empty batch.
Architecture
This library provides the foundation for database-specific implementations:
- Linger.DataAccess.SqlServer - SQL Server implementation
- Linger.DataAccess.Oracle - Oracle Database implementation
- Linger.DataAccess.Sqlite - SQLite implementation
Key Components
Database Class
Base implementation of IDatabase interface providing common database operations.
BaseDatabase Class
Core database functionality including connection management and parameter handling.
SqlBuilder Class
Helper utility for building dynamic SQL queries safely.
Async/Await Best Practices
True Async Implementation ✅
All async methods in this library use true asynchronous I/O operations, not Task.Run wrappers:
// ✅ TRUE ASYNC - Releases thread during I/O
public async Task<bool> ExistsAsync(string sql, CancellationToken ct = default)
{
var count = await FindCountBySqlAsync(sql).ConfigureAwait(false);
return count > 0;
}
// ❌ PSEUDO-ASYNC (Not used in this library)
// Task.Run just wraps synchronous blocking code
public Task<bool> BadExistsAsync(string sql)
{
return Task.Run(() => FindCountBySql(sql)); // Wastes thread pool threads
}
Performance Benefits
| Metric | Synchronous | Pseudo-Async (Task.Run) | True Async ✅ |
|---|---|---|---|
| Thread Usage | 1 thread blocked | 1 thread pool thread | 0 threads during I/O |
| Concurrency | ~Thousands | ~Thousands | ~Tens of thousands |
| Memory | ~1MB per thread | ~1MB per thread | ~Few KB per task |
| Scalability | Limited | Limited | Excellent |
| Cancellation | Not supported | Only before start | During I/O operation |
Usage Recommendations
// ✅ DO: Use async methods for I/O operations
var users = await database.FindTableBySqlAsync("SELECT * FROM Users");
var count = await database.FindCountBySqlAsync("SELECT COUNT(*) FROM Orders");
// ✅ DO: Use ConfigureAwait(false) in library code (already done internally)
var result = await database.QueryAsync(sql).ConfigureAwait(false);
// ✅ DO: Support cancellation tokens
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
var data = await database.QueryTableAsync(sql, null, cts.Token);
// ❌ DON'T: Mix sync and async (use one or the other)
var badResult = database.FindTableBySqlAsync(sql).Result; // Can deadlock!
High Concurrency Scenarios
For applications handling thousands of concurrent requests:
// ✅ Scales to tens of thousands of concurrent operations
var tasks = Enumerable.Range(1, 10000)
.Select(id => database.FindTableBySqlAsync($"SELECT * FROM Orders WHERE Id = {id}"))
.ToList();
var results = await Task.WhenAll(tasks); // Minimal thread usage
Best Practices
- Use parameterized queries to prevent SQL injection
- Use batch query helpers for large IN lists instead of manual concatenation
- Implement proper disposal patterns with
usingstatements - Use async methods for I/O intensive operations - All async methods use true async I/O
- Always pass CancellationToken to async methods for proper cancellation support
- Use ConfigureAwait(false) in library code (already done internally)
- Choose appropriate database-specific implementations for optimal performance
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Framework | net472 is compatible. net48 was computed. net481 was computed. |
-
.NETFramework 4.7.2
- Linger.Utils (>= 1.1.0)
-
net10.0
- Linger.Utils (>= 1.1.0)
-
net8.0
- Linger.Utils (>= 1.1.0)
-
net9.0
- Linger.Utils (>= 1.1.0)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on Linger.DataAccess:
| Package | Downloads |
|---|---|
|
Linger.DataAccess.SqlServer
Package Description |
|
|
Linger.DataAccess.Sqlite
Package Description |
|
|
Linger.DataAccess.Oracle
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.4.4-preview | 126 | 6/16/2026 |
| 1.4.3-preview | 138 | 6/15/2026 |
| 1.4.2 | 159 | 5/20/2026 |
| 1.4.1-preview | 154 | 5/12/2026 |
| 1.4.0 | 153 | 5/6/2026 |
| 1.3.3-preview | 147 | 5/5/2026 |
| 1.3.2-preview | 136 | 4/29/2026 |
| 1.3.1-preview | 146 | 4/28/2026 |
| 1.3.0-preview | 140 | 4/27/2026 |
| 1.2.0-preview | 155 | 3/29/2026 |
| 1.1.0 | 165 | 2/4/2026 |
| 1.0.3-preview | 158 | 1/9/2026 |
| 1.0.2-preview | 165 | 1/8/2026 |
| 1.0.0 | 359 | 11/12/2025 |
| 1.0.0-preview2 | 265 | 11/6/2025 |
| 1.0.0-preview1 | 257 | 11/5/2025 |
| 0.9.9 | 250 | 10/16/2025 |
| 0.9.8 | 262 | 10/14/2025 |
| 0.9.7-preview | 243 | 10/13/2025 |
| 0.9.6-preview | 238 | 10/12/2025 |