Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dapper - Multi Mapping with a single return value

Tags:

dapper

Below is the code I'm using to return a paged list of objects:

string query2 = @"
        select count(*) as TotalCount from blogposts p where p.Deleted = 0 and p.PublishDate <= @date
        select * from (
            select p.*, 
            row_number() over(order by publishdate desc) as rownum
            from blogposts as p
            where p.Deleted = 0 and p.PublishDate <= @date
        ) seq
        where seq.rownum between @x and @y";

using (var cn = new SqlConnection(connectionString))
{
    cn.Open();
    using (var multi = cn.QueryMultiple(query2, new { x= lower, y = upper, date = DateTime.UtcNow }))
    {
        var totalCount = multi.Read<int>().Single();
        var posts = multi.Read<PostModel>().ToList();
        return new PagedList<PostModel>(posts, page, pageSize, x => totalCount);
    }
}

Although this works, it means that I have to define my criteria twice, once for the count query and once for the resultset query. Rather than resort to string concatenation, I can execute just one query:

        string query = @"
                select * from (select p.*, 
                row_number() over(order by publishdate desc) as rownum,
                count(*) over() as TotalCount
                from blogposts as p) seq
                where seq.rownum between @x and @y";

However, I don't seem able to map this using Dapper. I can't use the same method as above because there aren't multiple results. I've tried using multi mapping but this expects to return an IEnumerable.

How would I map to the following?

    public class PostList
    {
        public IEnumerable<PostModel> Posts;
        public int TotalCount { get; set; }
    }

Thanks

Ben

like image 713
Ben Foster Avatar asked Jun 02 '11 11:06

Ben Foster


1 Answers

Well ... you wouldn't ...

You would have to amend your PostModel to include a TotalCount property ... which is really ugly. Or execute a dynamic and remap it in a Select which is also ugly.

You see, you are returning count(*) N times with count(*) over() ... it is a hack, using this hack is not necessarily faster. I have measured it being slower than running a double query in some of my scenarios, in particular you can shortcut some indexes in the select count(*) since you are not selecting all the columns. Additionally the hack disables certain paging optimisations, for example you can not add select top N to the query.

My recommendation around paging queries would be to get indexing right, it is key. Measure perf and see if this hack actually helps (when the correct indexing is in place).

I follow about the concerns around string concatenation, but you can always define general helper methods for that.

like image 60
Sam Saffron Avatar answered Nov 11 '22 07:11

Sam Saffron