I would like the following code to work:
```C#
_con.Query<(int id, int value)>("select 1 as id, 2 as value");
Currently, this compiles and executes without any errors, but the tuple is empty, both values are 0.
I think this is because it creates the object using the parameterless ctor, then tries to find settable properties named id and value, which it obviously can't find.
It would work, if dapper instead used the (int,int) ctor of ValueTuple.
My suggestion would be to bake special knowledge of C# tuples into dapper, special casing ValueTuple and using the second ctor (if it matches), instead of preferring the parameterless ctor.
The only breaking change would be, as far as I thought this through, if someone had existing code with
```C#
_con.Query<(int id, int value)>("select 1 as Item2, 2 as Item1");
With the existing behaviour, it would create the object using the parameterless ctor, then set Item1 to 2 and Item2 to 1.
With the proposed behaviour it would create the object with the (int,int) constructor, passing the values in the order they are returned, and setting Item1 to 1, Item2 to 2.
Another downside is that this would create unintuitive behaviour with regard to named return values, ignoring the tuple names and filling the tuple positionally.
E.g.
C#
_con.Query<(int id, int value)>("select 1 as value, 2 as id");
would return a tuple with id = 1, value = 2.
I still think that this would be worth it, what do you think?
yeah, I've been doing some looking in this area - it is particularly vexing that we can't pass them in as parameters sensible, and there's no good ways of passing them out without - as you note - it introducing different rules over name vs positional lookup. I'm still playing with a few things here. I'm genuinely in two minds as to how best to proceed. The problem isn't in implementing it; the problem is a human one - it is in not causing other people problems, essentially not being an "enabler" for their issues. The ambiguity / change over name/position is a tricky one and isn't necessarily obvious.
Needs thought more than anything.
I think that, while it would be nice to be able to pass them _in_, the existing anonymous type trick works pretty well to make that not a high priority.
But unless you use the workaround mentioned here, you can't materialize into a strongly typed object (I'm not a fan of dynamic) without declaring a type, which litters the codebase with little types, each only used once. It becomes even worse (in my opinion) if you define some kind of super-type which contains all properties you ever use, where each property is only filled in some queries. IMO there should be a 1:1 direct mapping between all properties and all sql results.
So I think that being unable to pass tuples _out_ is slightly more annoying.
Regardless of future design decisions (I trust you fully in this regards), what would you think about adding some kind of safeguard, so that users which expect this to already work get feedback that it doesn't?
My thinking would be to throw an exception if the target type is a ValueTuple, but no properties are filled (the sql does not contain Item1, Item2, ItemN). Such cases are surely 100% invalid user expectations.
That way
```C#
_con.Query<(int, int)>("select 1 as Item1, 2 as Item2");
continues to work, but
```C#
_con.Query<(int id, int value)>("select 1 as id, 2 as value");
will throw.
I personally don't like that because it really uglifies the SQL.
If value-tuple doesn't currently work, it makes it easier to allow
special-casing it as a positional type. I'll keep digging and thinking.
On 6 Apr 2017 6:41 p.m., "Lukas Rieger" notifications@github.com wrote:
I think that, while it would be nice to be able to pass them in, the
existing anonymous type trick works pretty well to make that not a high
priority.But unless you use the workaround mentioned here
http://stackoverflow.com/a/30469302/1872399, you can't materialize into
a strongly typed object (I'm not a fan of dynamic) without declaring a
type, which litters the codebase with little types, each only used once. It
becomes even worse (in my opinion) if you define some kind of super-type
which contains all properties you ever use, where each property is only
filled in some queries. IMO there should be a 1:1 direct mapping between
all properties and all sql results.So I think that being unable to pass tuples out is slightly more
annoying.
Regardless of future design decisions (I trust you fully in this regards),
what would you think about adding some kind of safeguard, so that users
which expect this to already work get feedback that it doesn't?My thinking would be to throw an exception if the target type is a
ValueTuple, but no properties are filled (the sql does not contain Item1,
Item2, ItemN). Such cases are surely 100% invalid user expectations.That way
_con.Query<(int, int)>("select 1 as Item1, 2 as Item2");
continues to work, but
_con.Query<(int id, int value)>("select 1 as id, 2 as value");
will throw.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/StackExchange/Dapper/issues/735#issuecomment-292251909,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AABDsGZgxDP3tlogzirfdUuygOKfkGdPks5rtSPWgaJpZM4M146w
.
Experimental branch is here: https://github.com/StackExchange/Dapper/compare/vs2017...value-tuples - feedback welcome, but seems to work
https://github.com/StackExchange/Dapper/blob/master/Dapper.Tests/TupleTests.cs
Looks like this is now supported?
from the test doesn't look like it's mapping using the names specified in the tuple
from the test doesn't look like it's mapping using the names specified in the tuple
That's because that is impossible. Value-tuples expose the names to callers, but the names are completely opaque to callees - they simply don't exist. So when using value-tuples, the best we can offer is "positional"
To illustrate:
void Caller() {
var data = TupleMethod();
Echo(data.x); // this is fine; names are exposed
Echo(data.y); // via attributes on TupleMethod's return,
// i.e. [return: TupleElementNames(new string[] { "x", "y" })]
}
(int x, string y) TupleMethod(...) {
return Callee<(int x, string y)>();
}
T Callee<T>() {
// this, however, only knows about Item1 and Item2;
// the names "x" and "y" **do not exist** here, in any way
}
Closing this out to cleanup...we just forgot to mention it was merged in, sorry! The by-name isn't noted as Marc above because we can't...so at this point all we can do is in there.
Most helpful comment
Experimental branch is here: https://github.com/StackExchange/Dapper/compare/vs2017...value-tuples - feedback welcome, but seems to work