Playing around with Dapper, I'm quite pleased with the results so far - intriguing!
But now, my next scenario would be to read data from two tables - a Student
and an Address
table.
Student
table has a primary key of StudentID (INT IDENTITY)
, Address
has an AddressID (INT IDENTITY)
. Student
also has an FK called AddressID
linking into the Address
table.
My idea was to create two classes, one for each table, with the properties I'm interested in. Additionally, I put an PrimaryAddress
property of type Address
onto my Student
class in C#.
I then tried to retrieve both student and address data in a single query - I mimick the sample that's given on the Github page:
var data = connection.Query<Post, User>(sql, (post, user) => { post.Owner = user; });
var post = data.First();
Here, a Post
and a User
are retrieved, and the owner of the post is set to the user - the type returned is a Post
- correct?
So in my code, I define two parameters to the generic Query
extension method - a Student
as the first which should be returned, and an Address
as the second, which will be stored onto the student instance:
var student = _conn.Query<Student, Address>
("SELECT s.*, a.* FROM dbo.Student s
INNER JOIN dbo.Address a ON s.AddressID = a.AddressID
WHERE s.StudentenID = @Id",
(stu, adr) => { stu.PrimaryAddress = adr; },
new { Id = 4711 });
Trouble is - I get an error in Visual Studio:
Using the generic method 'Dapper.SqlMapper.Query(System.Data.IDbConnection, string, System.Func, dynamic, System.Data.IDbTransaction, bool, string, int?, System.Data.CommandType?)' requires 6 type arguments
I don't really understand why Dapper insists on using this overload with 6 type arguments...
That would be cause I changed APIs and forgot to update the documentation, I corrected the error.
Be sure to have a look at Tests.cs for a full up-to-date spec.
In particular, the old API used to take in an
Action<T,U>
to perform the mapping, the trouble was that it felt both arbitrary and inflexible. You could not fully control the return type. The new APIs take in aFunc<T,U,V>
. So you can control the type you get back from the mapper and it does not need to be a mapped type.I just tied up some additional flexibility around multi mapping, this test should make it clear:
Dapper pipes all the multi mapping APIs through a single method, so if something fails it will end up in the 6 param one. The other piece of the puzzle was that I did not allow for some super flexible splits, which I just added.
Note, the
splitOn
param will default toId
, meaning it will take a column calledid
orId
as the first object boundary. However if you need boundaries on multiple primary keys that have different names for say a "3 way" multi mapping, you can now pass in a comma separated list.So if we were to fix the above, probably the following would work: