Skip to content

Commit

Permalink
5.9.0
Browse files Browse the repository at this point in the history
Implemented correct transaction flow.
Added extensions for creating commands from transactions.
Expanded expressive commands to allow for transactions.
Added examples.
  • Loading branch information
electricessence committed Apr 1, 2018
1 parent 02341c3 commit 15f086f
Show file tree
Hide file tree
Showing 27 changed files with 8,560 additions and 1,522 deletions.
11 changes: 11 additions & 0 deletions Examples/Examples.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Open.Database.Extensions.csproj" />
</ItemGroup>

</Project>
30 changes: 30 additions & 0 deletions Examples/Transaction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Open.Database.Extensions;
using System.Data.SqlClient;

public static partial class Examples
{
public readonly static DbConnectionFactory ConnectionFactory
= new DbConnectionFactory(()=>new SqlConnection());

// Returns true if the transaction is successful.
public static bool TryTransaction()
=> ConnectionFactory.Using(connection =>
// Open a connection and start a transaction.
connection.ExecuteTransactionConditional(transaction => {

// First procedure does some updates.
var count = transaction
.StoredProcedure("[Updated Procedure]")
.ExecuteNonQuery();

// Second procedure validates the results.
// If it returns true, then the transaction is commited.
// If it returns false, then the transaction is rolled back.
return transaction
.StoredProcedure("[Validation Procedure]")
.AddParam("@ExpectedCount", count)
.ExecuteScalar<bool>();
}));

}

79 changes: 45 additions & 34 deletions ExpressiveCommandBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,19 @@ public abstract partial class ExpressiveCommandBase<TConnection, TCommand, TDbTy
/// </summary>
protected readonly TConnection Connection;

/// <summary>
/// The transaction to execute commands on if not using a connection factory.
/// </summary>
protected readonly IDbTransaction Transaction;

ExpressiveCommandBase(
CommandType type,
string command,
List<Param> @params = null)
IEnumerable<Param> @params)
{
Type = type;
Command = command ?? throw new ArgumentNullException(nameof(command));
Params = @params ?? throw new ArgumentNullException(nameof(@params));
Params = @params?.ToList() ?? new List<Param>();
Timeout = CommandTimeout.DEFAULT_SECONDS;
}

Expand All @@ -50,25 +55,28 @@ protected ExpressiveCommandBase(
IDbConnectionFactory<TConnection> connFactory,
CommandType type,
string command,
List<Param> @params)
: this(type, command, @params ?? new List<Param>())
IEnumerable<Param> @params)
: this(type, command, @params)
{
ConnectionFactory = connFactory ?? throw new ArgumentNullException(nameof(connFactory));
}

/// <param name="connection">The connection to execute the command on.</param>
/// <param name="type">The command type>.</param>
/// <param name="command">The SQL command.</param>
/// <param name="params">The list of params</param>
protected ExpressiveCommandBase(
/// <param name="connection">The connection to execute the command on.</param>
/// <param name="transaction">The optional transaction to execute the command on.</param>
/// <param name="type">The command type>.</param>
/// <param name="command">The SQL command.</param>
/// <param name="params">The list of params</param>
protected ExpressiveCommandBase(
TConnection connection,
IDbTransaction transaction,
CommandType type,
string command,
List<Param> @params)
: this(type, command, @params ?? new List<Param>())
IEnumerable<Param> @params)
: this(type, command, @params)
{
Connection = connection ?? throw new ArgumentNullException(nameof(connection));
}
Transaction = transaction;
}

/// <summary>
/// The command text or procedure name to use.
Expand Down Expand Up @@ -252,36 +260,36 @@ public TThis SetTimeout(ushort seconds)
/// Handles providing the connection for use with the command.
/// </summary>
/// <param name="action">The handler for use with the connection.</param>
protected void UsingConnection(Action<TConnection> action)
protected void UsingConnection(Action<TConnection, IDbTransaction> action)
{
if (ConnectionFactory != null)
{
using (var conn = ConnectionFactory.Create())
{
action(conn);
}
}
else
{
action(Connection);
}
}
if (Connection != null)
{
action(Connection, Transaction);
}
else
{
using (var conn = ConnectionFactory.Create())
{
action(conn, null);
}
}
}

/// <summary>
/// Handles providing the connection for use with the command.
/// </summary>
/// <param name="action">The handler for use with the connection.</param>
protected T UsingConnection<T>(Func<TConnection, T> action)
protected T UsingConnection<T>(Func<TConnection, IDbTransaction, T> action)
{
if (Connection != null)
{
return action(Connection);
return action(Connection, Transaction);
}
else
{
using (var conn = ConnectionFactory.Create())
{
return action(conn);
return action(conn, null);
}
}
}
Expand All @@ -291,13 +299,14 @@ protected T UsingConnection<T>(Func<TConnection, T> action)
/// </summary>
/// <param name="handler">The handler function for each IDataRecord.</param>
public void Execute(Action<TCommand> handler)
=> UsingConnection(con =>
=> UsingConnection((con,t) =>
{
using (var cmd = con.CreateCommand(Type, Command, Timeout))
{
var c = cmd as TCommand;
if (c == null) throw new InvalidCastException($"Actual command type ({cmd.GetType()}) is not compatible with expected command type ({typeof(TCommand)}).");
AddParams(c);
if (t != null) c.Transaction = t;
AddParams(c);
con.EnsureOpen();
handler(c);
}
Expand All @@ -311,13 +320,14 @@ public void Execute(Action<TCommand> handler)
/// <param name="transform">The transform function for each IDataRecord.</param>
/// <returns>The result of the transform.</returns>
public T Execute<T>(Func<TCommand, T> transform)
=> UsingConnection(con =>
=> UsingConnection((con,t) =>
{
using (var cmd = con.CreateCommand(Type, Command, Timeout))
{
var c = cmd as TCommand;
if (c == null) throw new InvalidCastException($"Actual command type ({cmd.GetType()}) is not compatible with expected command type ({typeof(TCommand)}).");
AddParams(c);
if (t != null) c.Transaction = t;
AddParams(c);
con.EnsureOpen();
return transform(c);
}
Expand All @@ -329,13 +339,14 @@ public T Execute<T>(Func<TCommand, T> transform)
/// </summary>
/// <returns>The value from the return parameter.</returns>
public object ExecuteReturn()
=> UsingConnection(con =>
=> UsingConnection((con,t) =>
{
using (var cmd = con.CreateCommand(Type, Command, Timeout))
{
var c = cmd as TCommand;
if (c == null) throw new InvalidCastException($"Actual command type ({cmd.GetType()}) is not compatible with expected command type ({typeof(TCommand)}).");
AddParams(c);
if (t != null) c.Transaction = t;
AddParams(c);
var returnParameter = c.CreateParameter();
returnParameter.Direction = ParameterDirection.ReturnValue;
c.Parameters.Add(returnParameter);
Expand Down
53 changes: 22 additions & 31 deletions ExpressiveDbCommand.cs
Original file line number Diff line number Diff line change
@@ -1,49 +1,40 @@
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;

namespace Open.Database.Extensions
{

/// <summary>
/// An abstraction for executing commands on a database using best practices and simplified expressive syntax.
/// </summary>
public class ExpressiveDbCommand : ExpressiveDbCommandBase<DbConnection, DbCommand, DbType, ExpressiveDbCommand>
/// <summary>
/// An abstraction for executing commands on a database using best practices and simplified expressive syntax.
/// </summary>
public class ExpressiveDbCommand : ExpressiveDbCommandBase<DbConnection, DbCommand, DbType, ExpressiveDbCommand>
{
/// <param name="connFactory">The factory to generate connections from.</param>
/// <param name="type">The command type>.</param>
/// <param name="command">The SQL command.</param>
/// <param name="params">The list of params</param>
public ExpressiveDbCommand(IDbConnectionFactory<DbConnection> connFactory, CommandType type, string command, List<Param> @params)
: base(connFactory, type, command, @params?.ToList())
public ExpressiveDbCommand(
IDbConnectionFactory<DbConnection> connFactory,
CommandType type,
string command,
IEnumerable<Param> @params = null)
: base(connFactory, type, command, @params)
{
}

/// <param name="connFactory">The factory to generate connections from.</param>
/// <param name="type">The command type>.</param>
/// <param name="command">The SQL command.</param>
/// <param name="params">The list of params</param>
public ExpressiveDbCommand(IDbConnectionFactory<DbConnection> connFactory, CommandType type, string command, params Param[] @params)
: base(connFactory, type, command, @params?.ToList())
{
}

/// <param name="connection">The connection to execute the command on.</param>
/// <param name="type">The command type>.</param>
/// <param name="command">The SQL command.</param>
/// <param name="params">The list of params</param>
public ExpressiveDbCommand(DbConnection connection, CommandType type, string command, List<Param> @params)
: base(connection, type, command, @params?.ToList())
{
}

/// <param name="connection">The connection to execute the command on.</param>
/// <param name="type">The command type>.</param>
/// <param name="command">The SQL command.</param>
/// <param name="params">The list of params</param>
public ExpressiveDbCommand(DbConnection connection, CommandType type, string command, params Param[] @params)
: this(connection, type, command, @params?.ToList())
/// <param name="connection">The connection to execute the command on.</param>
/// <param name="transaction">The optional transaction to execute the command on.</param>
/// <param name="type">The command type>.</param>
/// <param name="command">The SQL command.</param>
/// <param name="params">The list of params</param>
public ExpressiveDbCommand(
DbConnection connection,
DbTransaction transaction,
CommandType type,
string command,
IEnumerable<Param> @params = null)
: base(connection, transaction, type, command, @params)
{
}

Expand Down
9 changes: 6 additions & 3 deletions ExpressiveDbCommandBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,23 @@ protected ExpressiveDbCommandBase(
IDbConnectionFactory<TConnection> connFactory,
CommandType type,
string command,
List<Param> @params)
IEnumerable<Param> @params)
: base(connFactory, type, command, @params)
{
}

/// <param name="connection">The connection to execute the command on.</param>
/// <param name="transaction">The optional transaction to execute the command on.</param>
/// <param name="type">The command type>.</param>
/// <param name="command">The SQL command.</param>
/// <param name="params">The list of params</param>
protected ExpressiveDbCommandBase(
TConnection connection,
IDbTransaction transaction,
CommandType type,
string command,
List<Param> @params)
: base(connection, type, command, @params)
IEnumerable<Param> @params)
: base(connection, transaction, type, command, @params)
{
}

Expand Down Expand Up @@ -223,6 +225,7 @@ public Task IterateReaderAsync(Action<IDataRecord> handler, CancellationToken? t
/// Iterates asynchronously until the handler returns false. Then cancels.
/// </summary>
/// <param name="predicate">If true, the iteration continues.</param>
/// <param name="token">An optional cancellation token.</param>
/// <returns>The task that completes when the iteration is done or the predicate evaluates false.</returns>
public Task IterateReaderAsyncWhile(Func<IDataRecord, bool> predicate, CancellationToken? token = null)
=> ExecuteAsync(command => command.IterateReaderAsyncWhile(predicate), token);
Expand Down
28 changes: 26 additions & 2 deletions Extensions.ConnectionFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,20 @@ public static ExpressiveDbCommand Command(
this DbConnection target,
string command,
CommandType type = CommandType.Text)
=> new ExpressiveDbCommand(target, type, command);
=> new ExpressiveDbCommand(target, null, type, command);

/// <summary>
/// Creates an ExpressiveDbCommand for subsequent configuration and execution.
/// </summary>
/// <param name="target">The transaction to execute the command on.</param>
/// <param name="command">The command text or stored procedure name to use.</param>
/// <param name="type">The command type.</param>
/// <returns>The resultant ExpressiveDbCommand.</returns>
public static ExpressiveDbCommand Command(
this DbTransaction target,
string command,
CommandType type = CommandType.Text)
=> new ExpressiveDbCommand(target.Connection, target, type, command);

/// <summary>
/// Creates an ExpressiveDbCommand with command type set to StoredProcedure for subsequent configuration and execution.
Expand All @@ -166,7 +179,18 @@ public static ExpressiveDbCommand Command(
public static ExpressiveDbCommand StoredProcedure(
this DbConnection target,
string command)
=> new ExpressiveDbCommand(target, CommandType.StoredProcedure, command);
=> new ExpressiveDbCommand(target, null, CommandType.StoredProcedure, command);

/// <summary>
/// Creates an ExpressiveDbCommand with command type set to StoredProcedure for subsequent configuration and execution.
/// </summary>
/// <param name="target">The transaction to execute the command on.</param>
/// <param name="command">The command text or stored procedure name to use.</param>
/// <returns>The resultant ExpressiveDbCommand.</returns>
public static ExpressiveDbCommand StoredProcedure(
this DbTransaction target,
string command)
=> new ExpressiveDbCommand(target.Connection, target, CommandType.StoredProcedure, command);

/// <summary>
/// Creates an ExpressiveDbCommand for subsequent configuration and execution.
Expand Down
Loading

0 comments on commit 15f086f

Please sign in to comment.