diff --git a/SqlMapper.cs b/SqlMapper.cs index 1cdc5496..5fef6de7 100644 --- a/SqlMapper.cs +++ b/SqlMapper.cs @@ -109,33 +109,28 @@ namespace SqlMapper static Dictionary typeMap; + /// + /// Execute parameterized SQL + /// + /// Number of rows affected + public static int ExecuteMapperCommand(this IDbConnection cnn, string sql, object param = null, SqlTransaction transaction = null) + { + return ExecuteCommand(cnn, transaction, sql, GetParamInfo(param)); + } + public static List ExecuteMapperQuery (this IDbConnection cnn, string sql, object param = null, SqlTransaction transaction = null) { // TODO: get rid of casting hackery return ExecuteMapperQuery(cnn, sql, param, transaction).Select(s => s as dynamic).ToList(); } + public static List ExecuteMapperQuery(this IDbConnection cnn, string sql, object param = null, SqlTransaction transaction = null) { var identity = new Identity(sql, typeof(T)); var rval = new List(); - - Func> paramInfoGenerator; - List paramInfo = null; - - if (param != null) - { - if (!cachedParamReaders.TryGetValue(param.GetType(), out paramInfoGenerator)) - { - paramInfoGenerator = CreateParamInfoGenerator(param.GetType()); - cachedParamReaders[param.GetType()] = paramInfoGenerator; - } - paramInfo = paramInfoGenerator(param); - } - - - using (var reader = GetReader(cnn, transaction, sql, paramInfo)) + using (var reader = GetReader(cnn, transaction, sql, GetParamInfo(param))) { object oDeserializer; if (!cachedSerializers.TryGetValue(identity, out oDeserializer)) @@ -167,6 +162,24 @@ namespace SqlMapper return rval; } + private static List GetParamInfo(object param) + { + Func> paramInfoGenerator; + List paramInfo = null; + + if (param != null) + { + if (!cachedParamReaders.TryGetValue(param.GetType(), out paramInfoGenerator)) + { + paramInfoGenerator = CreateParamInfoGenerator(param.GetType()); + cachedParamReaders[param.GetType()] = paramInfoGenerator; + } + paramInfo = paramInfoGenerator(param); + } + + return paramInfo; + } + private static object GetDynamicDeserializer(IDataReader reader) { List colNames = new List(); @@ -228,60 +241,75 @@ namespace SqlMapper return (Func>)dm.CreateDelegate(typeof(Func>)); } - - private static IDataReader GetReader(IDbConnection cnn, SqlTransaction tranaction, string sql, List paramInfo) + private static IDbCommand SetupCommand(IDbConnection cnn, SqlTransaction tranaction, string sql, List paramInfo) { - using (var cmd = cnn.CreateCommand()) + var cmd = cnn.CreateCommand(); + + cmd.Transaction = tranaction; + cmd.CommandText = sql; + if (paramInfo != null) { - cmd.Transaction = tranaction; - cmd.CommandText = sql; - if (paramInfo != null) + foreach (var info in paramInfo) { - foreach (var info in paramInfo) + var param = new SqlParameter("@" + info.Name, info.Type); + + + param.Value = info.Val ?? DBNull.Value; + param.Direction = ParameterDirection.Input; + if (info.Type == SqlDbType.NVarChar) { - var param = new SqlParameter("@" + info.Name, info.Type); - + param.Size = 4000; + } - param.Value = info.Val ?? DBNull.Value; - param.Direction = ParameterDirection.Input; - if (info.Type == SqlDbType.NVarChar) + if (info.Type == SqlDbType.Structured) + { + + // initially we tried TVP, however it performs quite poorly. + // keep in mind SQL support up to 2000 params easily in sp_executesql, needing more is rare + + var list = info.Val as IEnumerable; + var count = 0; + + if (list != null) { - param.Size = 4000; - } - - if (info.Type == SqlDbType.Structured) - { - - // initially we tried TVP, however it performs quite poorly. - // keep in mind SQL support up to 2000 params easily in sp_executesql, needing more is rare - - var list = info.Val as IEnumerable; - var count = 0; - - if (list != null) - { - foreach (var item in list) - { - count++; - cmd.Parameters.Add(new SqlParameter("@" + info.Name + count, item)); - } - - cmd.CommandText = cmd.CommandText.Replace("@" + info.Name, - "(" + string.Join( - ",", Enumerable.Range(1, count).Select(i => "@" + info.Name + i) - ) + ")"); - } - else + foreach (var item in list) { - throw new NotImplementedException(); + count++; + cmd.Parameters.Add(new SqlParameter("@" + info.Name + count, item)); } + + cmd.CommandText = cmd.CommandText.Replace("@" + info.Name, + "(" + string.Join( + ",", Enumerable.Range(1, count).Select(i => "@" + info.Name + i) + ) + ")"); } else { - cmd.Parameters.Add(param); + throw new NotImplementedException(); } } + else + { + cmd.Parameters.Add(param); + } } + } + return cmd; + } + + + private static int ExecuteCommand(IDbConnection cnn, SqlTransaction tranaction, string sql, List paramInfo) + { + using (var cmd = SetupCommand(cnn, tranaction, sql, paramInfo)) + { + return cmd.ExecuteNonQuery(); + } + } + + private static IDataReader GetReader(IDbConnection cnn, SqlTransaction tranaction, string sql, List paramInfo) + { + using (var cmd = SetupCommand(cnn, tranaction, sql, paramInfo)) + { return cmd.ExecuteReader(); } } diff --git a/Tests.cs b/Tests.cs index f8b6f9c1..1490bc5d 100644 --- a/Tests.cs +++ b/Tests.cs @@ -118,10 +118,22 @@ namespace SqlMapper } public void TestStringList() - { + { connection.ExecuteMapperQuery("select * from (select 'a' as x union all select 'b' union all select 'c') as T where x in @strings", new {strings = new[] {"a","b","c"}}) .IsSequenceEqual(new[] {"a","b","c"}); } + public void TestExecuteCommand() + { + connection.ExecuteMapperCommand(@" + set nocount on + create table #t(i int) + set nocount off + insert #t + select @a a union all select @b + set nocount on + drop table #t", new {a=1, b=2 }).IsEquals(2); + } + } }