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 @@
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<TargetFrameworks>net46;netcoreapp2.0;netcoreapp2.1</TargetFrameworks>
<TargetFrameworks>netcoreapp2.1;net46;netcoreapp2.0</TargetFrameworks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
......
......@@ -18,7 +18,9 @@
namespace Dapper.Tests
{
[Collection(NonParallelDefinition.Name)] // because it creates SQL types that compete between the two providers
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 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.Linq;
using System.Reflection;
......@@ -38,15 +40,58 @@ void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string nam
internal static void Set(IDbDataParameter parameter, IEnumerable<T> data, string typeName)
{
parameter.Value = data != null && data.Any() ? data : null;
var type = parameter.GetType();
if (type.Name == "SqlParameter")
StructuredHelper.ConfigureStructured(parameter, typeName);
}
}
static class StructuredHelper
{
var prop = type.GetProperty("SqlDbType");
prop?.SetValue(parameter, SqlDbType.Structured);
prop = type.GetProperty("TypeName");
prop?.SetValue(parameter, typeName);
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 helper = (Action<IDbDataParameter, string>)s_perTypeHelpers[type];
if (helper == null)
{
helper = CreateFor(type);
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)
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)
{
var param = command.CreateParameter();
......@@ -58,11 +47,7 @@ internal static void Set(IDbDataParameter parameter, DataTable table, string typ
{
typeName = table.GetTypeName();
}
if (!string.IsNullOrEmpty(typeName) && (parameter is System.Data.SqlClient.SqlParameter sqlParam))
{
setTypeName?.Invoke(sqlParam, typeName);
sqlParam.SqlDbType = SqlDbType.Structured;
}
if (!string.IsNullOrEmpty(typeName)) StructuredHelper.ConfigureStructured(parameter, typeName);
}
}
}
......
......@@ -33,11 +33,7 @@ void ITypeHandler.SetValue(IDbDataParameter parameter, object value)
#pragma warning disable 0618
parameter.Value = SanitizeParameterValue(value);
#pragma warning restore 0618
if (parameter is System.Data.SqlClient.SqlParameter && !(value is DBNull))
{
((System.Data.SqlClient.SqlParameter)parameter).SqlDbType = SqlDbType.Udt;
((System.Data.SqlClient.SqlParameter)parameter).UdtTypeName = udtTypeName;
}
if(!(value is DBNull)) StructuredHelper.ConfigureStructured(parameter, udtTypeName);
}
}
#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