Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SqlMapper.GridReader constructor is internal and prevents mocking #1338

Open
dreymers-goformz opened this issue Sep 24, 2019 · 4 comments
Open

Comments

@dreymers-goformz
Copy link

It would be great if the SqlMapper.GridReader constructor were changed to be public - then I'd be able to include QueryMultiple with the rest of my mocking for Dapper in my unit and integration tests.

@Debbus72
Copy link

If there is a good reason to keep the constructor internal then please say so and close this issue. The issue is now like 1,5 years open...
If there is a good way to mock the SqlMapper.GridReader that we can use (we use Moq), please also say so...

@dreymers-goformz
Copy link
Author

Since my original post, I've added a wrapper class around everything Dapper, and set up my tests to mock against that.

@ESSAdeveloper
Copy link

@dreymers-goformz I am having the same issue. Can you please post a couple of examples you wrote to wrap Dapper?

@Z-Zhao
Copy link

Z-Zhao commented Jan 26, 2023

@dreymers-goformz I am having the same issue. Can you please post a couple of examples you wrote to wrap Dapper?

This is my approach and example code of wrapper. Please have a look at my comments and the below links.
UnoSD/Moq.Dapper#85 (comment)
https://makolyte.com/csharp-how-to-unit-test-code-that-uses-dapper/

Then you could build Wrappers for QueryMultiple:
Please note the 1st Wrapper method that returns GridReader is still not be able to Mocked (because you can not construct it). But the 2nd method which has 2 types <T1, T2> can be mocked, because it returns a List that contains 2 items, and each item is a list.
You could keep going and create more methods for <T1, T2, T3> or more record sets. As long as the developer team has an agreement that there is no more than n Record sets, you can always stop at <T1, T2,... Tn>, and I assume that n won't be too large.

I can think of 2 cons of this approach:

  1. the returned type is List<List<object>>, and you have to cast each item to the expected List<T1>, List<T2>... when you retrieve it.
  2. the wrapper methods execucte GridReader.Read() 2 times, pre-load all of them and save all in a list. So when you have large data sets and many records in each data set, this could potentially cause performance issue.

Interface IDapperWrapper

// A wrapper to convert Dapper's static extention SQL execution methods (in its static partial class SqlMapper) to non-static methods
public interface IDapperWrapper
{

// Execute a command that returns multiple result sets as a GridReader object, and access each in turn.
GridReader QueryMultiple(IDbConnection cnn, string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null);

// Execute a command that returns 2 result sets as a list that contains 2 list items, and access each in turn.
// T1: First mapping object type.
// T2: Second mapping object type.
IEnumerable<IEnumerable<object>> QueryMultiple<T1, T2>(IDbConnection cnn, string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null);

}

The DapperWrapper class

public class DapperWrapper : IDapperWrapper
{
public GridReader QueryMultiple(IDbConnection cnn, string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = CommandType.StoredProcedure)
{
return cnn.QueryMultiple(sql, param, transaction, commandTimeout, commandType);
}

public IEnumerable<IEnumerable<object>> QueryMultiple<T1, T2>(IDbConnection cnn, string sql, object? param = null, IDbTransaction? transaction = null, int? commandTimeout = null, CommandType? commandType = null)
{
    GridReader multi = cnn.QueryMultiple(sql, param, transaction, commandTimeout, commandType);
    List<IEnumerable<object>> list = new List<IEnumerable<object>>();
    IEnumerable<T1> t1List = multi.Read<T1>();
    IEnumerable<T2> t2List = multi.Read<T2>();
    list.Add(t1List as IEnumerable<object>);
    list.Add(t2List as IEnumerable<object>);
    return list;
}

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants