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 ...@@ -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); SqlMapper.Link<Type, Action<IDbCommand, bool>>.TryAdd(ref bindByNameCache, commandType, ref action);
return action; return action;
} }
} }
/// <summary> /// <summary>
...@@ -617,6 +616,8 @@ static SqlMapper() ...@@ -617,6 +616,8 @@ static SqlMapper()
typeMap[typeof(DateTimeOffset?)] = DbType.DateTimeOffset; typeMap[typeof(DateTimeOffset?)] = DbType.DateTimeOffset;
typeMap[typeof(TimeSpan?)] = DbType.Time; typeMap[typeof(TimeSpan?)] = DbType.Time;
typeMap[typeof(object)] = DbType.Object; typeMap[typeof(object)] = DbType.Object;
AddTypeHandler(typeof(DataTable), new DataTableHandler());
} }
/// <summary> /// <summary>
/// Configire the specified type to be mapped to a given db-type /// Configire the specified type to be mapped to a given db-type
...@@ -3437,6 +3438,11 @@ private static void EmitInt32(ILGenerator il, int value) ...@@ -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> /// <summary>
/// How should connection strings be compared for equivalence? Defaults to StringComparer.Ordinal. /// 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 /// Providing a custom implementation can be useful for allowing multi-tenancy databases with identical
...@@ -3450,6 +3456,7 @@ public static IEqualityComparer<string> ConnectionStringComparer ...@@ -3450,6 +3456,7 @@ public static IEqualityComparer<string> ConnectionStringComparer
} }
private static IEqualityComparer<string> connectionStringComparer = StringComparer.Ordinal; private static IEqualityComparer<string> connectionStringComparer = StringComparer.Ordinal;
/// <summary> /// <summary>
/// The grid reader provides interfaces for reading multiple result sets from a Dapper query /// The grid reader provides interfaces for reading multiple result sets from a Dapper query
/// </summary> /// </summary>
...@@ -3700,6 +3707,28 @@ public void Dispose() ...@@ -3700,6 +3707,28 @@ public void Dispose()
{ {
return new TableValuedParameter(table, typeName); 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> /// <summary>
...@@ -3982,6 +4011,19 @@ public T Get<T>(string name) ...@@ -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> /// <summary>
/// Used to pass a DataTable as a TableValuedParameter /// Used to pass a DataTable as a TableValuedParameter
/// </summary> /// </summary>
...@@ -4016,17 +4058,25 @@ void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string nam ...@@ -4016,17 +4058,25 @@ void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string nam
{ {
var param = command.CreateParameter(); var param = command.CreateParameter();
param.ParameterName = name; 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)) if (!string.IsNullOrEmpty(typeName))
{ {
var sqlParam = param as System.Data.SqlClient.SqlParameter; var sqlParam = parameter as System.Data.SqlClient.SqlParameter;
if (sqlParam != null) if (sqlParam != null)
{ {
if (setTypeName != null) setTypeName(sqlParam, typeName); if (setTypeName != null) setTypeName(sqlParam, typeName);
sqlParam.SqlDbType = SqlDbType.Structured; sqlParam.SqlDbType = SqlDbType.Structured;
} }
} }
command.Parameters.Add(param);
} }
} }
/// <summary> /// <summary>
......
...@@ -2878,6 +2878,8 @@ class HasDoubleDecimal ...@@ -2878,6 +2878,8 @@ class HasDoubleDecimal
public void DataTableParameters() public void DataTableParameters()
{ {
try { connection.Execute("drop proc #DataTableParameters"); } catch { }
try { connection.Execute("drop table #DataTableParameters"); } catch { }
try { connection.Execute("drop type MyTVPType"); } catch { } try { connection.Execute("drop type MyTVPType"); } catch { }
connection.Execute("create type MyTVPType as table (id int)"); connection.Execute("create type MyTVPType as table (id int)");
connection.Execute("create proc #DataTableParameters @ids MyTVPType readonly as select count(1) from @ids"); connection.Execute("create proc #DataTableParameters @ids MyTVPType readonly as select count(1) from @ids");
...@@ -2900,6 +2902,33 @@ public void DataTableParameters() ...@@ -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() public void SupportInit()
{ {
var obj = connection.Query<WithInit>("select 'abc' as Value").Single(); 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