提交 e7ad0874 编写于 作者: S Sergii Volchkov 提交者: Nick Craver

Enable SqlDataRecord TVPs on corefx. (#801)

上级 0151dea8
......@@ -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;
......@@ -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);
......@@ -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);
......@@ -217,7 +213,6 @@ public void TestMassiveStrings()
.IsEqualTo(str);
}
#if !COREFX
[Fact]
public void TestTVPWithAnonymousObject()
{
......@@ -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);
......@@ -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()
{
......
......@@ -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" />
......
......@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Data;
#if !COREFX
namespace Dapper
{
internal sealed class SqlDataRecordHandler : SqlMapper.ITypeHandler
......@@ -18,4 +17,3 @@ public void SetValue(IDbDataParameter parameter, object value)
}
}
}
#endif
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
#if !COREFX
namespace Dapper
{
/// <summary>
......@@ -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();
......@@ -54,4 +41,3 @@ internal static void Set(IDbDataParameter parameter, IEnumerable<Microsoft.SqlSe
}
}
}
#endif
\ No newline at end of file
......@@ -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.
......@@ -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]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册