Невозможно сопоставить ответ SQL-запроса с переменными C #

#c# #sql-server #ado.net

#c# #sql-server #ado.net

Вопрос:

У меня есть этот скрипт SQL-запроса, который я пытаюсь вызвать через ADO.Net код.

 DECLARE   
  @Error NVARCHAR(MAX)
DECLARE
  @Errors TABLE
  (
    [Column_Name] VARCHAR(MAX),
    [Message] VARCHAR(MAX)
  )

IF NOT EXISTS(...)  
BEGIN  
  INSERT INTO @Errors
  SELECT
    'Col1',
    'value does not exist.'
END

IF NOT EXISTS(...)  
BEGIN  
  INSERT INTO @Errors
  SELECT
    'Col2',
    'value does not exist.'
END

IF EXISTS (SELECT * FROM @Errors)
BEGIN 
  SET @Error = (  
    SELECT  
      [Column_Name],
      [Message]
    FROM
      @Errors
    FOR JSON PATH, INCLUDE_NULL_VALUES
  );

  SELECT @Error AS 'Error_Message'
END
ELSE
BEGIN
    SELECT TOP(1) Id
    FROM dbo.table
END
  

Это код C #

 using (var conn = await _connectionFactory.OpenConnectionAsync(database: "dbName", readOnly: true))
{
    using (var cmd = conn.CreateCommandFromSqlFile(path, "test.sql"))
    {
        string errors = string.Empty;
        Guid id;

        using (DataReader reader = await cmd.ExecuteReaderAsync(ct))
        {
            if (reader.HasRows)
            {
                while (await reader.ReadAsync(ct))
                {
                    errors = reader.GetString("Error_Message");
                    id = reader.GetGuid("Id");
                }
            }
        }
    }
}
  

Сценарий SQL всегда возвращает либо, Error_Message либо Id в зависимости от условия. Если @Errors таблица заполнена, она возвращает Error_Message , и если ошибок нет, то она возвращает Id .

Я пытаюсь получить эти значения в коде c #, но он выдает исключение в while блоке, поскольку одно из значений, либо Error_Message либо Id , не будет присутствовать.

Мне нужна помощь в том, чтобы корректно добавить эти значения в мой код C #. Есть ли способ сделать это?

Комментарии:

1. Используйте выходные параметры.

2. @DaleK — Сценарий SQL не является хранимой процедурой или функцией, это просто SQL-скрипт.

3. Итак? Вы можете использовать параметры с обычным SQL… Я использую их все время для небольшого количества возвращаемых значений, они работают лучше, потому что вам не нужны средства чтения и / или таблицы данных и т.д.

Ответ №1:

Я не думаю, что есть «изящный» способ сделать это так, как вы его структурируете, ИМХО

  • вы могли бы использовать c # try catch , чтобы перехватить исключение, возникающее при отсутствии ошибки, но обработка исключений выполняется медленно и не должна использоваться как обычный поток управляющего устройства
  • вы могли бы изменить свои запросы так, чтобы они оба возвращали одинаковые имена столбцов, чтобы больше не было исключений, а идентификатор был представлен в виде строки, и у вас либо был другой столбец, который укажет вам, является ли строка идентификатором GUId или ошибкой, чтобы вы могли проанализировать или сообщить об этом соответствующим образом, либо вы пытаетесь проанализировать его и посмотреть, работает ли он
  • вы могли бы изменить свои запросы, чтобы в них всегда было два столбца — строка и идентификатор GUId, а один всегда равен null, и использовать null, чтобы определить, какой тип у вас есть
  • вы всегда можете выбрать как свои ошибки, так и свои идентификаторы и использовать тот факт, что sql server позволяет вам открывать несколько наборов результатов

Но реально я думаю, что я бы перестал пытаться возвращать ошибки SQL в виде результирующих наборов и просто выбрасывал их как исключения и перехватывал их в c #. Примите шаблон наличия строки, которая начинается с пустого, проверьте наличие col1 и, если его там нет, добавьте «col1 не существует» к строке, проверьте наличие col2 и, возможно, добавьте «col2 не существует».. затем, если строка не пуста, выбросьте ее, в противном случае верните свой результирующий набор. C # возникнет исключение с полезным сообщением об ошибке (и вы можете предоставить код переменной, если хотите обработать его по-другому, а не просто сообщать пользователю)

Ответ №2:

Вы можете использовать GetName функцию SqlDataReader

 using (SqlDataReader reader = await cmd.ExecuteReaderAsync(ct))
{
    if (reader.HasRows)
    {
        while (await reader.ReadAsync(ct))
        {
            var colName = reader.GetName(0);
            if (colName.Equals("Error_Message")
                errors = reader.GetString("Error_Message");
            else 
                id = reader.GetGuid("Id");
        }
    }
}
  

https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqldatareader.getname?view=dotnet-plat-ext-3.1#System_Data_SqlClient_SqlDataReader_GetName_System_Int32_