Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable SqlDataRecord TVPs on corefx. #801

Merged
merged 1 commit into from
Jun 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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