Skip to content

Commit

Permalink
Enable SqlDataRecord TVPs on corefx. (#801)
Browse files Browse the repository at this point in the history
  • Loading branch information
sergiivolchkov authored and NickCraver committed Jun 15, 2017
1 parent 0151dea commit e7ad087
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 61 deletions.
135 changes: 98 additions & 37 deletions Dapper.Tests/ParameterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,24 @@ void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Id
}
}

private static List<Microsoft.SqlServer.Server.SqlDataRecord> CreateSqlDataRecordList(IEnumerable<int> numbers)
{
var number_list = new List<Microsoft.SqlServer.Server.SqlDataRecord>();

// Create an SqlMetaData object that describes our table type.
Microsoft.SqlServer.Server.SqlMetaData[] tvp_definition = { new Microsoft.SqlServer.Server.SqlMetaData("n", SqlDbType.Int) };

foreach (int n in numbers)
{
// Create a new record, using the metadata array above.
var rec = new Microsoft.SqlServer.Server.SqlDataRecord(tvp_definition);
rec.SetInt32(0, n); // Set the value.
number_list.Add(rec); // Add it to the list.
}

return number_list;
}

private class IntDynamicParam : SqlMapper.IDynamicParameters
{
private readonly IEnumerable<int> numbers;
Expand All @@ -50,18 +68,7 @@ public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
var sqlCommand = (SqlCommand)command;
sqlCommand.CommandType = CommandType.StoredProcedure;

var number_list = new List<Microsoft.SqlServer.Server.SqlDataRecord>();

// Create an SqlMetaData object that describes our table type.
Microsoft.SqlServer.Server.SqlMetaData[] tvp_definition = { new Microsoft.SqlServer.Server.SqlMetaData("n", SqlDbType.Int) };

foreach (int n in numbers)
{
// Create a new record, using the metadata array above.
var rec = new Microsoft.SqlServer.Server.SqlDataRecord(tvp_definition);
rec.SetInt32(0, n); // Set the value.
number_list.Add(rec); // Add it to the list.
}
var number_list = CreateSqlDataRecordList(numbers);

// Add the table parameter.
var p = sqlCommand.Parameters.Add("ints", SqlDbType.Structured);
Expand All @@ -84,18 +91,7 @@ public void AddParameter(IDbCommand command, string name)
var sqlCommand = (SqlCommand)command;
sqlCommand.CommandType = CommandType.StoredProcedure;

var number_list = new List<Microsoft.SqlServer.Server.SqlDataRecord>();

// Create an SqlMetaData object that describes our table type.
Microsoft.SqlServer.Server.SqlMetaData[] tvp_definition = { new Microsoft.SqlServer.Server.SqlMetaData("n", SqlDbType.Int) };

foreach (int n in numbers)
{
// Create a new record, using the metadata array above.
var rec = new Microsoft.SqlServer.Server.SqlDataRecord(tvp_definition);
rec.SetInt32(0, n); // Set the value.
number_list.Add(rec); // Add it to the list.
}
var number_list = CreateSqlDataRecordList(numbers);

// Add the table parameter.
var p = sqlCommand.Parameters.Add(name, SqlDbType.Structured);
Expand Down Expand Up @@ -217,7 +213,6 @@ public void TestMassiveStrings()
.IsEqualTo(str);
}

#if !COREFX
[Fact]
public void TestTVPWithAnonymousObject()
{
Expand Down Expand Up @@ -288,18 +283,7 @@ public new void AddParameters(IDbCommand command, SqlMapper.Identity identity)
var sqlCommand = (SqlCommand)command;
sqlCommand.CommandType = CommandType.StoredProcedure;

var number_list = new List<Microsoft.SqlServer.Server.SqlDataRecord>();

// Create an SqlMetaData object that describes our table type.
Microsoft.SqlServer.Server.SqlMetaData[] tvp_definition = { new Microsoft.SqlServer.Server.SqlMetaData("n", SqlDbType.Int) };

foreach (int n in numbers)
{
// Create a new record, using the metadata array above.
var rec = new Microsoft.SqlServer.Server.SqlDataRecord(tvp_definition);
rec.SetInt32(0, n); // Set the value.
number_list.Add(rec); // Add it to the list.
}
var number_list = CreateSqlDataRecordList(numbers);

// Add the table parameter.
var p = sqlCommand.Parameters.Add("ints", SqlDbType.Structured);
Expand Down Expand Up @@ -343,6 +327,83 @@ public void TestTVPWithAdditionalParams()
}
}

[Fact]
public void TestSqlDataRecordListParametersWithAsTableValuedParameter()
{
try
{
connection.Execute("CREATE TYPE int_list_type AS TABLE (n int NOT NULL PRIMARY KEY)");
connection.Execute("CREATE PROC get_ints @integers int_list_type READONLY AS select * from @integers");

var records = CreateSqlDataRecordList(new int[] { 1, 2, 3 });

var nums = connection.Query<int>("get_ints", new { integers = records.AsTableValuedParameter() }, commandType: CommandType.StoredProcedure).ToList();
nums.IsSequenceEqualTo(new int[] { 1, 2, 3 });

nums = connection.Query<int>("select * from @integers", new { integers = records.AsTableValuedParameter("int_list_type") }).ToList();
nums.IsSequenceEqualTo(new int[] { 1, 2, 3 });

try
{
connection.Query<int>("select * from @integers", new { integers = records.AsTableValuedParameter() }).First();
throw new InvalidOperationException();
}
catch (Exception ex)
{
ex.Message.Equals("The table type parameter 'ids' must have a valid type name.");
}
}
finally
{
try
{
connection.Execute("DROP PROC get_ints");
}
finally
{
connection.Execute("DROP TYPE int_list_type");
}
}
}

