Donald 1.0.1

Functional F# interface for ADO.NET.

There is a newer version of this package available.
See the version list below for details.
Install-Package Donald -Version 1.0.1
dotnet add package Donald --version 1.0.1
<PackageReference Include="Donald" Version="1.0.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Donald --version 1.0.1
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: Donald, 1.0.1"
#r directive can be used in F# Interactive, C# scripting and .NET Interactive. Copy this into the interactive tool or source code of the script to reference the package.
// Install Donald as a Cake Addin
#addin nuget:?package=Donald&version=1.0.1

// Install Donald as a Cake Tool
#tool nuget:?package=Donald&version=1.0.1
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

Donald

NuGet Version Build Status

Meet Donald.

If you're a programmer and have used a database, he's impacted your life in a big way.

This library is named after him.

Getting Started

Donald is a well-tested library that aims to make working with ADO.NET a little bit more succinct.

Providing basic functional wrappers for the IDbCommand methods ExecuteNonQuery(), ExecuteScalar() & ExecuteReader() and a full-suite of IDataReader extension methods to make retrieving values safer and more direct.

If you came looking for an ORM, this is not your light saber. And may the force be with you.

Install the Donald NuGet package:

PM>  Install-Package Donald

Or using the dotnet CLI

dotnet add package Donald

An example using SQL Server

Consider the following model:

type Author = 
    {
        AuthorId : int
        FullName : string
    }
    // Not mandatory, but helpful
    static member fromReader (rd : IDataReader) = 
        {
            AuthorId = rd.GetInt32("author_id")  // IDataReader extension method
            FullName = rd.GetString("full_name") // IDataReader extension method
        }

Define a DbConnectionFactory

open System.Data.SqlClient
open Donald

let connectionString = 
    "Server=MY_SERVER;Database=MyDatabase;Trusted_Connection=True;"

let connectionFactory : DbConnectionFactory = 
    fun _ -> new SqlConnection(connectionString) :> IDbConnection

Query for multiple strongly-typed results

let findAuthor search =
    use conn = createConn connectionFactory

    query
         "SELECT author_id, full_name
          FROM   author
          WHERE  full_name LIKE @search"
        [ newParam "search" search ]
        Author.fromReader
	conn

Query for exactly one strongly-type result

let getAuthor authorId =
    use conn = createConn connectionFactory

    querySingle // Returns Option<Author>
        "SELECT author_id, full_name
         FROM   author
         WHERE  author_id = @author_id"
         [ newParam "author_id" authorId ]
         Author.fromReader 
     conn

Doing work transactionally

The five main API functions: query, querySingle, scalar, exec and execMany, all have transactional sister functions: tranQuery, tranQuerySingle, tranScalar, tranExec and tranExecMany.

As opposed to an IDbConnection, these functions expect an IDbTransaction as the final paramter.

Execute a statement

let updateAuthor author =
    use conn = createConn connectionFactory
    use tran = beginTran conn 

    tranExec // ExecuteNonQuery() within scope of transaction
        "UPDATE author 
         SET    full_name = @full_name 
         WHERE  author_id = @author_id"
        [ 
            newParam "author_id" author.AuthorId
            newParam "full_name" author.FullName
        ]
        tran

    commitTran tran // safely commit transaction

Execute a statement many times

let insertAuthors =
    use conn = createConn connectionFactory
    use tran = beginTran conn 

    tranExecMany
        "INSERT INTO author (full_name) VALUES (@full_name);"                
        [
            [ newParam "full_name" "Bugs Bunny" ]
            [ newParam "full_name" "Donald Duck" ]
        ]                        
        tran    
  
    commitTran tran

Execute a statement that returns a value

let insertAuthor fullName =
    use conn = createConn connectionFactory
    use tran = beginTran conn // Base function's are transaction-oriented
    
    let authorId = 
        tranScalar // ExecuteScalar() within scope of transaction
            "INSERT INTO author (full_name) 
             VALUES (@full_name);

             SELECT SCOPE_IDENTITY();"
            [ newParam "full_name" fullName]
            Convert.ToInt32 // Any obj -> int function would do here
	    tran

    commitTran tran

    authorId 

IDataReader Extension Methods

To make obtaining values from reader more straight-forward, 3 sets of extension methods are available for:

  1. Get value, automatically defaulted
  2. Get value as option<'a>
  3. Get value as Nullable<'a>

Assume we have an open IDataReader and are currently reading a row, the IDataRecord:

rd.GetString "some_field"           // string -> string
rd.GetBoolean "some_field"          // string -> bool
rd.GetByte "some_field"             // string -> byte
rd.GetChar "some_field"             // string -> char
rd.GetDateTime "some_field"         // string -> DateTime
rd.GetDecimal "some_field"          // string -> Decimal
rd.GetDouble "some_field"           // string -> Double
rd.GetFloat "some_field"            // string -> float32
rd.GetGuid "some_field"             // string -> Guid
rd.GetInt16 "some_field"            // string -> int16
rd.GetInt32 "some_field"            // string -> int32
rd.GetInt64 "some_field"            // string -> int64

rd.GetStringOption "some_field"     // string -> string option
rd.GetBooleanOption "some_field"    // string -> bool option
rd.GetByteOption "some_field"       // string -> byte option
rd.GetCharOption "some_field"       // string -> char option
rd.GetDateTimeOption "some_field"   // string -> DateTime option
rd.GetDecimalOption "some_field"    // string -> Decimal option
rd.GetDoubleOption "some_field"     // string -> Double option
rd.GetFloatOption "some_field"      // string -> float32 option
rd.GetGuidOption "some_field"       // string -> Guid option
rd.GetInt16Option "some_field"      // string -> int16 option
rd.GetInt32Option "some_field"      // string -> int32 option
rd.GetInt64Option "some_field"      // string -> int64 option

rd.GetNullableBoolean "some_field"  // string -> Nullable<bool>
rd.GetNullableByte "some_field"     // string -> Nullable<byte>
rd.GetNullableChar "some_field"     // string -> Nullable<char>
rd.GetNullableDateTime "some_field" // string -> Nullable<DateTime>
rd.GetNullableDecimal "some_field"  // string -> Nullable<Decimal>
rd.GetNullableDouble "some_field"   // string -> Nullable<Double>
rd.GetNullableFloat "some_field"    // string -> Nullable<float32>
rd.GetNullableGuid "some_field"     // string -> Nullable<Guid>
rd.GetNullableInt16 "some_field"    // string -> Nullable<int16>
rd.GetNullableInt32 "some_field"    // string -> Nullable<int32>
rd.GetNullableInt64 "some_field"    // string -> Nullable<int64>

Find a bug?

There's an issue for that.

License

Built with ♥ by Pim Brouwers in Toronto, ON. Licensed under Apache License 2.0.

Donald

NuGet Version Build Status

Meet Donald.

If you're a programmer and have used a database, he's impacted your life in a big way.

This library is named after him.

Getting Started

Donald is a well-tested library that aims to make working with ADO.NET a little bit more succinct.

Providing basic functional wrappers for the IDbCommand methods ExecuteNonQuery(), ExecuteScalar() & ExecuteReader() and a full-suite of IDataReader extension methods to make retrieving values safer and more direct.

If you came looking for an ORM, this is not your light saber. And may the force be with you.

Install the Donald NuGet package:

PM>  Install-Package Donald

Or using the dotnet CLI

dotnet add package Donald

An example using SQL Server

Consider the following model:

type Author = 
    {
        AuthorId : int
        FullName : string
    }
    // Not mandatory, but helpful
    static member fromReader (rd : IDataReader) = 
        {
            AuthorId = rd.GetInt32("author_id")  // IDataReader extension method
            FullName = rd.GetString("full_name") // IDataReader extension method
        }

Define a DbConnectionFactory

open System.Data.SqlClient
open Donald

let connectionString = 
    "Server=MY_SERVER;Database=MyDatabase;Trusted_Connection=True;"

let connectionFactory : DbConnectionFactory = 
    fun _ -> new SqlConnection(connectionString) :> IDbConnection

Query for multiple strongly-typed results

let findAuthor search =
    use conn = createConn connectionFactory

    query
         "SELECT author_id, full_name
          FROM   author
          WHERE  full_name LIKE @search"
        [ newParam "search" search ]
        Author.fromReader
	conn

Query for exactly one strongly-type result

let getAuthor authorId =
    use conn = createConn connectionFactory

    querySingle // Returns Option<Author>
        "SELECT author_id, full_name
         FROM   author
         WHERE  author_id = @author_id"
         [ newParam "author_id" authorId ]
         Author.fromReader 
     conn

Doing work transactionally

The five main API functions: query, querySingle, scalar, exec and execMany, all have transactional sister functions: tranQuery, tranQuerySingle, tranScalar, tranExec and tranExecMany.

As opposed to an IDbConnection, these functions expect an IDbTransaction as the final paramter.

Execute a statement

let updateAuthor author =
    use conn = createConn connectionFactory
    use tran = beginTran conn 

    tranExec // ExecuteNonQuery() within scope of transaction
        "UPDATE author 
         SET    full_name = @full_name 
         WHERE  author_id = @author_id"
        [ 
            newParam "author_id" author.AuthorId
            newParam "full_name" author.FullName
        ]
        tran

    commitTran tran // safely commit transaction

Execute a statement many times

