Commit 8c9bb526 authored by Marc Gravell's avatar Marc Gravell

Add DataTable support as a custom handler, with extended-property detection for TypeName

parent 58fb9c5c
......@@ -177,7 +177,6 @@ internal IDbCommand SetupCommand(IDbConnection cnn, Action<IDbCommand, object> p
SqlMapper.Link<Type, Action<IDbCommand, bool>>.TryAdd(ref bindByNameCache, commandType, ref action);
return action;
}
}
/// <summary>
......@@ -617,6 +616,8 @@ static SqlMapper()
typeMap[typeof(DateTimeOffset?)] = DbType.DateTimeOffset;
typeMap[typeof(TimeSpan?)] = DbType.Time;
typeMap[typeof(object)] = DbType.Object;
AddTypeHandler(typeof(DataTable), new DataTableHandler());
}
/// <summary>
/// Configire the specified type to be mapped to a given db-type
......@@ -3437,6 +3438,11 @@ private static void EmitInt32(ILGenerator il, int value)
}
/// <summary>
/// Key used to indicate the type name associated with a DataTable
/// </summary>
private const string DataTableTypeNameKey = "dapper:TypeName";
/// <summary>
/// How should connection strings be compared for equivalence? Defaults to StringComparer.Ordinal.
/// Providing a custom implementation can be useful for allowing multi-tenancy databases with identical
......@@ -3450,6 +3456,7 @@ public static IEqualityComparer<string> ConnectionStringComparer
}
private static IEqualityComparer<string> connectionStringComparer = StringComparer.Ordinal;
/// <summary>
/// The grid reader provides interfaces for reading multiple result sets from a Dapper query
/// </summary>
......@@ -3700,6 +3707,28 @@ public void Dispose()
{
return new TableValuedParameter(table, typeName);
}
/// <summary>
/// Associate a DataTable with a type name
/// </summary>
public static void SetTypeName(this DataTable table, string typeName)
{
if (table != null)
{
if (string.IsNullOrEmpty(typeName))
table.ExtendedProperties.Remove(DataTableTypeNameKey);
else
table.ExtendedProperties[DataTableTypeNameKey] = typeName;
}
}
/// <summary>
/// Fetch the type name associated with a DataTable
/// </summary>
public static string GetTypeName(this DataTable table)
{
return table == null ? null : table.ExtendedProperties[DataTableTypeNameKey] as string;
}
}
/// <summary>
......@@ -3982,6 +4011,19 @@ public T Get<T>(string name)
}
}
sealed class DataTableHandler : Dapper.SqlMapper.ITypeHandler
{
public object Parse(Type destinationType, object value)
{
throw new NotImplementedException();
}
public void SetValue(IDbDataParameter parameter, object value)
{
TableValuedParameter.Set(parameter, value as DataTable, null);
}
}
/// <summary>
/// Used to pass a DataTable as a TableValuedParameter
/// </summary>
......@@ -4016,17 +4058,25 @@ void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string nam
{
var param = command.CreateParameter();
param.ParameterName = name;
param.Value = (object)table ?? DBNull.Value;
Set(param, table, typeName);
command.Parameters.Add(param);
}
internal static void Set(IDbDataParameter parameter, DataTable table, string typeName)
{
parameter.Value = (object)table ?? DBNull.Value;
if (string.IsNullOrEmpty(typeName) && table != null)
{
typeName = SqlMapper.GetTypeName(table);
}
if (!string.IsNullOrEmpty(typeName))
{
var sqlParam = param as System.Data.SqlClient.SqlParameter;
var sqlParam = parameter as System.Data.SqlClient.SqlParameter;
if (sqlParam != null)
{
if (setTypeName != null) setTypeName(sqlParam, typeName);
sqlParam.SqlDbType = SqlDbType.Structured;
}
}
command.Parameters.Add(param);
}
}
/// <summary>
......
......@@ -2878,6 +2878,8 @@ class HasDoubleDecimal
public void DataTableParameters()
{
try { connection.Execute("drop proc #DataTableParameters"); } catch { }
try { connection.Execute("drop table #DataTableParameters"); } catch { }
try { connection.Execute("drop type MyTVPType"); } catch { }
connection.Execute("create type MyTVPType as table (id int)");
connection.Execute("create proc #DataTableParameters @ids MyTVPType readonly as select count(1) from @ids");
......@@ -2900,6 +2902,33 @@ public void DataTableParameters()
}
}
public void DataTableParametersWithExtendedProperty()
{
try { connection.Execute("drop proc #DataTableParameters"); } catch { }
try { connection.Execute("drop table #DataTableParameters"); } catch { }
try { connection.Execute("drop type MyTVPType"); } catch { }
connection.Execute("create type MyTVPType as table (id int)");
connection.Execute("create proc #DataTableParameters @ids MyTVPType readonly as select count(1) from @ids");
var table = new DataTable { Columns = { { "id", typeof(int) } }, Rows = { { 1 }, { 2 }, { 3 } } };
table.SetTypeName("MyTVPType"); // <== extended metadata
int count = connection.Query<int>("#DataTableParameters", new { ids = table }, commandType: CommandType.StoredProcedure).First();
count.IsEqualTo(3);
count = connection.Query<int>("select count(1) from @ids", new { ids = table }).First();
count.IsEqualTo(3);
try
{
connection.Query<int>("select count(1) from @ids", new { ids = table }).First();
throw new InvalidOperationException();
}
catch (Exception ex)
{
ex.Message.Equals("The table type parameter 'ids' must have a valid type name.");
}
}
public void SupportInit()
{
var obj = connection.Query<WithInit>("select 'abc' as Value").Single();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment