提交 9cc975b3 编写于 作者: M Marc Gravell

Define expected behaviour for value-tuple parameters and returns, and implement

上级 ae7a29a9
......@@ -25,6 +25,7 @@
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
<PackageReference Include="xunit.runner.console" Version="2.2.0" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="System.ValueTuple" Version="4.3.0"/>
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net452' ">
......
using System;
using Xunit;
namespace Dapper.Tests
{
public class TupleTests : TestBase
{
[Fact]
public void TupleStructParameter_Fails_HelpfulMessage()
{
try
{
// I can see this happening...
connection.QuerySingle<int>("select @id", (id: 42, name: "Fred"));
Assert.Fail();
ValueTuple<int, int> b = (24, 13);
b.Item1.IsEqualTo(24);
}
catch (NotSupportedException ex)
{
ex.Message.IsEqualTo("ValueTuple should not be used for parameters - the language-level names are not available to use as parameter names, and it adds unnecessary boxing");
}
}
[Fact]
public void TupleClassParameter_Works()
{
connection.QuerySingle<int>("select @Item1", Tuple.Create(42, "Fred")).IsEqualTo(42);
}
[Fact]
public void TupleReturnValue_Works_ByPosition()
{
var val = connection.QuerySingle<(int id, string name)>("select 42, 'Fred'");
val.id.IsEqualTo(42);
val.name.IsEqualTo("Fred");
}
[Fact]
public void TupleReturnValue_Works_NamesIgnored()
{
var val = connection.QuerySingle<(int id, string name)>("select 42 as [Item2], 'Fred' as [Item1]");
val.id.IsEqualTo(42);
val.name.IsEqualTo("Fred");
}
}
}
......@@ -2232,11 +2232,42 @@ internal static IList<LiteralToken> GetLiteralTokens(string sql)
{
return CreateParamInfoGenerator(identity, checkForDuplicates, removeUnused, GetLiteralTokens(identity.sql));
}
static bool IsValueTuple(Type type) => type != null && type.IsValueType() && type.FullName.StartsWith("System.ValueTuple`");
static List<IMemberMap> GetValueTupleMembers(Type type, string[] names)
{
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
int keep = Math.Min(fields.Length, names.Length);
var result = new List<IMemberMap>(keep);
for(int i = 0; i < keep; i++)
{
FieldInfo field = null;
string name = "Item" + (i + 1).ToString(CultureInfo.InvariantCulture);
foreach(var test in fields)
{
if(test.Name == name)
{
field = test;
break;
}
}
if (field != null)
{
result.Add(new SimpleMemberMap(string.IsNullOrWhiteSpace(names[i]) ? name : names[i], field));
}
}
return result;
}
internal static Action<IDbCommand, object> CreateParamInfoGenerator(Identity identity, bool checkForDuplicates, bool removeUnused, IList<LiteralToken> literals)
{
Type type = identity.parametersType;
if (IsValueTuple(type))
{
throw new NotSupportedException("ValueTuple should not be used for parameters - the language-level names are not available to use as parameter names, and it adds unnecessary boxing");
}
bool filterParams = false;
if (removeUnused && identity.commandType.GetValueOrDefault(CommandType.Text) == CommandType.Text)
{
......@@ -2924,7 +2955,6 @@ static LocalBuilder GetTempLocal(ILGenerator il, ref Dictionary<Type, LocalBuild
ITypeMap typeMap = GetTypeMap(type);
int index = startBound;
ConstructorInfo specializedConstructor = null;
#if !COREFX
......@@ -3010,9 +3040,9 @@ static LocalBuilder GetTempLocal(ILGenerator il, ref Dictionary<Type, LocalBuild
il.Emit(OpCodes.Ldloc_1);// [target]
}
var members = (specializedConstructor != null
var members = IsValueTuple(type) ? GetValueTupleMembers(type, names) :((specializedConstructor != null
? names.Select(n => typeMap.GetConstructorParameter(specializedConstructor, n))
: names.Select(n => typeMap.GetMember(n))).ToList();
: names.Select(n => typeMap.GetMember(n))).ToList());
// stack is now [target]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册