Skip to content

Instantly share code, notes, and snippets.

@Scooletz
Created September 11, 2015 05:19
Show Gist options
  • Save Scooletz/e281be257f05d622a7e2 to your computer and use it in GitHub Desktop.
Save Scooletz/e281be257f05d622a7e2 to your computer and use it in GitHub Desktop.
System.Data.SqlClient.SqlCommandSet extracted from Data internals, better than NHibernate, as no delegates creation is required beside the first static ctor
public class SqlClientSqlCommandSet : IDisposable
{
private static readonly Type SqlCmdSetType;
private readonly object _instance;
private int _countOfCommands;
private readonly static Action<object, SqlConnection> SetConnection;
private readonly static Func<object, SqlConnection> GetConnection;
private readonly static Action<object, SqlTransaction> SetTransaction;
private readonly static Func<object, SqlCommand> GetCommand;
private readonly static Action<object, SqlCommand> AppendMethod;
private readonly static Func<object, int> ExecuteNonQueryMethod;
private readonly static Action<object> DisposeMethod;
static SqlClientSqlCommandSet()
{
var sysData = typeof(SqlCommand).Assembly;
SqlCmdSetType = sysData.GetType("System.Data.SqlClient.SqlCommandSet");
Debug.Assert(SqlCmdSetType != null, "Could not find SqlCommandSet!");
var p1 = Expression.Parameter(typeof(object));
var converted = Expression.Convert(p1, SqlCmdSetType);
var con = Expression.Parameter(typeof(SqlConnection));
var tran = Expression.Parameter(typeof(SqlTransaction));
var cmd = Expression.Parameter(typeof(SqlCommand));
SetConnection = Expression.Lambda<Action<object, SqlConnection>>(Expression.Call(converted, "set_Connection", null, con), p1, con).Compile();
GetConnection = Expression.Lambda<Func<object, SqlConnection>>(Expression.Call(converted, "get_Connection", null), p1).Compile();
SetTransaction = Expression.Lambda<Action<object, SqlTransaction>>(Expression.Call(converted, "set_Transaction", null, tran), p1, tran).Compile();
GetCommand = Expression.Lambda<Func<object, SqlCommand>>(Expression.Call(converted, "get_BatchCommand", null), p1).Compile();
AppendMethod = Expression.Lambda<Action<object, SqlCommand>>(Expression.Call(converted, "Append", null, cmd), p1, cmd).Compile();
ExecuteNonQueryMethod = Expression.Lambda<Func<object, int>>(Expression.Call(converted, "ExecuteNonQuery", null), p1).Compile();
DisposeMethod = Expression.Lambda<Action<object>>(Expression.Call(converted, "Dispose", null), p1).Compile();
}
public SqlClientSqlCommandSet()
{
_instance = Activator.CreateInstance(SqlCmdSetType, true);
}
/// <summary>
/// Append a command to the batch
/// </summary>
/// <param name="command"></param>
public void Append(SqlCommand command)
{
AssertHasParameters(command);
AppendMethod(_instance, command);
_countOfCommands++;
}
/// <summary>
/// This is required because SqlClient.SqlCommandSet will throw if
/// the command has no parameters.
/// </summary>
/// <param name="command"></param>
private static void AssertHasParameters(SqlCommand command)
{
if (command.Parameters.Count == 0)
{
throw new ArgumentException("A command in SqlCommandSet must have parameters. You can't pass hardcoded sql strings.");
}
}
/// <summary>
/// Return the batch command to be executed
/// </summary>
public SqlCommand BatchCommand
{
get { return GetCommand(_instance); }
}
/// <summary>
/// The number of commands batched in this instance
/// </summary>
public int CountOfCommands
{
get { return _countOfCommands; }
}
/// <summary>
/// Executes the batch
/// </summary>
/// <returns>
/// This seems to be returning the total number of affected rows in all queries
/// </returns>
public int ExecuteNonQuery()
{
if (Connection == null)
throw new ArgumentNullException(
"Connection was not set! You must set the connection property before calling ExecuteNonQuery()");
if (CountOfCommands == 0)
return 0;
return ExecuteNonQueryMethod(_instance);
}
public SqlConnection Connection
{
get { return GetConnection(_instance); }
set { SetConnection(_instance, value); }
}
public SqlTransaction Transaction
{
set { SetTransaction(_instance, value); }
}
///<summary>
///Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///</summary>
///<filterpriority>2</filterpriority>
public void Dispose()
{
DisposeMethod(_instance);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment