Разработка
04
Июн
5

Получение SQL ConnectionString из плагина

Иногда в плагинах CRM необходимо выполнить прямой запрос к БД. Для этого Вам нужна строка подключения к CRM. Но нет никакого нормального поддерживаемого метода, предоставляемого SDK, чтобы ее получить. И тут у Вас есть несколько вариантов, где ее взять: захардкодить, передавать в параметрах или вовсе хранить в CRM. Но в некоторых случаях ее можно получить из контекста плагина. Этот код поможет Вам получать ConnectString из плагина, кастомного БП или Экшена:

using System;
using Microsoft.Xrm.Sdk;
using System.Data.SqlClient;
using System.Reflection;

namespace ConnectionString
{
    public class ConnectionString : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            var platformContext = context.GetType().InvokeMember("PlatformContext", BindingFlags.GetProperty, null, context, null);
            var transaction = (SqlTransaction)platformContext.GetType().InvokeMember("SqlTransaction", BindingFlags.GetProperty, null, platformContext, null);
            throw new InvalidPluginExecutionException(transaction.Connection.ConnectionString);
        }
    }
}

Данный маневр работает только для On-Premise и без режима Sandbox.


Примечание: если Вы попробуете из транзакционного конвейера выполнить SQL-запрос для того же объекта, который Вы обновляете/добавляете, то Вы получите блокировку, что в итоге приведет к таймауту.

Вы можете обойти это несколькими путями:

  • Выполнить SQL-запрос вне транзакции: на этапе PreValidation или в асинхронном плагине.
  • Обратится к процедуре, хранящейся на сторонней БД, которая в свою очередь выполнит запрос к базе CRM (возможно при этом Вы должны будете использовать SET TRUSTWORTHY ON, чтобы гарантировать, что передать контекст безопасности между этими двумя БД).
  • Воспользоваться транзакцией текущего плагина:
    IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
    object platformContext = context.GetType().InvokeMember("PlatformContext", BindingFlags.GetProperty, null, context, null);
    SqlTransaction tx = (SqlTransaction)platformContext.GetType().InvokeMember("SqlTransaction", BindingFlags.GetProperty, null, platformContext, null);
    DataSet result = SqlHelper.ExecuteDataset(tx, CommandType.Text, "SELECT ...");
    
Комментарии (5)
  • joman 04.06.2017

    Добрый день. А для workflow activity такое можно провернуть?
    В него передаётся executionContext. Можно ли из executionContext получить PlatformContext?

  • slivka_83 04.06.2017

    Здрасте.
    Сам такое не делал, но люди пишут, что этот код и для БП работает.

  • joman 04.06.2017

    У меня не получилось получить PlatformContext из executionContext. Поиск по интернетам тоже не дал результата. В общем на данный момент для БП провернуть такое не получилось

  • joman 04.06.2017

    Таки нашел, что искал. Оказалось, что нужно было копать в «IOrganizationServiceFactory». Там через 1 вложенность можно получить тот же «PlatformContext».
    Код для получения транзакции в «workflow activity»:
    object ExecCont = executionContext.GetExtension().GetType().InvokeMember(«ExecutionContext», BindingFlags.GetProperty, null, executionContext.GetExtension(), null);
    object PlugCont = ExecCont.GetType().InvokeMember(«PluginContext», BindingFlags.GetProperty, null, ExecCont, null);
    object platformContext = PlugCont.GetType().InvokeMember(«PlatformContext», BindingFlags.GetProperty, null, PlugCont, null);
    SqlTransaction Transaction = (SqlTransaction)platformContext.GetType().InvokeMember(«SqlTransaction», BindingFlags.GetProperty, null, platformContext, null);

  • joman 04.06.2017

    Для асинхронных БП в итоге так и не удалось найти platformContext. Видимо его нет, т.к. выполнение происходит в отдельной утилите. В итоге получить строку соединения с БД можно гораздо проще, чем все уловки с InvokeMember иже с ними. Всего одна строка кода и строка в любом плагине и БП получена:
    string Conn = Registry.GetValue(@»HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM», «configdb», «»).ToString()
    Данный маневр работает только для On-Premise и без режима Sandbox, как и в приведённом примере.

*

code