Получение 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 ...");
Добрый день. А для workflow activity такое можно провернуть?
В него передаётся executionContext. Можно ли из executionContext получить PlatformContext?
Здрасте.
Сам такое не делал, но люди пишут, что этот код и для БП работает.
У меня не получилось получить PlatformContext из executionContext. Поиск по интернетам тоже не дал результата. В общем на данный момент для БП провернуть такое не получилось
Таки нашел, что искал. Оказалось, что нужно было копать в «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);
Для асинхронных БП в итоге так и не удалось найти platformContext. Видимо его нет, т.к. выполнение происходит в отдельной утилите. В итоге получить строку соединения с БД можно гораздо проще, чем все уловки с InvokeMember иже с ними. Всего одна строка кода и строка в любом плагине и БП получена:
string Conn = Registry.GetValue(@»HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSCRM», «configdb», «»).ToString()
Данный маневр работает только для On-Premise и без режима Sandbox, как и в приведённом примере.
Для Workflow не нужно искать PlatformContext, там все немного по другому:
var context = activityContext.GetExtension();
var executionContext = context.SourceContext.GetType().GetProperty(«ExecutionContext»).GetValue(context.SourceContext);
var transaction = (SqlTransaction)executionContext.GetType().InvokeMember(«SqlTransaction», BindingFlags.GetProperty, null, executionContext, null);
var connectionString = transaction.Connection.ConnectionString;