Thomas.Cache 2.1.0

dotnet add package Thomas.Cache --version 2.1.0
                    
NuGet\Install-Package Thomas.Cache -Version 2.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Thomas.Cache" Version="2.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Thomas.Cache" Version="2.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Thomas.Cache" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Thomas.Cache --version 2.1.0
                    
#r "nuget: Thomas.Cache, 2.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Thomas.Cache@2.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Thomas.Cache&version=2.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Thomas.Cache&version=2.1.0
                    
Install as a Cake Tool

alternate text is missing from this package README image ThomasDataAdapter

Simplified setup for robust database operations by relief having to manage connection, commands or transactions

🌟 Join Us in Shaping the Future of ThomasDataAdapter! 🌟

Join us in refining a library designed to streamline database interactions effortlessly. Your insights, tests, and small enhancements can make a big difference. Feel free to fix bugs and suggest features.

🎯 Features:

  • Seamless compatibility with both .NET Core and .NET Framework.
  • Type-safe data results for enhanced reliability.
  • Streamlined query integration with full support for both typed and anonymous parameters.
  • Flexible transaction handling with lambda-based expressions.
  • Customizable DbParameter attributes, including direction, size, and precision.
  • Unique database signature for instant static context creation.
  • Cache data management to boost performance with dynamic cache refresh capabilities.
  • Full compatibility with record types.
  • Zero dependencies.

Supported database providers

  • Microsoft.Data.SqlClient
  • Oracle.ManagedDataAccess.Core
  • Npgsql
  • Mysql.Data
  • Microsoft.Data.Sqlite

🚀 Quick start

Basic configuration in startup.cs

Benefits of persisting the configuration with its string connection you may have different versions of it for different purposes like pooling or Packet Size:

DbConfig.Register(new DbSettings(
    signature: "db1",
    provider: SqlProvider.SqlServer,
    stringConnection: $"Data Source={SourceName};Initial Catalog={database};User ID={User};Password={Pass}"
));

DbConfig.Register(new DbSettings(
    signature: "db2",
    provider: SqlProvider.Sqlite,
    stringConnection: $"Data Source={filePath}"
));

Custom configuration

DbConfig.Register(new DbSettings { 
    Signature = "db1",
    StringConnection = $"Data Source={SourceName};Initial Catalog={database};User ID={User};Password={Pass}",
    DetailErrorMessage = true,
    ConnectionTimeout = 300,
    SensitiveDataLog = true
 });

Expression configuration

   var builder = new TableBuilder();
   builder.AddTable<User>(key: x => x.Id).AddFieldsAsColumns<User>().DbName("system_user");
   DbConfig.AddDbBuilder(builder);

Expressions calls

public class UserRepository
{
    public List<User> GetUsers() => DbHub.Use("db1").FetchList<User>();
    public List<User> GetInvalidUsers() => DbHub.Use("db1").FetchList<User>(
        user => 
        user.LastName == null &&
        user.Age <= 0 && user.Age > 100 &&
        user.Salary <= 0
    );

    public int Add(User user) => DbHub.Use("db2").Add<User, int>(user);
    public void Update(User user) => DbHub.Use("db2").Update(user);
    public void Delete(User user) => DbHub.Use("db2").Delete(user);
}

Direct calls

    public void DisableUser(int id) => DbHub.Use("db2").ExecuteOp("disable_user", new { id });
    public Tuple<List<User>, List<Office>> ProcessData() {
        return DbHub.Use("db1").ToTuple<User, Office>("sp_process");
    }

    public bool ActiveOffice(decimal officeId)
    {
        return DbHub.Use("db2").ExecuteTransaction((db) =>
        {
            db.Execute("UPDATE User SET Active = 1 WHERE OfficeId = $officeId", new { officeId });
            db.Execute("UPDATE Office SET Active = 1 WHERE OfficeId = $officeId", new { officeId });
            return db.Commit();
        });
    }

    //batching
    public void Export()
    {
        var (dispose, data) = DbHub.Use("db1").FetchData<Order>(script: "SELECT * FROM Order", parameters: null, batchSize: 10000);
        foreach (var batch in data)
        {
            //process batch
        }
        dispose();
    }

    //records
    public record User(int Id, string Name, byte[] photo);
    public List<User> Export()
    {
        return DbHub.Use("db1").FetchList<User>("SELECT * FROM User"); 
    }

Perfomance

Experience really good performance without the hassle of managing DbConnection. Here's a fair test where we consistently dispose of connections after each use.

Note: Tested with Docker MSSQL locally

BenchmarkDotNet v0.13.12, Windows 11 (10.0.22631.4169/23H2/2023Update/SunValley3)
13th Gen Intel Core i9-13900H, 1 CPU, 20 logical and 14 physical cores
.NET SDK 8.0.400
  [Host]     : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2

