Возвращая тип SqlDataReader, даже если попытка catch не удалась - получение не всех путей возвращает значение - PullRequest
1 голос
/ 03 апреля 2020

Я пытаюсь читать из БД с помощью try / catch, и я хочу явно вернуть данные. Я получаю сообщение о not all code paths return a value. Я понимаю, что мне нужно что-то вернуть, но как мне справиться с этим сценарием? Могу ли я вернуть пустой SqlDataReader объект?

Запущенные хранимые процедуры будут возвращать различные типы объектов в зависимости от того, что выполнено, в противном случае я бы вернул указанный тип c.

    private SqlDataReader RunSql(string connectionString, string procName)
    {
        using(SqlConnection = conn = new SqlConnection(connectionString))
        { 
            using(SqlCommand SqlCmd = new SqlCommant(procName, conn))
            { 
                SqlCmd.CommandType = CommandType.StoredProcedure;
                conn.Open();

                try 
                {
                    using(SqlDataReader reader = SqlCmd.ExecuteReader())
                    {
                        if(reader.HasRows)
                        {  
                             return reader;
                        }
                    }
                }
                catch(Exception e)
                {
                    //?
                }
                finally
                {
                    conn.Close();

                }
            } 
        }
        //?
    }

Ответы [ 3 ]

0 голосов
/ 03 апреля 2020

Поскольку метод не возвращает ничего, если условие не выполнено, это причина, по которой вы получаете эту ошибку компиляции. Вы могли бы попробовать ниже фрагмент. Просто return null в вашей закомментированной строке //?

private SqlDataReader RunSql(string connectionString, string procName)
        {

            using (var conn = new SqlConnection(connectionString))
            {
                using (SqlCommand SqlCmd = new SqlCommand(procName, conn))
                {
                    SqlCmd.CommandType = CommandType.StoredProcedure;
                    conn.Open();

                    try
                    {
                        using (SqlDataReader reader = SqlCmd.ExecuteReader())
                        {
                            if (reader.HasRows)
                            {
                                return reader;
                            }

                        }
                    }
                    catch (Exception e)
                    {

                    }
                    finally
                    {
                       conn.Close();

                    }
                }
            }
            return null;
        }

Надеюсь, что это может исправить вашу ошибку компиляции.

0 голосов
/ 03 апреля 2020

Принятый ответ будет работать, но, как написано, он "проглатывает" исключение, которое вы поймали; это препятствует тому, чтобы звонящий даже знал, что что-то пошло не так. Гораздо лучший подход состоит в том, чтобы перебросить исключение, чтобы вызывающий абонент все еще был проинформирован о том, что произошло что-то плохое, и затем он мог бы решить проблему, как захочет.

Другая проблема заключается в том, что объекты чтения и подключения будут избавиться от них до того, как использовать их в последующем коде; благодаря заявлениям using и finally. Одним из решений этой проблемы является возложение на вызывающий объект ответственности за объект соединения.

private static SqlDataReader RunSql(SqlConnection connection, string procedureName) {
    using (var command = connection.CreateCommand()) {
        command.CommandText = procedureName;
        command.CommandType = CommandType.StoredProcedure;

        try {
            if (connection.State != ConnectionState.Open) {
                connection.Open();
            }

            var reader = command.ExecuteReader();

            if (reader.HasRows) {
                return reader;
            }
            else {
                return null;
            }
        }
        catch (Exception e) {
            // log the exception or whatever you wanna do with it

            throw; // rethrow the exception
        }
    }
}

Вот альтернативная версия, в которой используется IAsyncEnumerable; Обратите внимание, что теперь нам разрешено распоряжаться объектами в рамках одного и того же метода. Кроме того, логика c, которая обрабатывает пустой случай считывателя, больше не требуется, поскольку наш перечисляемый будет просто пустым, если нечего было читать.

// define a container for information relevant to each row
public sealed class SqlResultSetRow : IEnumerable<(string fieldName, Type fieldType, object fieldValue)>
{
    private readonly (string fieldName, Type fieldType, object fieldValue)[] m_fields;

    public int ResultSetIndex { get; }

    public SqlResultSetRow((string, Type, object)[] fields, int resultSetIndex) {
        m_fields = fields;

        ResultSetIndex = resultSetIndex;
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    public IEnumerator<(string fieldName, Type fieldType, object fieldValue)> GetEnumerator() {
        var rows = m_fields;

        foreach (var row in rows) {
            yield return row;
        }
    }
    public object GetFieldName(int fieldOffset) => m_fields[fieldOffset].fieldName;
    public object GetFieldValue(int fieldOffset) => m_fields[fieldOffset].fieldValue;
}

// define a class to hold our generic method(s)
public static class SqlClientExtensions
{
    // implement a refactored version of the original method
    public static async IAsyncEnumerable<T> ProcessRows<T>(this SqlCommand command, Func<SqlResultSetRow, CancellationToken, ValueTask<T>> rowCallback, CommandBehavior commandBehavior = CommandBehavior.SequentialAccess, [EnumeratorCancellation] CancellationToken cancellationToken = default) {
        using (var dataReader = await command.ExecuteReaderAsync(commandBehavior, cancellationToken)) {
            var resultSetIndex = 0;

            do {
                var fieldCount = dataReader.FieldCount;
                var fieldNames = new string[fieldCount];
                var fieldTypes = new Type[fieldCount];

                for (var i = 0; (i < fieldCount); ++i) {
                    fieldNames[i] = dataReader.GetName(i);
                    fieldTypes[i] = dataReader.GetFieldType(i);
                }

                while (await dataReader.ReadAsync(cancellationToken)) {
                    var fields = new (string, Type, object)[fieldCount];

                    for (var i = 0; (i < fieldCount); ++i) {
                        fields[i] = (fieldNames[i], fieldTypes[i], dataReader.GetValue(i));
                    }

                    yield return await rowCallback(new SqlResultSetRow(fields, resultSetIndex), cancellationToken);
                }
            } while (await dataReader.NextResultAsync(cancellationToken));
        }
    }
}

class Program
{
    // a minimal implementation of a rowCallBack function
    public static async ValueTask<ExpandoObject> OnProcessRow(SqlResultSetRow resultSetRow, CancellationToken cancellationToken) {
        var rowValue = (new ExpandoObject() as IDictionary<string, object>);

        foreach (var field in resultSetRow) {
            rowValue[field.fieldName] = field.fieldValue;
        }

        return (rowValue as ExpandoObject);
    }

    // put everything together
    static async Task Main(string[] args) {
        try {
            var programTimeout = TimeSpan.FromMinutes(3);
            var cancellationTokenSource = new CancellationTokenSource(programTimeout);

            using (var connection = new SqlConnection(@"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=master;Integrated Security=True;"))
            using (var command = connection.CreateCommand()) {
                command.CommandText = "select 1 as [a];";
                command.CommandType = CommandType.Text;

                await connection.OpenAsync(cancellationToken: cancellationTokenSource.Token);

                await foreach (dynamic row in command.ProcessRows(rowCallback: OnProcessRow, cancellationToken: cancellationTokenSource.Token)) {
                    Console.WriteLine($"a: {row.a}");
                }
            }
        }
        catch (Exception e) {
            // do something with exception here

            throw;
        }
    }
}
0 голосов
/ 03 апреля 2020
        { 
            SqlCmd.CommandType = CommandType.StoredProcedure;
            conn.Open();

            try 
            {
                using(SqlDataReader reader = SqlCmd.ExecuteReader())
                {
                    if(reader.HasRows)
                    {  
                         return reader;
                    }
                }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...