Commit 881176c6 authored by mgravell's avatar mgravell

fix bug setting type name etc when configuring structured type parameters

parent c32096cf
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<GenerateDocumentationFile>false</GenerateDocumentationFile> <GenerateDocumentationFile>false</GenerateDocumentationFile>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<TargetFrameworks>net46;netcoreapp2.0;netcoreapp2.1</TargetFrameworks> <TargetFrameworks>netcoreapp2.1;net46;netcoreapp2.0</TargetFrameworks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors> <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
......
...@@ -18,7 +18,9 @@ ...@@ -18,7 +18,9 @@
namespace Dapper.Tests namespace Dapper.Tests
{ {
[Collection(NonParallelDefinition.Name)] // because it creates SQL types that compete between the two providers
public sealed class SystemSqlClientParameterTests : ParameterTests<SystemSqlClientProvider> { } public sealed class SystemSqlClientParameterTests : ParameterTests<SystemSqlClientProvider> { }
[Collection(NonParallelDefinition.Name)] // because it creates SQL types that compete between the two providers
public sealed class MicrosoftSqlClientParameterTests : ParameterTests<MicrosoftSqlClientProvider> { } public sealed class MicrosoftSqlClientParameterTests : ParameterTests<MicrosoftSqlClientProvider> { }
public abstract class ParameterTests<TProvider> : TestBase<TProvider> where TProvider : DatabaseProvider public abstract class ParameterTests<TProvider> : TestBase<TProvider> where TProvider : DatabaseProvider
{ {
......
using System.Collections.Generic; using System;
using System.Collections;
using System.Collections.Generic;
using System.Data; using System.Data;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
...@@ -38,15 +40,58 @@ void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string nam ...@@ -38,15 +40,58 @@ void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string nam
internal static void Set(IDbDataParameter parameter, IEnumerable<T> data, string typeName) internal static void Set(IDbDataParameter parameter, IEnumerable<T> data, string typeName)
{ {
parameter.Value = data != null && data.Any() ? data : null; parameter.Value = data != null && data.Any() ? data : null;
var type = parameter.GetType(); StructuredHelper.ConfigureStructured(parameter, typeName);
if (type.Name == "SqlParameter") }
}
static class StructuredHelper
{
private static readonly Hashtable s_perTypeHelpers = new Hashtable();
private static Action<IDbDataParameter, string> GetHelper(Type type)
=> (Action<IDbDataParameter, string>)s_perTypeHelpers[type] ?? SlowGetHelper(type);
static Action<IDbDataParameter, string> SlowGetHelper(Type type)
{
lock(s_perTypeHelpers)
{ {
var prop = type.GetProperty("SqlDbType"); var helper = (Action<IDbDataParameter, string>)s_perTypeHelpers[type];
prop?.SetValue(parameter, SqlDbType.Structured); if (helper == null)
{
prop = type.GetProperty("TypeName"); helper = CreateFor(type);
prop?.SetValue(parameter, typeName); s_perTypeHelpers.Add(type, helper);
}
return helper;
} }
} }
static Action<IDbDataParameter, string> CreateFor(Type type)
{
var name = type.GetProperty("TypeName", BindingFlags.Public | BindingFlags.Instance);
if (!name.CanWrite) name = null;
if (name == null) return (p, n) => { };
var dbType = type.GetProperty("SqlDbType", BindingFlags.Public | BindingFlags.Instance);
if (!dbType.CanWrite) dbType = null;
var dm = new DynamicMethod(nameof(CreateFor) + "_" + type.Name, null,
new[] { typeof(IDbDataParameter), typeof(string) }, true);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Castclass, type);
if (dbType != null) il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldarg_1);
il.EmitCall(OpCodes.Callvirt, name.GetSetMethod(), null);
if (dbType != null) il.Emit(OpCodes.Ldind_I4, (int)SqlDbType.Structured);
il.EmitCall(OpCodes.Callvirt, dbType.GetSetMethod(), null);
il.Emit(OpCodes.Ret);
return (Action<IDbDataParameter, string>)dm.CreateDelegate(typeof(Action<IDbDataParameter, string>));
}
// this needs to be done per-provider; "dynamic" doesn't work well on all runtimes, although that
// would be a fair option otherwise
internal static void ConfigureStructured(IDbDataParameter parameter, string typeName)
=> GetHelper(parameter.GetType())(parameter, typeName);
} }
} }
...@@ -30,17 +30,6 @@ public TableValuedParameter(DataTable table, string typeName) ...@@ -30,17 +30,6 @@ public TableValuedParameter(DataTable table, string typeName)
this.typeName = typeName; this.typeName = typeName;
} }
private static readonly Action<System.Data.SqlClient.SqlParameter, string> setTypeName;
static TableValuedParameter()
{
var prop = typeof(System.Data.SqlClient.SqlParameter).GetProperty("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) void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string name)
{ {
var param = command.CreateParameter(); var param = command.CreateParameter();
...@@ -58,12 +47,8 @@ internal static void Set(IDbDataParameter parameter, DataTable table, string typ ...@@ -58,12 +47,8 @@ internal static void Set(IDbDataParameter parameter, DataTable table, string typ
{ {
typeName = table.GetTypeName(); typeName = table.GetTypeName();
} }
if (!string.IsNullOrEmpty(typeName) && (parameter is System.Data.SqlClient.SqlParameter sqlParam)) if (!string.IsNullOrEmpty(typeName)) StructuredHelper.ConfigureStructured(parameter, typeName);
{
setTypeName?.Invoke(sqlParam, typeName);
sqlParam.SqlDbType = SqlDbType.Structured;
}
} }
} }
} }
#endif #endif
\ No newline at end of file
...@@ -33,11 +33,7 @@ void ITypeHandler.SetValue(IDbDataParameter parameter, object value) ...@@ -33,11 +33,7 @@ void ITypeHandler.SetValue(IDbDataParameter parameter, object value)
#pragma warning disable 0618 #pragma warning disable 0618
parameter.Value = SanitizeParameterValue(value); parameter.Value = SanitizeParameterValue(value);
#pragma warning restore 0618 #pragma warning restore 0618
if (parameter is System.Data.SqlClient.SqlParameter && !(value is DBNull)) if(!(value is DBNull)) StructuredHelper.ConfigureStructured(parameter, udtTypeName);
{
((System.Data.SqlClient.SqlParameter)parameter).SqlDbType = SqlDbType.Udt;
((System.Data.SqlClient.SqlParameter)parameter).UdtTypeName = udtTypeName;
}
} }
} }
#endif #endif
......
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