Commit ac7b9f5a authored by Nick Craver's avatar Nick Craver

Merge branch 'netstandard2' of https://github.com/StackExchange/Dapper into netstandard2

parents ba641f18 a9560b31
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' "> <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.Collections.Concurrent" Version="4.3.0" /> <PackageReference Include="System.Collections.Concurrent" Version="4.3.0" />
<PackageReference Include="System.Collections.NonGeneric" 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.Dynamic.Runtime" Version="4.3.0" />
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" /> <PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" /> <PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" />
...@@ -31,7 +31,8 @@ ...@@ -31,7 +31,8 @@
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" /> <PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' "> <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.Data.SqlClient" Version="4.4.0-preview1-25305-02" /> <PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.4.0-preview2-25405-01" />
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" /> <PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" /> <PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" />
</ItemGroup> </ItemGroup>
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
<PackageReference Include="Microsoft.Data.Sqlite" Version="1.1.0" /> <PackageReference Include="Microsoft.Data.Sqlite" Version="1.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="MySql.Data" Version="7.0.7-m61" /> <PackageReference Include="MySql.Data" Version="7.0.7-m61" />
<PackageReference Include="System.Data.SqlClient" Version="4.4.0-preview1-25305-02" /> <PackageReference Include="System.Data.SqlClient" Version="4.4.0-preview2-25405-01" />
<PackageReference Include="xunit" Version="2.3.0-beta1-build3642" /> <PackageReference Include="xunit" Version="2.3.0-beta1-build3642" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta1-build1309" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta1-build1309" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.0-beta1-build3642" /> <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.0-beta1-build3642" />
......
...@@ -5,10 +5,8 @@ ...@@ -5,10 +5,8 @@
using Dapper.Contrib.Extensions; using Dapper.Contrib.Extensions;
#if !NETCOREAPP1_0
using System.Transactions;
#endif
#if !NETCOREAPP1_0 && !NETCOREAPP2_0 #if !NETCOREAPP1_0 && !NETCOREAPP2_0
using System.Transactions;
using System.Data.SqlServerCe; using System.Data.SqlServerCe;
#endif #endif
using FactAttribute = Dapper.Tests.Contrib.SkippableFactAttribute; using FactAttribute = Dapper.Tests.Contrib.SkippableFactAttribute;
...@@ -527,7 +525,7 @@ public void Transactions() ...@@ -527,7 +525,7 @@ public void Transactions()
} }
} }
#if !NETCOREAPP1_0 #if !NETCOREAPP1_0 && !NETCOREAPP2_0
[Fact] [Fact]
public void TransactionScope() public void TransactionScope()
{ {
......
...@@ -353,28 +353,35 @@ public void RunSequentialVersusParallelSync() ...@@ -353,28 +353,35 @@ public void RunSequentialVersusParallelSync()
Console.WriteLine("Pipeline: {0}ms", watch.ElapsedMilliseconds); Console.WriteLine("Pipeline: {0}ms", watch.ElapsedMilliseconds);
} }
[Fact] [Collection("QueryCacheTests")]
public void AssertNoCacheWorksForQueryMultiple() public class AsyncQueryCacheTests : TestBase
{ {
const int a = 123, b = 456; private SqlConnection _marsConnection;
var cmdDef = new CommandDefinition(@"select @a; select @b;", new private SqlConnection MarsConnection => _marsConnection ?? (_marsConnection = GetOpenConnection(true));
{
a, b
}, commandType: CommandType.Text, flags: CommandFlags.NoCache);
int c, d; [Fact]
SqlMapper.PurgeQueryCache(); public void AssertNoCacheWorksForQueryMultiple()
int before = SqlMapper.GetCachedSQLCount();
using (var multi = MarsConnection.QueryMultiple(cmdDef))
{ {
c = multi.Read<int>().Single(); const int a = 123, b = 456;
d = multi.Read<int>().Single(); var cmdDef = new CommandDefinition(@"select @a; select @b;", new
{
a, b
}, commandType: CommandType.Text, flags: CommandFlags.NoCache);
int c, d;
SqlMapper.PurgeQueryCache();
int before = SqlMapper.GetCachedSQLCount();
using (var multi = MarsConnection.QueryMultiple(cmdDef))
{
c = multi.Read<int>().Single();
d = multi.Read<int>().Single();
}
int after = SqlMapper.GetCachedSQLCount();
before.IsEqualTo(0);
after.IsEqualTo(0);
c.IsEqualTo(123);
d.IsEqualTo(456);
} }
int after = SqlMapper.GetCachedSQLCount();
before.IsEqualTo(0);
after.IsEqualTo(0);
c.IsEqualTo(123);
d.IsEqualTo(456);
} }
private class BasicType private class BasicType
......
...@@ -104,85 +104,6 @@ public bool GetWentThroughProperConstructor() ...@@ -104,85 +104,6 @@ public bool GetWentThroughProperConstructor()
} }
} }
[Fact]
public void Issue461_TypeHandlerWorksInConstructor()
{
SqlMapper.AddTypeHandler(new Issue461_BlargHandler());
connection.Execute(@"CREATE TABLE #Issue461 (
Id int not null IDENTITY(1,1),
SomeValue nvarchar(50),
SomeBlargValue nvarchar(200),
)");
const string Expected = "abc123def";
var blarg = new Blarg(Expected);
connection.Execute(
"INSERT INTO #Issue461 (SomeValue, SomeBlargValue) VALUES (@value, @blarg)",
new { value = "what up?", blarg });
// test: without constructor
var parameterlessWorks = connection.QuerySingle<Issue461_ParameterlessTypeConstructor>("SELECT * FROM #Issue461");
parameterlessWorks.Id.IsEqualTo(1);
parameterlessWorks.SomeValue.IsEqualTo("what up?");
parameterlessWorks.SomeBlargValue.Value.IsEqualTo(Expected);
// test: via constructor
var parameterDoesNot = connection.QuerySingle<Issue461_ParameterisedTypeConstructor>("SELECT * FROM #Issue461");
parameterDoesNot.Id.IsEqualTo(1);
parameterDoesNot.SomeValue.IsEqualTo("what up?");
parameterDoesNot.SomeBlargValue.Value.IsEqualTo(Expected);
}
// I would usually expect this to be a struct; using a class
// so that we can't pass unexpectedly due to forcing an unsafe cast - want
// to see an InvalidCastException if it is wrong
private class Blarg
{
public Blarg(string value) { Value = value; }
public string Value { get; }
public override string ToString()
{
return Value;
}
}
private class Issue461_BlargHandler : SqlMapper.TypeHandler<Blarg>
{
public override void SetValue(IDbDataParameter parameter, Blarg value)
{
parameter.Value = ((object)value.Value) ?? DBNull.Value;
}
public override Blarg Parse(object value)
{
string s = (value == null || value is DBNull) ? null : Convert.ToString(value);
return new Blarg(s);
}
}
private class Issue461_ParameterlessTypeConstructor
{
public int Id { get; set; }
public string SomeValue { get; set; }
public Blarg SomeBlargValue { get; set; }
}
private class Issue461_ParameterisedTypeConstructor
{
public Issue461_ParameterisedTypeConstructor(int id, string someValue, Blarg someBlargValue)
{
Id = id;
SomeValue = someValue;
SomeBlargValue = someBlargValue;
}
public int Id { get; }
public string SomeValue { get; }
public Blarg SomeBlargValue { get; }
}
public static class AbstractInheritance public static class AbstractInheritance
{ {
public abstract class Order public abstract class Order
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
<PackageReference Include="MySql.Data" Version="7.0.7-m61" /> <PackageReference Include="MySql.Data" Version="7.0.7-m61" />
<PackageReference Include="Npgsql" Version="3.2.2" /> <PackageReference Include="Npgsql" Version="3.2.2" />
<PackageReference Include="System.Data.SqlClient" Version="4.4.0-preview1-25305-02" /> <PackageReference Include="System.Data.SqlClient" Version="4.4.0-preview2-25405-01" />
<PackageReference Include="System.ValueTuple" Version="4.3.0" /> <PackageReference Include="System.ValueTuple" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.3.0-beta1-build3642" /> <PackageReference Include="xunit" Version="2.3.0-beta1-build3642" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta1-build1309" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta1-build1309" />
......
...@@ -6,36 +6,40 @@ namespace Dapper.Tests ...@@ -6,36 +6,40 @@ namespace Dapper.Tests
{ {
public partial class DataReaderTests : TestBase public partial class DataReaderTests : TestBase
{ {
[Fact] [Collection("QueryCacheTests")]
public void GetSameReaderForSameShape() public class DataReaderQueryCacheTests : TestBase
{ {
var origReader = connection.ExecuteReader("select 'abc' as Name, 123 as Id"); [Fact]
var origParser = origReader.GetRowParser(typeof(HazNameId)); public void GetSameReaderForSameShape()
{
var origReader = connection.ExecuteReader("select 'abc' as Name, 123 as Id");
var origParser = origReader.GetRowParser(typeof(HazNameId));
var typedParser = origReader.GetRowParser<HazNameId>(); var typedParser = origReader.GetRowParser<HazNameId>();
ReferenceEquals(origParser, typedParser).IsEqualTo(true); ReferenceEquals(origParser, typedParser).IsEqualTo(true);
var list = origReader.Parse<HazNameId>().ToList(); var list = origReader.Parse<HazNameId>().ToList();
list.Count.IsEqualTo(1); list.Count.IsEqualTo(1);
list[0].Name.IsEqualTo("abc"); list[0].Name.IsEqualTo("abc");
list[0].Id.IsEqualTo(123); list[0].Id.IsEqualTo(123);
origReader.Dispose(); origReader.Dispose();
var secondReader = connection.ExecuteReader("select 'abc' as Name, 123 as Id"); var secondReader = connection.ExecuteReader("select 'abc' as Name, 123 as Id");
var secondParser = secondReader.GetRowParser(typeof(HazNameId)); var secondParser = secondReader.GetRowParser(typeof(HazNameId));
var thirdParser = secondReader.GetRowParser(typeof(HazNameId), 1); var thirdParser = secondReader.GetRowParser(typeof(HazNameId), 1);
list = secondReader.Parse<HazNameId>().ToList(); list = secondReader.Parse<HazNameId>().ToList();
list.Count.IsEqualTo(1); list.Count.IsEqualTo(1);
list[0].Name.IsEqualTo("abc"); list[0].Name.IsEqualTo("abc");
list[0].Id.IsEqualTo(123); list[0].Id.IsEqualTo(123);
secondReader.Dispose(); secondReader.Dispose();
// now: should be different readers, but same parser // now: should be different readers, but same parser
ReferenceEquals(origReader, secondReader).IsEqualTo(false); ReferenceEquals(origReader, secondReader).IsEqualTo(false);
ReferenceEquals(origParser, secondParser).IsEqualTo(true); ReferenceEquals(origParser, secondParser).IsEqualTo(true);
ReferenceEquals(secondParser, thirdParser).IsEqualTo(false); ReferenceEquals(secondParser, thirdParser).IsEqualTo(false);
}
} }
[Fact] [Fact]
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using System.Linq; using System.Linq;
namespace Dapper.Tests namespace Dapper.Tests
{ {
[Collection("QueryCacheTests")]
public class NullTests : TestBase public class NullTests : TestBase
{ {
[Fact] [Fact]
......
...@@ -38,6 +38,24 @@ void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Id ...@@ -38,6 +38,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 class IntDynamicParam : SqlMapper.IDynamicParameters
{ {
private readonly IEnumerable<int> numbers; private readonly IEnumerable<int> numbers;
...@@ -51,19 +69,8 @@ public void AddParameters(IDbCommand command, SqlMapper.Identity identity) ...@@ -51,19 +69,8 @@ public void AddParameters(IDbCommand command, SqlMapper.Identity identity)
var sqlCommand = (SqlCommand)command; var sqlCommand = (SqlCommand)command;
sqlCommand.CommandType = CommandType.StoredProcedure; sqlCommand.CommandType = CommandType.StoredProcedure;
var number_list = new List<Microsoft.SqlServer.Server.SqlDataRecord>(); var number_list = CreateSqlDataRecordList(numbers);
// 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.
}
// Add the table parameter. // Add the table parameter.
var p = sqlCommand.Parameters.Add("ints", SqlDbType.Structured); var p = sqlCommand.Parameters.Add("ints", SqlDbType.Structured);
p.Direction = ParameterDirection.Input; p.Direction = ParameterDirection.Input;
...@@ -85,19 +92,8 @@ public void AddParameter(IDbCommand command, string name) ...@@ -85,19 +92,8 @@ public void AddParameter(IDbCommand command, string name)
var sqlCommand = (SqlCommand)command; var sqlCommand = (SqlCommand)command;
sqlCommand.CommandType = CommandType.StoredProcedure; sqlCommand.CommandType = CommandType.StoredProcedure;
var number_list = new List<Microsoft.SqlServer.Server.SqlDataRecord>(); var number_list = CreateSqlDataRecordList(numbers);
// 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.
}
// Add the table parameter. // Add the table parameter.
var p = sqlCommand.Parameters.Add(name, SqlDbType.Structured); var p = sqlCommand.Parameters.Add(name, SqlDbType.Structured);
p.Direction = ParameterDirection.Input; p.Direction = ParameterDirection.Input;
...@@ -218,7 +214,6 @@ public void TestMassiveStrings() ...@@ -218,7 +214,6 @@ public void TestMassiveStrings()
.IsEqualTo(str); .IsEqualTo(str);
} }
#if !NETCOREAPP1_0
[Fact] [Fact]
public void TestTVPWithAnonymousObject() public void TestTVPWithAnonymousObject()
{ {
...@@ -289,19 +284,8 @@ public new void AddParameters(IDbCommand command, SqlMapper.Identity identity) ...@@ -289,19 +284,8 @@ public new void AddParameters(IDbCommand command, SqlMapper.Identity identity)
var sqlCommand = (SqlCommand)command; var sqlCommand = (SqlCommand)command;
sqlCommand.CommandType = CommandType.StoredProcedure; sqlCommand.CommandType = CommandType.StoredProcedure;
var number_list = new List<Microsoft.SqlServer.Server.SqlDataRecord>(); var number_list = CreateSqlDataRecordList(numbers);
// 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.
}
// Add the table parameter. // Add the table parameter.
var p = sqlCommand.Parameters.Add("ints", SqlDbType.Structured); var p = sqlCommand.Parameters.Add("ints", SqlDbType.Structured);
p.Direction = ParameterDirection.Input; p.Direction = ParameterDirection.Input;
...@@ -344,6 +328,83 @@ public void TestTVPWithAdditionalParams() ...@@ -344,6 +328,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 !NETCOREAPP1_0
[Fact] [Fact]
public void DataTableParameters() public void DataTableParameters()
{ {
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
namespace Dapper.Tests.Providers namespace Dapper.Tests.Providers
{ {
[Collection("TypeHandlerTests")]
public class EntityFrameworkTests : TestBase public class EntityFrameworkTests : TestBase
{ {
public EntityFrameworkTests() public EntityFrameworkTests()
......
...@@ -24,41 +24,45 @@ public void DapperEnumValue_Sqlite() ...@@ -24,41 +24,45 @@ public void DapperEnumValue_Sqlite()
} }
} }
[FactSqlite] [Collection("TypeHandlerTests")]
public void Issue466_SqliteHatesOptimizations() public class SqliteTypeHandlerTests : TestBase
{ {
using (var connection = GetSQLiteConnection()) [FactSqlite]
public void Issue466_SqliteHatesOptimizations()
{ {
SqlMapper.ResetTypeHandlers(); using (var connection = GetSQLiteConnection())
var row = connection.Query<HazNameId>("select 42 as Id").First(); {
row.Id.IsEqualTo(42); SqlMapper.ResetTypeHandlers();
row = connection.Query<HazNameId>("select 42 as Id").First(); var row = connection.Query<HazNameId>("select 42 as Id").First();
row.Id.IsEqualTo(42); row.Id.IsEqualTo(42);
row = connection.Query<HazNameId>("select 42 as Id").First();
row.Id.IsEqualTo(42);
SqlMapper.ResetTypeHandlers(); SqlMapper.ResetTypeHandlers();
row = connection.QueryFirst<HazNameId>("select 42 as Id"); row = connection.QueryFirst<HazNameId>("select 42 as Id");
row.Id.IsEqualTo(42); row.Id.IsEqualTo(42);
row = connection.QueryFirst<HazNameId>("select 42 as Id"); row = connection.QueryFirst<HazNameId>("select 42 as Id");
row.Id.IsEqualTo(42); row.Id.IsEqualTo(42);
}
} }
}
[FactSqlite] [FactSqlite]
public async Task Issue466_SqliteHatesOptimizations_Async() public async Task Issue466_SqliteHatesOptimizations_Async()
{
using (var connection = GetSQLiteConnection())
{ {
SqlMapper.ResetTypeHandlers(); using (var connection = GetSQLiteConnection())
var row = (await connection.QueryAsync<HazNameId>("select 42 as Id").ConfigureAwait(false)).First(); {
row.Id.IsEqualTo(42); SqlMapper.ResetTypeHandlers();
row = (await connection.QueryAsync<HazNameId>("select 42 as Id").ConfigureAwait(false)).First(); var row = (await connection.QueryAsync<HazNameId>("select 42 as Id").ConfigureAwait(false)).First();
row.Id.IsEqualTo(42); row.Id.IsEqualTo(42);
row = (await connection.QueryAsync<HazNameId>("select 42 as Id").ConfigureAwait(false)).First();
row.Id.IsEqualTo(42);
SqlMapper.ResetTypeHandlers(); SqlMapper.ResetTypeHandlers();
row = await connection.QueryFirstAsync<HazNameId>("select 42 as Id").ConfigureAwait(false); row = await connection.QueryFirstAsync<HazNameId>("select 42 as Id").ConfigureAwait(false);
row.Id.IsEqualTo(42); row.Id.IsEqualTo(42);
row = await connection.QueryFirstAsync<HazNameId>("select 42 as Id").ConfigureAwait(false); row = await connection.QueryFirstAsync<HazNameId>("select 42 as Id").ConfigureAwait(false);
row.Id.IsEqualTo(42); row.Id.IsEqualTo(42);
}
} }
} }
......
...@@ -8,46 +8,96 @@ ...@@ -8,46 +8,96 @@
namespace Dapper.Tests namespace Dapper.Tests
{ {
[Collection("TypeHandlerTests")]
public class TypeHandlerTests : TestBase public class TypeHandlerTests : TestBase
{ {
[Fact] [Collection("QueryCacheTests")]
public void TestChangingDefaultStringTypeMappingToAnsiString() public class TypeHandlerQueryCacheTests : TestBase
{ {
const string sql = "SELECT SQL_VARIANT_PROPERTY(CONVERT(sql_variant, @testParam),'BaseType') AS BaseType"; [Fact]
var param = new { testParam = "TestString" }; public void TestChangingDefaultStringTypeMappingToAnsiString()
{
const string sql = "SELECT SQL_VARIANT_PROPERTY(CONVERT(sql_variant, @testParam),'BaseType') AS BaseType";
var param = new { testParam = "TestString" };
var result01 = connection.Query<string>(sql, param).FirstOrDefault(); var result01 = connection.Query<string>(sql, param).FirstOrDefault();
result01.IsEqualTo("nvarchar"); result01.IsEqualTo("nvarchar");
SqlMapper.PurgeQueryCache(); SqlMapper.PurgeQueryCache();
SqlMapper.AddTypeMap(typeof(string), DbType.AnsiString); // Change Default String Handling to AnsiString SqlMapper.AddTypeMap(typeof(string), DbType.AnsiString); // Change Default String Handling to AnsiString
var result02 = connection.Query<string>(sql, param).FirstOrDefault(); var result02 = connection.Query<string>(sql, param).FirstOrDefault();
result02.IsEqualTo("varchar"); result02.IsEqualTo("varchar");
SqlMapper.PurgeQueryCache(); SqlMapper.PurgeQueryCache();
SqlMapper.AddTypeMap(typeof(string), DbType.String); // Restore Default to Unicode String SqlMapper.AddTypeMap(typeof(string), DbType.String); // Restore Default to Unicode String
} }
[Fact] [Fact]
public void TestChangingDefaultStringTypeMappingToAnsiStringFirstOrDefault() public void TestChangingDefaultStringTypeMappingToAnsiStringFirstOrDefault()
{ {
const string sql = "SELECT SQL_VARIANT_PROPERTY(CONVERT(sql_variant, @testParam),'BaseType') AS BaseType"; const string sql = "SELECT SQL_VARIANT_PROPERTY(CONVERT(sql_variant, @testParam),'BaseType') AS BaseType";
var param = new { testParam = "TestString" }; var param = new { testParam = "TestString" };
var result01 = connection.QueryFirstOrDefault<string>(sql, param); var result01 = connection.QueryFirstOrDefault<string>(sql, param);
result01.IsEqualTo("nvarchar"); result01.IsEqualTo("nvarchar");
SqlMapper.PurgeQueryCache(); SqlMapper.PurgeQueryCache();
SqlMapper.AddTypeMap(typeof(string), DbType.AnsiString); // Change Default String Handling to AnsiString SqlMapper.AddTypeMap(typeof(string), DbType.AnsiString); // Change Default String Handling to AnsiString
var result02 = connection.QueryFirstOrDefault<string>(sql, param); var result02 = connection.QueryFirstOrDefault<string>(sql, param);
result02.IsEqualTo("varchar"); result02.IsEqualTo("varchar");
SqlMapper.PurgeQueryCache(); SqlMapper.PurgeQueryCache();
SqlMapper.AddTypeMap(typeof(string), DbType.String); // Restore Default to Unicode String SqlMapper.AddTypeMap(typeof(string), DbType.String); // Restore Default to Unicode String
} }
[Fact]
public void TestCustomTypeMap()
{
// default mapping
var item = connection.Query<TypeWithMapping>("Select 'AVal' as A, 'BVal' as B").Single();
item.A.IsEqualTo("AVal");
item.B.IsEqualTo("BVal");
// custom mapping
var map = new CustomPropertyTypeMap(typeof(TypeWithMapping),
(type, columnName) => type.GetProperties().FirstOrDefault(prop => GetDescriptionFromAttribute(prop) == columnName));
SqlMapper.SetTypeMap(typeof(TypeWithMapping), map);
item = connection.Query<TypeWithMapping>("Select 'AVal' as A, 'BVal' as B").Single();
item.A.IsEqualTo("BVal");
item.B.IsEqualTo("AVal");
// reset to default
SqlMapper.SetTypeMap(typeof(TypeWithMapping), null);
item = connection.Query<TypeWithMapping>("Select 'AVal' as A, 'BVal' as B").Single();
item.A.IsEqualTo("AVal");
item.B.IsEqualTo("BVal");
}
private static string GetDescriptionFromAttribute(MemberInfo member)
{
if (member == null) return null;
#if NETCOREAPP1_0
var data = member.CustomAttributes.FirstOrDefault(x => x.AttributeType == typeof(DescriptionAttribute));
return (string)data?.ConstructorArguments.Single().Value;
#else
var attrib = (DescriptionAttribute)Attribute.GetCustomAttribute(member, typeof(DescriptionAttribute), false);
return attrib?.Description;
#endif
}
public class TypeWithMapping
{
[Description("B")]
public string A { get; set; }
[Description("A")]
public string B { get; set; }
}
}
[Fact] [Fact]
public void Issue136_ValueTypeHandlers() public void Issue136_ValueTypeHandlers()
{ {
...@@ -518,51 +568,6 @@ private class ResultsChangeType ...@@ -518,51 +568,6 @@ private class ResultsChangeType
public int Z { get; set; } public int Z { get; set; }
} }
[Fact]
public void TestCustomTypeMap()
{
// default mapping
var item = connection.Query<TypeWithMapping>("Select 'AVal' as A, 'BVal' as B").Single();
item.A.IsEqualTo("AVal");
item.B.IsEqualTo("BVal");
// custom mapping
var map = new CustomPropertyTypeMap(typeof(TypeWithMapping),
(type, columnName) => type.GetProperties().FirstOrDefault(prop => GetDescriptionFromAttribute(prop) == columnName));
SqlMapper.SetTypeMap(typeof(TypeWithMapping), map);
item = connection.Query<TypeWithMapping>("Select 'AVal' as A, 'BVal' as B").Single();
item.A.IsEqualTo("BVal");
item.B.IsEqualTo("AVal");
// reset to default
SqlMapper.SetTypeMap(typeof(TypeWithMapping), null);
item = connection.Query<TypeWithMapping>("Select 'AVal' as A, 'BVal' as B").Single();
item.A.IsEqualTo("AVal");
item.B.IsEqualTo("BVal");
}
private static string GetDescriptionFromAttribute(MemberInfo member)
{
if (member == null) return null;
#if NETCOREAPP1_0
var data = member.CustomAttributes.FirstOrDefault(x => x.AttributeType == typeof(DescriptionAttribute));
return (string)data?.ConstructorArguments.Single().Value;
#else
var attrib = (DescriptionAttribute)Attribute.GetCustomAttribute(member, typeof(DescriptionAttribute), false);
return attrib?.Description;
#endif
}
public class TypeWithMapping
{
[Description("B")]
public string A { get; set; }
[Description("A")]
public string B { get; set; }
}
public class WrongTypes public class WrongTypes
{ {
public int A { get; set; } public int A { get; set; }
...@@ -658,5 +663,84 @@ public void SO29343103_UtcDates() ...@@ -658,5 +663,84 @@ public void SO29343103_UtcDates()
var delta = returned - date; var delta = returned - date;
Assert.IsTrue(delta.TotalMilliseconds >= -10 && delta.TotalMilliseconds <= 10); Assert.IsTrue(delta.TotalMilliseconds >= -10 && delta.TotalMilliseconds <= 10);
} }
[Fact]
public void Issue461_TypeHandlerWorksInConstructor()
{
SqlMapper.AddTypeHandler(new Issue461_BlargHandler());
connection.Execute(@"CREATE TABLE #Issue461 (
Id int not null IDENTITY(1,1),
SomeValue nvarchar(50),
SomeBlargValue nvarchar(200),
)");
const string Expected = "abc123def";
var blarg = new Blarg(Expected);
connection.Execute(
"INSERT INTO #Issue461 (SomeValue, SomeBlargValue) VALUES (@value, @blarg)",
new { value = "what up?", blarg });
// test: without constructor
var parameterlessWorks = connection.QuerySingle<Issue461_ParameterlessTypeConstructor>("SELECT * FROM #Issue461");
parameterlessWorks.Id.IsEqualTo(1);
parameterlessWorks.SomeValue.IsEqualTo("what up?");
parameterlessWorks.SomeBlargValue.Value.IsEqualTo(Expected);
// test: via constructor
var parameterDoesNot = connection.QuerySingle<Issue461_ParameterisedTypeConstructor>("SELECT * FROM #Issue461");
parameterDoesNot.Id.IsEqualTo(1);
parameterDoesNot.SomeValue.IsEqualTo("what up?");
parameterDoesNot.SomeBlargValue.Value.IsEqualTo(Expected);
}
// I would usually expect this to be a struct; using a class
// so that we can't pass unexpectedly due to forcing an unsafe cast - want
// to see an InvalidCastException if it is wrong
private class Blarg
{
public Blarg(string value) { Value = value; }
public string Value { get; }
public override string ToString()
{
return Value;
}
}
private class Issue461_BlargHandler : SqlMapper.TypeHandler<Blarg>
{
public override void SetValue(IDbDataParameter parameter, Blarg value)
{
parameter.Value = ((object)value.Value) ?? DBNull.Value;
}
public override Blarg Parse(object value)
{
string s = (value == null || value is DBNull) ? null : Convert.ToString(value);
return new Blarg(s);
}
}
private class Issue461_ParameterlessTypeConstructor
{
public int Id { get; set; }
public string SomeValue { get; set; }
public Blarg SomeBlargValue { get; set; }
}
private class Issue461_ParameterisedTypeConstructor
{
public Issue461_ParameterisedTypeConstructor(int id, string someValue, Blarg someBlargValue)
{
Id = id;
SomeValue = someValue;
SomeBlargValue = someBlargValue;
}
public int Id { get; }
public string SomeValue { get; }
public Blarg SomeBlargValue { get; }
}
} }
} }
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' "> <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.Collections.Concurrent" Version="4.3.0" /> <PackageReference Include="System.Collections.Concurrent" Version="4.3.0" />
<PackageReference Include="System.Collections.NonGeneric" 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.Dynamic.Runtime" Version="4.3.0" />
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" /> <PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" /> <PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" />
...@@ -26,7 +26,8 @@ ...@@ -26,7 +26,8 @@
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" /> <PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' "> <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="System.Data.SqlClient" Version="4.4.0-preview1-25305-02" /> <PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.4.0-preview2-25405-01" />
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" /> <PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" /> <PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.3.0" />
</ItemGroup> </ItemGroup>
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
#if !NETSTANDARD1_3
namespace Dapper namespace Dapper
{ {
internal sealed class SqlDataRecordHandler : SqlMapper.ITypeHandler internal sealed class SqlDataRecordHandler : SqlMapper.ITypeHandler
...@@ -18,4 +17,3 @@ public void SetValue(IDbDataParameter parameter, object value) ...@@ -18,4 +17,3 @@ public void SetValue(IDbDataParameter parameter, object value)
} }
} }
} }
#endif
\ No newline at end of file
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Reflection;
#if !NETSTANDARD1_3
namespace Dapper namespace Dapper
{ {
/// <summary> /// <summary>
...@@ -23,18 +22,6 @@ public SqlDataRecordListTVPParameter(IEnumerable<Microsoft.SqlServer.Server.SqlD ...@@ -23,18 +22,6 @@ public SqlDataRecordListTVPParameter(IEnumerable<Microsoft.SqlServer.Server.SqlD
this.typeName = typeName; 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) void SqlMapper.ICustomQueryParameter.AddParameter(IDbCommand command, string name)
{ {
var param = command.CreateParameter(); var param = command.CreateParameter();
...@@ -54,4 +41,3 @@ internal static void Set(IDbDataParameter parameter, IEnumerable<Microsoft.SqlSe ...@@ -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) ...@@ -219,24 +219,22 @@ private static void ResetTypeHandlers(bool clone)
typeHandlers = new Dictionary<Type, ITypeHandler>(); typeHandlers = new Dictionary<Type, ITypeHandler>();
#if !NETSTANDARD1_3 #if !NETSTANDARD1_3
AddTypeHandlerImpl(typeof(DataTable), new DataTableHandler(), clone); AddTypeHandlerImpl(typeof(DataTable), new DataTableHandler(), clone);
#endif
try try
{ {
AddSqlDataRecordsTypeHandler(clone); AddSqlDataRecordsTypeHandler(clone);
} }
catch { /* https://github.com/StackExchange/dapper-dot-net/issues/424 */ } catch { /* https://github.com/StackExchange/dapper-dot-net/issues/424 */ }
#endif
AddTypeHandlerImpl(typeof(XmlDocument), new XmlDocumentHandler(), clone); AddTypeHandlerImpl(typeof(XmlDocument), new XmlDocumentHandler(), clone);
AddTypeHandlerImpl(typeof(XDocument), new XDocumentHandler(), clone); AddTypeHandlerImpl(typeof(XDocument), new XDocumentHandler(), clone);
AddTypeHandlerImpl(typeof(XElement), new XElementHandler(), clone); AddTypeHandlerImpl(typeof(XElement), new XElementHandler(), clone);
} }
#if !NETSTANDARD1_3
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
private static void AddSqlDataRecordsTypeHandler(bool clone) private static void AddSqlDataRecordsTypeHandler(bool clone)
{ {
AddTypeHandlerImpl(typeof(IEnumerable<Microsoft.SqlServer.Server.SqlDataRecord>), new SqlDataRecordHandler(), clone); AddTypeHandlerImpl(typeof(IEnumerable<Microsoft.SqlServer.Server.SqlDataRecord>), new SqlDataRecordHandler(), clone);
} }
#endif
/// <summary> /// <summary>
/// Configure the specified type to be mapped to a given db-type. /// 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) ...@@ -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> /// <param name="table">The <see cref="DataTable"/> that has a type name associated with it.</param>
public static string GetTypeName(this DataTable table) => public static string GetTypeName(this DataTable table) =>
table?.ExtendedProperties[DataTableTypeNameKey] as string; table?.ExtendedProperties[DataTableTypeNameKey] as string;
#endif
/// <summary> /// <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> /// </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> /// <param name="typeName">The sql parameter type name.</param>
public static ICustomQueryParameter AsTableValuedParameter(this IEnumerable<Microsoft.SqlServer.Server.SqlDataRecord> list, string typeName = null) => public static ICustomQueryParameter AsTableValuedParameter(this IEnumerable<Microsoft.SqlServer.Server.SqlDataRecord> list, string typeName = null) =>
new SqlDataRecordListTVPParameter(list, typeName); new SqlDataRecordListTVPParameter(list, typeName);
#endif
// one per thread // one per thread
[ThreadStatic] [ThreadStatic]
......
image: Visual Studio 2017 image: Visual Studio 2017 Preview
skip_branch_with_pr: true skip_branch_with_pr: true
skip_tags: true skip_tags: true
...@@ -52,4 +52,4 @@ deploy: ...@@ -52,4 +52,4 @@ deploy:
branch: master branch: master
api_key: api_key:
secure: P/UHxq2DEs0GI1SoDXDesHjRVsSVgdywz5vmsnhFQQY5aJgO3kP+QfhwfhXz19Rw secure: P/UHxq2DEs0GI1SoDXDesHjRVsSVgdywz5vmsnhFQQY5aJgO3kP+QfhwfhXz19Rw
symbol_server: https://www.myget.org/F/stackoverflow/symbols/api/v2/package symbol_server: https://www.myget.org/F/stackoverflow/symbols/api/v2/package
\ No newline at end of file
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