Кто-нибудь может предложить элегантный способ написания обобщенной c C# Dapper процедуры для возврата n наборов результатов? - PullRequest
2 голосов
/ 08 апреля 2020

Я создал небольшое количество общих c процедур, использующих Dapper для возврата списков или отдельных элементов любого заданного типа (используя SP на стороне SQL) для использования в службе данных модели DI, например,

public async Task<List<TOut>> GetList<TOut>(string proc, dynamic parameters)
{
    await using var conn = new SqlConnection(connectionString);
    var cmd = parameters == null ? new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure) : new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure, parameters: parameters);
    return conn.Query<TOut>(cmd).ToList();
}

Однако я пытался расширить это, чтобы использовать метод QueryMultiple для возврата произвольного числа наборов результатов в одном запросе (для эффективности, поскольку первый запрос довольно дорогой, а другие наборы результатов зависят от него) , Я хотел бы закончить со строго типизированными наборами результатов (не анонимными объектами) без написания множества кода. Однако я не вижу никакого способа придумать произвольное количество параметров типа - пока лучшее, что мне удалось сделать, - это создать один метод для двух наборов результатов, один для трех и так далее:

public async Task<dynamic> Get2Lists<T1, T2>(string proc, dynamic parameters)
{
    await using var conn = new SqlConnection(connectionString);
    var cmd = parameters == null ? new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure) : new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure, parameters: parameters);
    var result = conn.QueryMultiple(cmd);
    return new
    {
        Table1 = result.Read<T1>(),
        Table2 = result.Read<T2>()
    };
}

public async Task<dynamic> Get3Lists<T1, T2, T3>(string proc, dynamic parameters)
{
    await using var conn = new SqlConnection(connectionString);
    var cmd = parameters == null ? new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure) : new CommandDefinition(commandText: proc, commandType: CommandType.StoredProcedure, parameters: parameters);
    var result = conn.QueryMultiple(cmd);
    return new
    {
        Table1 = result.Read<T1>(),
        Table2 = result.Read<T2>(),
        Table3 = result.Read<T3>()
    };
}

Мне кажется, должен быть более элегантный способ - есть предложения?

1 Ответ

2 голосов
/ 10 апреля 2020

Краткий ответ: Dapper поддерживает использование SqlMapper.GridReader.

Вот как мы это используем. Вы должны иметь возможность изменить это в соответствии с вашими потребностями. У нас есть класс DapperRepository, в котором содержатся все наши общие вызовы c. Это (сильно) сокращенная версия, чтобы показать вам, что вам нужно.

using Dapper;
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;

public class DapperRepository
{

    private readonly string _connStringName;

    public DapperRepository(string connStringName)
    {
        _connStringName = connStringName;
    }

    private SqlConnection OpenConnection(string cs)
    {
        var connection = ConfigurationManager.ConnectionStrings[cs].ConnectionString;
        SqlConnection con = new SqlConnection(connection);
        con.Open();
        return con;
    }

    // lots of other methods removed for the brevity of this post

    // This is the method you want
    public void QuerySPMultiple(string sql, Action<SqlMapper.GridReader> callback, object parameters = null)
    {
        using (var connection = OpenConnection(_connStringName))
        {
            var gr = connection.QueryMultiple(sql, param: parameters,commandTimeout:0, commandType: CommandType.StoredProcedure);
            callback(gr);
         }
    }
}

Допустим, у нас есть класс, который выглядит следующим образом:

public class FooBar
{
    public List<Stuff> MyStuff { get; set; }
    public int MyInt { get; set; }
}

А затем для заполнения FooBar с одним сохраненным вызовом pro c, который возвращает несколько наборов данных:

// Create an instance of the repository
DapperRepository repo = new DapperRepository("your connection string");

// Create your parameters
DynamicParameters param = new DynamicParameters();
param.Add("@Name", "Value");

// Call the stored proc and get back multiple sets of data
FooBar fooBar = new FooBar();
repo.QuerySPMultiple("YourSPName", (reader) =>
{
    fooBar.MyStuff = reader.Read<Stuff>().ToList();
    fooBar.MyInt = reader.Read<int>().FirstOrDefault();
}, parameters: param);

Надеюсь, все это имеет смысл! Не стесняйтесь задавать дополнительные вопросы, если нет.

...