mirror of
https://github.com/DapperLib/Dapper.git
synced 2026-10-17 05:12:13 +08:00
Enable SqlDataRecord TVPs on netstandard1.3. (#802)
* Enable SqlDataRecord TVPs on netstandard1.3. * Update netstandard2.0 SqlClient references to Preview 2. * Skip TransactionScope tests for netcoreapp2.0. - According to https://github.com/dotnet/corefx/issues/12534, ambient transaction enlistment is not included with .net core 2.0. * Update references in Dapper.StrongName. * Disable parallel test run to stabilize flaky type handler tests. * Disable parallel test run by assembly attribute. * Move Issue461 tests to TypeHandlerTests. * Group type handler tests in a collection, re-enable parallelization. - Being part of the same collection, type handler tests will run sequentially. All other tests can run in parallel. * Move tests relying on query cache & type maps to a separate collection.
This commit is contained in:
committed by
Nick Craver
parent
910246bf24
commit
a9560b3193
@@ -22,7 +22,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" />
|
||||
@@ -31,7 +31,8 @@
|
||||
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
<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.Lightweight" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
|
||||
<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.runner.visualstudio" Version="2.3.0-beta1-build1309" />
|
||||
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.0-beta1-build3642" />
|
||||
|
||||
@@ -5,10 +5,8 @@ using System.Linq;
|
||||
|
||||
using Dapper.Contrib.Extensions;
|
||||
|
||||
#if !NETCOREAPP1_0
|
||||
using System.Transactions;
|
||||
#endif
|
||||
#if !NETCOREAPP1_0 && !NETCOREAPP2_0
|
||||
using System.Transactions;
|
||||
using System.Data.SqlServerCe;
|
||||
#endif
|
||||
using FactAttribute = Dapper.Tests.Contrib.SkippableFactAttribute;
|
||||
@@ -527,7 +525,7 @@ namespace Dapper.Tests.Contrib
|
||||
}
|
||||
}
|
||||
|
||||
#if !NETCOREAPP1_0
|
||||
#if !NETCOREAPP1_0 && !NETCOREAPP2_0
|
||||
[Fact]
|
||||
public void TransactionScope()
|
||||
{
|
||||
|
||||
+25
-18
@@ -353,28 +353,35 @@ namespace Dapper.Tests
|
||||
Console.WriteLine("Pipeline: {0}ms", watch.ElapsedMilliseconds);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AssertNoCacheWorksForQueryMultiple()
|
||||
[Collection("QueryCacheTests")]
|
||||
public class AsyncQueryCacheTests : TestBase
|
||||
{
|
||||
const int a = 123, b = 456;
|
||||
var cmdDef = new CommandDefinition(@"select @a; select @b;", new
|
||||
{
|
||||
a, b
|
||||
}, commandType: CommandType.Text, flags: CommandFlags.NoCache);
|
||||
private SqlConnection _marsConnection;
|
||||
private SqlConnection MarsConnection => _marsConnection ?? (_marsConnection = GetOpenConnection(true));
|
||||
|
||||
int c, d;
|
||||
SqlMapper.PurgeQueryCache();
|
||||
int before = SqlMapper.GetCachedSQLCount();
|
||||
using (var multi = MarsConnection.QueryMultiple(cmdDef))
|
||||
[Fact]
|
||||
public void AssertNoCacheWorksForQueryMultiple()
|
||||
{
|
||||
c = multi.Read<int>().Single();
|
||||
d = multi.Read<int>().Single();
|
||||
const int a = 123, b = 456;
|
||||
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
|
||||
|
||||
@@ -104,85 +104,6 @@ SELECT * FROM @ExplicitConstructors"
|
||||
}
|
||||
}
|
||||
|
||||
[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 abstract class Order
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
|
||||
<PackageReference Include="MySql.Data" Version="7.0.7-m61" />
|
||||
<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="xunit" Version="2.3.0-beta1-build3642" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.0-beta1-build1309" />
|
||||
|
||||
@@ -6,36 +6,40 @@ namespace Dapper.Tests
|
||||
{
|
||||
public partial class DataReaderTests : TestBase
|
||||
{
|
||||
[Fact]
|
||||
public void GetSameReaderForSameShape()
|
||||
[Collection("QueryCacheTests")]
|
||||
public class DataReaderQueryCacheTests : TestBase
|
||||
{
|
||||
var origReader = connection.ExecuteReader("select 'abc' as Name, 123 as Id");
|
||||
var origParser = origReader.GetRowParser(typeof(HazNameId));
|
||||
[Fact]
|
||||
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();
|
||||
list.Count.IsEqualTo(1);
|
||||
list[0].Name.IsEqualTo("abc");
|
||||
list[0].Id.IsEqualTo(123);
|
||||
origReader.Dispose();
|
||||
var list = origReader.Parse<HazNameId>().ToList();
|
||||
list.Count.IsEqualTo(1);
|
||||
list[0].Name.IsEqualTo("abc");
|
||||
list[0].Id.IsEqualTo(123);
|
||||
origReader.Dispose();
|
||||
|
||||
var secondReader = connection.ExecuteReader("select 'abc' as Name, 123 as Id");
|
||||
var secondParser = secondReader.GetRowParser(typeof(HazNameId));
|
||||
var thirdParser = secondReader.GetRowParser(typeof(HazNameId), 1);
|
||||
var secondReader = connection.ExecuteReader("select 'abc' as Name, 123 as Id");
|
||||
var secondParser = secondReader.GetRowParser(typeof(HazNameId));
|
||||
var thirdParser = secondReader.GetRowParser(typeof(HazNameId), 1);
|
||||
|
||||
list = secondReader.Parse<HazNameId>().ToList();
|
||||
list.Count.IsEqualTo(1);
|
||||
list[0].Name.IsEqualTo("abc");
|
||||
list[0].Id.IsEqualTo(123);
|
||||
secondReader.Dispose();
|
||||
list = secondReader.Parse<HazNameId>().ToList();
|
||||
list.Count.IsEqualTo(1);
|
||||
list[0].Name.IsEqualTo("abc");
|
||||
list[0].Id.IsEqualTo(123);
|
||||
secondReader.Dispose();
|
||||
|
||||
// now: should be different readers, but same parser
|
||||
ReferenceEquals(origReader, secondReader).IsEqualTo(false);
|
||||
ReferenceEquals(origParser, secondParser).IsEqualTo(true);
|
||||
ReferenceEquals(secondParser, thirdParser).IsEqualTo(false);
|
||||
// now: should be different readers, but same parser
|
||||
ReferenceEquals(origReader, secondReader).IsEqualTo(false);
|
||||
ReferenceEquals(origParser, secondParser).IsEqualTo(true);
|
||||
ReferenceEquals(secondParser, thirdParser).IsEqualTo(false);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Linq;
|
||||
namespace Dapper.Tests
|
||||
{
|
||||
[Collection("QueryCacheTests")]
|
||||
public class NullTests : TestBase
|
||||
{
|
||||
[Fact]
|
||||
|
||||
+101
-40
@@ -38,6 +38,24 @@ namespace Dapper.Tests
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -51,19 +69,8 @@ namespace Dapper.Tests
|
||||
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);
|
||||
p.Direction = ParameterDirection.Input;
|
||||
@@ -85,19 +92,8 @@ namespace Dapper.Tests
|
||||
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);
|
||||
p.Direction = ParameterDirection.Input;
|
||||
@@ -218,7 +214,6 @@ namespace Dapper.Tests
|
||||
.IsEqualTo(str);
|
||||
}
|
||||
|
||||
#if !NETCOREAPP1_0
|
||||
[Fact]
|
||||
public void TestTVPWithAnonymousObject()
|
||||
{
|
||||
@@ -289,19 +284,8 @@ namespace Dapper.Tests
|
||||
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);
|
||||
p.Direction = ParameterDirection.Input;
|
||||
@@ -344,6 +328,83 @@ namespace Dapper.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[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]
|
||||
public void DataTableParameters()
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ using Xunit;
|
||||
|
||||
namespace Dapper.Tests.Providers
|
||||
{
|
||||
[Collection("TypeHandlerTests")]
|
||||
public class EntityFrameworkTests : TestBase
|
||||
{
|
||||
public EntityFrameworkTests()
|
||||
|
||||
@@ -24,41 +24,45 @@ namespace Dapper.Tests
|
||||
}
|
||||
}
|
||||
|
||||
[FactSqlite]
|
||||
public void Issue466_SqliteHatesOptimizations()
|
||||
[Collection("TypeHandlerTests")]
|
||||
public class SqliteTypeHandlerTests : TestBase
|
||||
{
|
||||
using (var connection = GetSQLiteConnection())
|
||||
[FactSqlite]
|
||||
public void Issue466_SqliteHatesOptimizations()
|
||||
{
|
||||
SqlMapper.ResetTypeHandlers();
|
||||
var row = connection.Query<HazNameId>("select 42 as Id").First();
|
||||
row.Id.IsEqualTo(42);
|
||||
row = connection.Query<HazNameId>("select 42 as Id").First();
|
||||
row.Id.IsEqualTo(42);
|
||||
using (var connection = GetSQLiteConnection())
|
||||
{
|
||||
SqlMapper.ResetTypeHandlers();
|
||||
var row = connection.Query<HazNameId>("select 42 as Id").First();
|
||||
row.Id.IsEqualTo(42);
|
||||
row = connection.Query<HazNameId>("select 42 as Id").First();
|
||||
row.Id.IsEqualTo(42);
|
||||
|
||||
SqlMapper.ResetTypeHandlers();
|
||||
row = connection.QueryFirst<HazNameId>("select 42 as Id");
|
||||
row.Id.IsEqualTo(42);
|
||||
row = connection.QueryFirst<HazNameId>("select 42 as Id");
|
||||
row.Id.IsEqualTo(42);
|
||||
SqlMapper.ResetTypeHandlers();
|
||||
row = connection.QueryFirst<HazNameId>("select 42 as Id");
|
||||
row.Id.IsEqualTo(42);
|
||||
row = connection.QueryFirst<HazNameId>("select 42 as Id");
|
||||
row.Id.IsEqualTo(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[FactSqlite]
|
||||
public async Task Issue466_SqliteHatesOptimizations_Async()
|
||||
{
|
||||
using (var connection = GetSQLiteConnection())
|
||||
[FactSqlite]
|
||||
public async Task Issue466_SqliteHatesOptimizations_Async()
|
||||
{
|
||||
SqlMapper.ResetTypeHandlers();
|
||||
var row = (await connection.QueryAsync<HazNameId>("select 42 as Id").ConfigureAwait(false)).First();
|
||||
row.Id.IsEqualTo(42);
|
||||
row = (await connection.QueryAsync<HazNameId>("select 42 as Id").ConfigureAwait(false)).First();
|
||||
row.Id.IsEqualTo(42);
|
||||
using (var connection = GetSQLiteConnection())
|
||||
{
|
||||
SqlMapper.ResetTypeHandlers();
|
||||
var row = (await connection.QueryAsync<HazNameId>("select 42 as Id").ConfigureAwait(false)).First();
|
||||
row.Id.IsEqualTo(42);
|
||||
row = (await connection.QueryAsync<HazNameId>("select 42 as Id").ConfigureAwait(false)).First();
|
||||
row.Id.IsEqualTo(42);
|
||||
|
||||
SqlMapper.ResetTypeHandlers();
|
||||
row = await connection.QueryFirstAsync<HazNameId>("select 42 as Id").ConfigureAwait(false);
|
||||
row.Id.IsEqualTo(42);
|
||||
row = await connection.QueryFirstAsync<HazNameId>("select 42 as Id").ConfigureAwait(false);
|
||||
row.Id.IsEqualTo(42);
|
||||
SqlMapper.ResetTypeHandlers();
|
||||
row = await connection.QueryFirstAsync<HazNameId>("select 42 as Id").ConfigureAwait(false);
|
||||
row.Id.IsEqualTo(42);
|
||||
row = await connection.QueryFirstAsync<HazNameId>("select 42 as Id").ConfigureAwait(false);
|
||||
row.Id.IsEqualTo(42);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,46 +8,96 @@ using Xunit;
|
||||
|
||||
namespace Dapper.Tests
|
||||
{
|
||||
[Collection("TypeHandlerTests")]
|
||||
public class TypeHandlerTests : TestBase
|
||||
{
|
||||
[Fact]
|
||||
public void TestChangingDefaultStringTypeMappingToAnsiString()
|
||||
[Collection("QueryCacheTests")]
|
||||
public class TypeHandlerQueryCacheTests : TestBase
|
||||
{
|
||||
const string sql = "SELECT SQL_VARIANT_PROPERTY(CONVERT(sql_variant, @testParam),'BaseType') AS BaseType";
|
||||
var param = new { testParam = "TestString" };
|
||||
[Fact]
|
||||
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();
|
||||
result01.IsEqualTo("nvarchar");
|
||||
var result01 = connection.Query<string>(sql, param).FirstOrDefault();
|
||||
result01.IsEqualTo("nvarchar");
|
||||
|
||||
SqlMapper.PurgeQueryCache();
|
||||
SqlMapper.PurgeQueryCache();
|
||||
|
||||
SqlMapper.AddTypeMap(typeof(string), DbType.AnsiString); // Change Default String Handling to AnsiString
|
||||
var result02 = connection.Query<string>(sql, param).FirstOrDefault();
|
||||
result02.IsEqualTo("varchar");
|
||||
SqlMapper.AddTypeMap(typeof(string), DbType.AnsiString); // Change Default String Handling to AnsiString
|
||||
var result02 = connection.Query<string>(sql, param).FirstOrDefault();
|
||||
result02.IsEqualTo("varchar");
|
||||
|
||||
SqlMapper.PurgeQueryCache();
|
||||
SqlMapper.AddTypeMap(typeof(string), DbType.String); // Restore Default to Unicode String
|
||||
SqlMapper.PurgeQueryCache();
|
||||
SqlMapper.AddTypeMap(typeof(string), DbType.String); // Restore Default to Unicode String
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestChangingDefaultStringTypeMappingToAnsiStringFirstOrDefault()
|
||||
{
|
||||
const string sql = "SELECT SQL_VARIANT_PROPERTY(CONVERT(sql_variant, @testParam),'BaseType') AS BaseType";
|
||||
var param = new { testParam = "TestString" };
|
||||
|
||||
var result01 = connection.QueryFirstOrDefault<string>(sql, param);
|
||||
result01.IsEqualTo("nvarchar");
|
||||
|
||||
SqlMapper.PurgeQueryCache();
|
||||
|
||||
SqlMapper.AddTypeMap(typeof(string), DbType.AnsiString); // Change Default String Handling to AnsiString
|
||||
var result02 = connection.QueryFirstOrDefault<string>(sql, param);
|
||||
result02.IsEqualTo("varchar");
|
||||
|
||||
SqlMapper.PurgeQueryCache();
|
||||
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]
|
||||
public void TestChangingDefaultStringTypeMappingToAnsiStringFirstOrDefault()
|
||||
{
|
||||
const string sql = "SELECT SQL_VARIANT_PROPERTY(CONVERT(sql_variant, @testParam),'BaseType') AS BaseType";
|
||||
var param = new { testParam = "TestString" };
|
||||
|
||||
var result01 = connection.QueryFirstOrDefault<string>(sql, param);
|
||||
result01.IsEqualTo("nvarchar");
|
||||
|
||||
SqlMapper.PurgeQueryCache();
|
||||
|
||||
SqlMapper.AddTypeMap(typeof(string), DbType.AnsiString); // Change Default String Handling to AnsiString
|
||||
var result02 = connection.QueryFirstOrDefault<string>(sql, param);
|
||||
result02.IsEqualTo("varchar");
|
||||
|
||||
SqlMapper.PurgeQueryCache();
|
||||
SqlMapper.AddTypeMap(typeof(string), DbType.String); // Restore Default to Unicode String
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Issue136_ValueTypeHandlers()
|
||||
{
|
||||
@@ -518,51 +568,6 @@ namespace Dapper.Tests
|
||||
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 int A { get; set; }
|
||||
@@ -658,5 +663,84 @@ namespace Dapper.Tests
|
||||
var delta = returned - date;
|
||||
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 @@
|
||||
<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" />
|
||||
@@ -26,7 +26,8 @@
|
||||
<PackageReference Include="System.Xml.XmlDocument" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
<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.Lightweight" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
|
||||
#if !NETSTANDARD1_3
|
||||
namespace Dapper
|
||||
{
|
||||
internal sealed class SqlDataRecordHandler : SqlMapper.ITypeHandler
|
||||
@@ -18,4 +17,3 @@ namespace Dapper
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Reflection;
|
||||
#if !NETSTANDARD1_3
|
||||
|
||||
namespace Dapper
|
||||
{
|
||||
/// <summary>
|
||||
@@ -23,18 +22,6 @@ namespace Dapper
|
||||
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 @@ namespace Dapper
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+4
-6
@@ -219,24 +219,22 @@ namespace Dapper
|
||||
typeHandlers = new Dictionary<Type, ITypeHandler>();
|
||||
#if !NETSTANDARD1_3
|
||||
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 !NETSTANDARD1_3
|
||||
[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 @@ namespace Dapper
|
||||
/// <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<SqlDataRecord> as a <see cref="TableValuedParameter"/>.
|
||||
/// Used to pass a IEnumerable<SqlDataRecord> 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]
|
||||
|
||||
Reference in New Issue
Block a user