| Detailed Runtime           | Type                       | Method                   | Mean     | StdDev   | Error    | Op/s    | Gen0   | Allocated |
|--------------------------- |--------------------------- |------------------------- |---------:|---------:|---------:|--------:|-------:|----------:|
| .NET 8.0.8 (8.0.824.36612) | ThomasDataAdapterBenchmark | FetchOne<>               | 398.2 us |  4.01 us |  5.13 us | 2,511.3 | 0.4883 |   8.09 KB |
| .NET 8.0.8 (8.0.824.36612) | DapperBenckmark            | QuerySingle<T>           | 406.7 us |  6.78 us |  7.25 us | 2,459.0 | 0.4883 |   7.43 KB |
| .NET 8.0.8 (8.0.824.36612) | ThomasDataAdapterBenchmark | FetchListRecord<>        | 447.2 us |  9.42 us |  8.80 us | 2,236.3 | 1.9531 |  25.02 KB |
| .NET 8.0.8 (8.0.824.36612) | ThomasDataAdapterBenchmark | FetchList<>              | 465.0 us |  7.68 us |  9.20 us | 2,150.3 | 1.9531 |  25.53 KB |
| .NET 8.0.8 (8.0.824.36612) | DapperBenckmark            | Query<T>                 | 513.8 us | 49.20 us | 16.87 us | 1,946.4 | 1.9531 |   30.3 KB |
| .NET 8.0.8 (8.0.824.36612) | ThomasDataAdapterBenchmark | 'FetchList<> Expression' | 529.4 us | 60.12 us | 20.50 us | 1,889.0 | 1.9531 |  27.65 KB |

Best practices

  • For fastest data retrieve prefer use Store procedure over Sql text queries
  • Prefer use Records over Anemic class to retrieve data
  • Try different bufferSize values when need read streams for optimal performance
  • Columns defined as NOT NULL will enhance the generate algorithm because will avoid IsDBNull branching
  • Use latest database provider library version as much as possible for security and performance concerns
  • Use nullable Datatype for classes used to write operations or store procedures object filter for more natural interpretation of DBNull when is possible, here are more DbNull implicit values:
Type implicit DbNull
String null
Datetime MinValue
TimeSpan MinValue
Guid default
StringBuilder null
XmlDocument null
Byte[] null or empty array
Nullable<> not(HasValue)

Considerations

Library's purpose is to make easy DB interactions with a simple configuration. Obviously it doesn't attempt to solve every problem. There are some considerations at development time:

  • Ensure that the specific database provider library is installed in your project, as it will be accessed via reflection by this library.
  • Configuration for write operations (insert, update and delete) requires TableBuilder preferring at application startup.

Db Types

  • Nullable versions has the same corresponding DbType
  • DateOnly and TimeOnly available in NET 6 or greater
  • C# types will transform to their specific dbTypes to setup parameters command
  • SQLite dbtypes won't setup so when create dynamically parameters the library itself to infer their type
C# Type SQL Server Oracle PostgreSQL MySQL
string NVARCHAR VARCHAR2 VARCHAR VARCHAR
short / ushort SMALLINT INT16 SMALLINT INT16
int / uint INT INT32 INTEGER INT32
long / ulong BIGINT INT64 BIGINT INT64
byte / sbyte TINYINT BYTE SMALLINT BYTE
decimal DECIMAL DECIMAL NUMERIC DECIMAL
double FLOAT DOUBLE DOUBLE DOUBLE
float FLOAT FLOAT REAL FLOAT
bool BIT INT32 (0 or 1) BIT BIT
DateTime DATETIME DATE TIMESTAMP DATETIME
TimeSpan TIME IntervalDS INTERVAL -
Guid UNIQUEIDENTIFIER RAW UUID GUID
byte[] VARBINARY BLOB BYTEA MEDIUMBLOB
SqlBinary BINARY - - -
XmlDocument XML XMLTYPE XML XML
StringBuilder TEXT CLOB TEXT -
DateOnly DATE DATE DATE DATE
TimeOnly TIME - TIME TIME

Limitations

Be aware of the following limitations:

  • Currently limited to specific DB library providers, with plans to expand support in the future.
Product Compatible and additional computed target framework versions.
.NET net5.0 is compatible.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  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 was computed.  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 was computed.  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 Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 is compatible.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.1.0 122 9/24/2024
2.0.2 121 9/7/2024
2.0.1 128 7/9/2024
2.0.0 142 5/15/2024
1.1.0 155 1/21/2024
1.0.0 143 1/16/2024

Features released 2.1.0:

- Cache data management to boost performance with dynamic cache refresh capabilities.
- Refresh data from anywhere by single string key
- Query string and query expression support