[Fact]
public void TestSqlDataRecordListParametersWithTypeHandlers()
{
try
{
connection.Execute("CREATE TYPE int_list_type AS TABLE (n int NOT NULL PRIMARY KEY)");
connection.Execute("CREATE PROC get_ints @integers int_list_type READONLY AS select * from @integers");

// Variable type has to be IEnumerable<SqlDataRecord> for TypeHandler to kick in.
IEnumerable<Microsoft.SqlServer.Server.SqlDataRecord> records = CreateSqlDataRecordList(new int[] { 1, 2, 3 });

var nums = connection.Query<int>("get_ints", new { integers = records }, commandType: CommandType.StoredProcedure).ToList();
nums.IsSequenceEqualTo(new int[] { 1, 2, 3 });

try
{
connection.Query<int>("select * from @integers", new { integers = records }).First();
throw new InvalidOperationException();
}
catch (Exception ex)
{
ex.Message.Equals("The table type parameter 'ids' must have a valid type name.");
}
}
finally
{
try
{
connection.Execute("DROP PROC get_ints");
}
finally
{
connection.Execute("DROP TYPE int_list_type");
}
}
}

#if !COREFX
[Fact]
public void DataTableParameters()
{
Expand Down
2 changes: 1 addition & 1 deletion Dapper/Dapper.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.Collections.Concurrent" Version="4.3.0" />
<PackageReference Include="System.Collections.NonGeneric" Version="4.3.0" />
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.3.0" />
<PackageReference Include="System.Dynamic.Runtime" Version="4.3.0" />
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" />
Expand Down
2 changes: 0 additions & 2 deletions Dapper/SqlDataRecordHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Data;

#if !COREFX
namespace Dapper
{
internal sealed class SqlDataRecordHandler : SqlMapper.ITypeHandler
Expand All @@ -18,4 +17,3 @@ public void SetValue(IDbDataParameter parameter, object value)
}
}
}
#endif
16 changes: 1 addition & 15 deletions Dapper/SqlDataRecordListTVPParameter.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
#if !COREFX

namespace Dapper
{
/// <summary>
Expand All @@ -23,18 +22,6 @@ public SqlDataRecordListTVPParameter(IEnumerable<Microsoft.SqlServer.Server.SqlD
this.typeName = typeName;
}

private static readonly Action<System.Data.SqlClient.SqlParameter, string> setTypeName;

static SqlDataRecordListTVPParameter()
{
var prop = typeof(System.Data.SqlClient.SqlParameter).GetProperty(nameof(System.Data.SqlClient.SqlParameter.TypeName), BindingFlags.Instance | BindingFlags.Public);
if (prop != null && prop.PropertyType == typeof(string) && prop.CanWrite)
{
setTypeName = (Action<System.Data.SqlClient.SqlParameter, string>)
Delegate.CreateDelegate(typeof(Action<System.Data.SqlClient.SqlParameter, string>), prop.GetSetMethod());
}
}

void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string name)
{
var param = command.CreateParameter();
Expand All @@ -54,4 +41,3 @@ internal static void Set(IDbDataParameter parameter, IEnumerable<Microsoft.SqlSe
}
}
}
#endif
10 changes: 4 additions & 6 deletions Dapper/SqlMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,24 +219,22 @@ private static void ResetTypeHandlers(bool clone)
typeHandlers = new Dictionary<Type, ITypeHandler>();
#if !COREFX
AddTypeHandlerImpl(typeof(DataTable), new DataTableHandler(), clone);
#endif
try
{
AddSqlDataRecordsTypeHandler(clone);
}
catch { /* https://github.com/StackExchange/dapper-dot-net/issues/424 */ }
#endif
AddTypeHandlerImpl(typeof(XmlDocument), new XmlDocumentHandler(), clone);
AddTypeHandlerImpl(typeof(XDocument), new XDocumentHandler(), clone);
AddTypeHandlerImpl(typeof(XElement), new XElementHandler(), clone);
}

#if !COREFX
[MethodImpl(MethodImplOptions.NoInlining)]
private static void AddSqlDataRecordsTypeHandler(bool clone)
{
AddTypeHandlerImpl(typeof(IEnumerable<Microsoft.SqlServer.Server.SqlDataRecord>), new SqlDataRecordHandler(), clone);
}
#endif

/// <summary>
/// Configure the specified type to be mapped to a given db-type.
Expand Down Expand Up @@ -3662,15 +3660,15 @@ public static void SetTypeName(this DataTable table, string typeName)
/// <param name="table">The <see cref="DataTable"/> that has a type name associated with it.</param>
public static string GetTypeName(this DataTable table) =>
table?.ExtendedProperties[DataTableTypeNameKey] as string;
#endif

/// <summary>
/// Used to pass a IEnumerable&lt;SqlDataRecord&gt; as a <see cref="TableValuedParameter"/>.
/// Used to pass a IEnumerable&lt;SqlDataRecord&gt; as a TableValuedParameter.
/// </summary>
/// <param name="list">Thhe list of records to convert to TVPs.</param>
/// <param name="list">The list of records to convert to TVPs.</param>
/// <param name="typeName">The sql parameter type name.</param>
public static ICustomQueryParameter AsTableValuedParameter(this IEnumerable<Microsoft.SqlServer.Server.SqlDataRecord> list, string typeName = null) =>
new SqlDataRecordListTVPParameter(list, typeName);
#endif

// one per thread
[ThreadStatic]
Expand Down

0 comments on commit e7ad087

Please sign in to comment.