let insertAuthors =
    use conn = createConn connectionFactory
    use tran = beginTran conn 

    tranExecMany
        "INSERT INTO author (full_name) VALUES (@full_name);"                
        [
            [ newParam "full_name" "Bugs Bunny" ]
            [ newParam "full_name" "Donald Duck" ]
        ]                        
        tran    
  
    commitTran tran

Execute a statement that returns a value

let insertAuthor fullName =
    use conn = createConn connectionFactory
    use tran = beginTran conn // Base function's are transaction-oriented
    
    let authorId = 
        tranScalar // ExecuteScalar() within scope of transaction
            "INSERT INTO author (full_name) 
             VALUES (@full_name);

             SELECT SCOPE_IDENTITY();"
            [ newParam "full_name" fullName]
            Convert.ToInt32 // Any obj -> int function would do here
	    tran

    commitTran tran

    authorId 

IDataReader Extension Methods

To make obtaining values from reader more straight-forward, 3 sets of extension methods are available for:

  1. Get value, automatically defaulted
  2. Get value as option<'a>
  3. Get value as Nullable<'a>

Assume we have an open IDataReader and are currently reading a row, the IDataRecord:

rd.GetString "some_field"           // string -> string
rd.GetBoolean "some_field"          // string -> bool
rd.GetByte "some_field"             // string -> byte
rd.GetChar "some_field"             // string -> char
rd.GetDateTime "some_field"         // string -> DateTime
rd.GetDecimal "some_field"          // string -> Decimal
rd.GetDouble "some_field"           // string -> Double
rd.GetFloat "some_field"            // string -> float32
rd.GetGuid "some_field"             // string -> Guid
rd.GetInt16 "some_field"            // string -> int16
rd.GetInt32 "some_field"            // string -> int32
rd.GetInt64 "some_field"            // string -> int64

rd.GetStringOption "some_field"     // string -> string option
rd.GetBooleanOption "some_field"    // string -> bool option
rd.GetByteOption "some_field"       // string -> byte option
rd.GetCharOption "some_field"       // string -> char option
rd.GetDateTimeOption "some_field"   // string -> DateTime option
rd.GetDecimalOption "some_field"    // string -> Decimal option
rd.GetDoubleOption "some_field"     // string -> Double option
rd.GetFloatOption "some_field"      // string -> float32 option
rd.GetGuidOption "some_field"       // string -> Guid option
rd.GetInt16Option "some_field"      // string -> int16 option
rd.GetInt32Option "some_field"      // string -> int32 option
rd.GetInt64Option "some_field"      // string -> int64 option

rd.GetNullableBoolean "some_field"  // string -> Nullable<bool>
rd.GetNullableByte "some_field"     // string -> Nullable<byte>
rd.GetNullableChar "some_field"     // string -> Nullable<char>
rd.GetNullableDateTime "some_field" // string -> Nullable<DateTime>
rd.GetNullableDecimal "some_field"  // string -> Nullable<Decimal>
rd.GetNullableDouble "some_field"   // string -> Nullable<Double>
rd.GetNullableFloat "some_field"    // string -> Nullable<float32>
rd.GetNullableGuid "some_field"     // string -> Nullable<Guid>
rd.GetNullableInt16 "some_field"    // string -> Nullable<int16>
rd.GetNullableInt32 "some_field"    // string -> Nullable<int32>
rd.GetNullableInt64 "some_field"    // string -> Nullable<int64>

Find a bug?

There's an issue for that.

License

Built with ♥ by Pim Brouwers in Toronto, ON. Licensed under Apache License 2.0.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version History

Version Downloads Last updated
6.2.5 181 8/4/2021
6.2.4 79 8/4/2021
6.2.3 62 7/30/2021
6.2.2 80 7/27/2021
6.2.1 55 7/27/2021
6.2.0 69 7/26/2021
6.1.0 120 7/6/2021
6.1.0-beta3 51 7/5/2021
6.1.0-beta2 88 7/4/2021
6.1.0-beta1 163 7/4/2021
6.0.0 92 4/11/2021
5.1.3 76 3/29/2021
5.1.2 143 2/27/2021
5.1.1 153 1/23/2021
5.0.1 672 12/3/2020
5.0.0 129 12/1/2020
5.0.0-alpha3 142 12/1/2020
5.0.0-alpha2 126 11/30/2020
5.0.0-alpha1 96 11/30/2020
4.0.0 153 11/12/2020
3.0.4 553 10/31/2020
3.0.3 458 8/2/2020
3.0.2 220 7/17/2020
3.0.1 208 7/14/2020
3.0.0 247 6/29/2020
2.0.2 175 5/1/2020
2.0.1 169 4/27/2020
2.0.0 155 4/27/2020
1.0.6 167 4/24/2020
1.0.4 177 4/24/2020
1.0.3 172 4/24/2020
1.0.2 183 4/24/2020
1.0.1 343 4/18/2020
1.0.0 214 4/5/2020
Show less