Лучший способ обработки подключения при вызове функции из консольного приложения или объекта SQLCLR с (Context Connection=true)

У меня есть следующий тип кода на моем уровне данных, который можно вызывать из консольного приложения, приложения Windows и т. д., при этом правильная строка подключения считывается из соответствующего файла App.Config вызывающей стороны:

public static udsDataset GetDataset(int datasetID)         
{
   string connectionString = 
             ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
   string sql = @"select * from Dataset WHERE DatasetID=@datasetID";

   using (SqlConnection conn = new SqlConnection(connectionString))
   {
      // Dapper query:            
      return conn.Query<udsDataset>(sql, new {datasetID } ).First();
   }    
}

Теперь я хочу вызвать этот же код из хранимой процедуры SQLCLR (в базе данных, где существуют эти таблицы), где обычно используется контекстное подключение:

using(SqlConnection connection = new SqlConnection("context connection=true")) 
{
    connection.Open();
    // etc etc etc
}

Самый очевидный подход, который приходит на ум, — перегрузить функцию:

public static udsDataset GetDataset(int datasetID)
{
   string connectionString = 
       ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;

   using (SqlConnection conn = new SqlConnection(connectionString))
   {
      return GetDataset(datasetID, conn);
   }
}

public static udsDataset GetDataset(int datasetID, SqlConnection conn)         
{
    // caller is responsible for closing and disposing connection
    string sql = @"select * from Dataset WHERE DatasetID=@datasetID";

    return conn.Query<udsDataset>(sql, new {datasetID } ).First();
}

Таким образом, приложения с App.Config могут вызывать версию без подключения, а SQLCLR может вызывать версию, требующую SqlConnection.

Это «кажется нормальным», но необходимость писать один и тот же стиль перегрузки для каждой похожей функции заставляет чувствовать себя неправильно.


person tbone    schedule 07.07.2015    source источник
comment
Почему бы вам не написать метод GetConnection() и не использовать его во всех местах, где вы используете Using(sqlConnection conn = GetConnection()) {}? метод GetConnection должен обрабатывать строку подключения, поэтому остальная часть вашего кода не должна заботиться об этом.   -  person Thorarins    schedule 07.07.2015
comment
Разве при этом теоретически не теряется преимущество гарантированного избавления от соединения? Несмотря на это, мне по-прежнему нужна возможность передачи существующего соединения при вызове из процедуры SQLCLR.   -  person tbone    schedule 07.07.2015
comment
нет, метод должен создавать новое соединение при каждом вызове, а окружающий его метод Using() обрабатывает удаление, поэтому с этим проблем нет.   -  person Thorarins    schedule 08.07.2015


Ответы (1)


Принимая вопрос (и комментарии к нему) за чистую монету, зачем вам нужно:

возможность передачи существующего соединения при вызове из процедуры SQLCLR

? Вы должны относиться к Context Connection так же, как и к любому другому соединению в отношении Open и Dispose. Похоже, вы думаете, что SqlConnection при использовании строки подключения "Context Connection = true;" нужно открыть только один раз, а затем не удалять до полного завершения, тогда как в противном случае вы бы Open / Dispose несколько раз. Я не вижу причин для разного поведения в этих двух сценариях.


Помимо всего этого, как лучше всего обрабатывать обнаружение изменений в среде (между консольным приложением и объектом SQLCLR)? У вас есть два варианта, оба из которых, вероятно, проще, чем вы ожидаете:

  1. Не вносите никаких изменений в код приложения, но полагайтесь на дополнительный файл конфигурации:

    Вы можете создать файл с именем sqlservr.exe.Config в папке C:\Program Files\Microsoft SQL Server\MSSQL{SqlVersion}.{SqlServerInstanceName}\MSSQL\Binn (например, C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Binn, где 11 в MSSQL11 для SQL Server 2012). Формат этого файла, как и следовало ожидать, следующий:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
        <connectionStrings>
            <add name="CoolioAppDB" connectionString="Context Connection = true;" />
        </connectionStrings>
    </configuration>
    

    Этот может считаться "более чистым" кодом, но он вводит внешнюю зависимость, с которой ваш администратор баз данных может согласиться, может не понравиться, но терпеть, или может попросить вашего менеджера выписать вас за ;-).

  2. Внесите очень незначительные изменения в код приложения, но не полагайтесь на дополнительный файл конфигурации:

    Вы можете легко автоматически определить, работаете ли вы в настоящее время на узле CLR SQL Servers, используя IsAvailable класса SqlContext. Просто обновите исходный код следующим образом:

    string connectionString = "Context Connection = true;"; // default = SQLCLR connection
    
    if (!SqlContext.IsAvailable) // if not running within SQL Server, get from config file
    {
      connectionString = 
                ConfigurationManager.ConnectionStrings["CoolioAppDB"].ConnectionString;
    }
    

    Это использование, кстати, отмечено в разделе «Примечания» этой связанной страницы MSDN для свойства IsAvailable.

person Solomon Rutzky    schedule 07.07.2015
comment
Вариант №2 кажется наиболее привлекательным, но либо я не так объяснил, либо я чего-то не понимаю. DLL, содержащая GetDataset(), может быть запущена или не запущена в SQL Server, она может быть просто развернута на обычном рабочем столе Windows. Мне просто нужно добавить использование Microsoft.SqlServer.Server в этот класс, и тогда я в порядке???? Итак, если SqlContext.IsAvailable == true, используйте: connectionString = Context Connection = true; а если нет, прочитайте строку подключения из файла конфигурации (и это произойдет только в том случае, если НЕ работает в SQL Server, поэтому мне НЕ НУЖНА конфигурация, установленная в Sql Server) - person tbone; 08.07.2015
comment
Предполагая, что это правда, мне просто нужно добавить это использование, тогда эта логика может быть просто инкапсулирована в функцию GetConnectionString, и тогда отдельные методы не должны будут знать об этом, прекрасно. - person tbone; 08.07.2015
comment
@tbone Отвечая на ваш первый комментарий здесь: Да, это именно то, что касается Варианта № 2. Просто добавьте using Microsoft.SqlServer.Server;, чтобы проверить SqlContext.IsAvailable. Код, который я разместил, будет работать независимо от того, где установлена ​​​​DLL. Опять же, именно этот вариант использования рекомендуется на этой странице MSDN в разделе «Примечания». Итак, ко второму пункту: да, вы можете инкапсулировать это в общий метод GetConnectionString. О, да, это красиво :-). - person Solomon Rutzky; 08.07.2015
comment
Отлично, еще раз спасибо @srutzky, кажется, вы ответили на половину моих вопросов! :) - person tbone; 08.07.2015