![]() |
#1 |
Участник
|
axforum blogs: Кастомные служебные задания для CRM
Источник: http://axforum.info/forums/blog.php?b=345
============== Начиная с версии 4.0 в CRM есть подсистема Служебных заданий - System Job. В SDK служебные задания представлены объектом AsyncOperation и, как можно понять из названия, за их выполнение отвечает Асинхронный сервис системы. Служебные задания - это запускаемые асинхронно плагины, рабочие процессы и различные служебные системные задания, такие как обновления индексов базы данных. В более ранних версиях системы эти задания выполнялись SQL Agent. И хотя со Служебными заданиями можно взаимодействовать через SDK, с ними нельзя сделать одно самое главное - создать свой тип Служебного задания. Это прискорбно, так как достаточно часто возникает задача выполнять какие-либо операции на периодической основе. Хороший пример - это системные задания автоматического сведения Целей и закрытия истекших сервисных Контрактов. Даже набившая оскомину проблема "Поздравлялки с днем рождения" могла бы получить изысканное решение, будь у нас возможность выполнять некие действия на периодической основе. Вариантов решения множество:
Для реализации этой задачи я выбрал следующий подход:
В теории все просто, но на практике возникают сложности. Первая проблема, которую нам придется преодолеть - это врожденное неумение CRL сборок для SQL работать с веб-сервисами. Дело в том, что текущие версии SQL поддерживают работу только с определенным, очень ограниченным набором сборок из состава .NET: Supported .NET Framework Libraries. Все прочие (и их зависимости) придется регистрировать в базе отдельно. Но, обо всем по порядку! 1. Поддержка CLR. Необходимо включить поддержку CLR типов для базы в которой мы планируем разворачивать нашу процедуру. Для этого необходимо выполнить следующий скрипт: X++: sp_configure 'show advanced options', 1;GORECONFIGURE;GOsp_configure 'clr enabled', 1;GORECONFIGURE;GO Пожалуйста, убедитесь что вы понимаете что делаете, прежде чем выполнять данный скрипт! Рекомендую ознакомиться с официальным источником: Common Language Runtime (CLR) Integration. 2. Поддержка WCF Следующим шагом, необходимо зарегистрировать сборки (и их зависимости), которые нам потребуются для взаимодействия с веб-сервисами Windows Communication Foundation. Для этого выполним следующий скрипт: X++: ALTER DATABASE [FixRM_MSCRM] SET Trustworthy ONGOCREATE ASSEMBLY[System.Web] from 'C:\Windows\Microsoft.NET\Framework64\v2.0.50727\System.Web.dll'with permission_set = UNSAFEGOCREATE ASSEMBLYSMDiagnostics from 'C:\Windows\Microsoft.NET\Framework64\v3.0\Windows Communication Foundation\SMDiagnostics.dll'with permission_set = UNSAFEGOCREATE ASSEMBLY [System.Runtime.Serialization] from 'C:\Windows\Microsoft.NET\Framework64\v3.0\Windows Communication Foundation\System.Runtime.Serialization.dll'with permission_set = UNSAFEGOCREATE ASSEMBLY[System.IdentityModel] from 'C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\System.IdentityModel.dll'with permission_set = UNSAFEGOCREATE ASSEMBLY[System.IdentityModel.Selectors] from 'C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\System.IdentityModel.Selectors.dll'with permission_set = UNSAFEGOCREATE ASSEMBLY[System.Messaging] from 'C:\Windows\Microsoft.NET\Framework64\v2.0.50727\System.Messaging.dll' with permission_set = UNSAFEGOCREATE ASSEMBLY[Microsoft.Transactions.Bridge] from'C:\Windows\Microsoft.NET\Framework\v3.0\Windows Communication Foundation\Microsoft.Transactions.Bridge.dll'with permission_set = UNSAFEGOCREATE ASSEMBLY [System.ServiceModel] from 'C:\Windows\Microsoft.NET\Framework64\v3.0\Windows Communication Foundation\System.ServiceModel.dll'with permission_set = UNSAFEGO Так как наши сборки "UNSAFE" мы вынуждены сделать нашу базу Trustworthy в первой инструкции. 3. Проект CLR процедуры для SQL Server. Visual Studio имеет готовые шаблоны проектов CLR типов для SQL Server. Воспользуемся этим: Далее необходимо указать к какой базе будет подключено решение. Нужно указать ту базу, для которой вы выполняли инструкции выше: Следующий шаг - создание самой процедуры. Для этого выбираем пункт "Создать новый элемент" (Add new item) из контекстного меню проекта: Далее добавим в созданный файл следующий код: X++: using System;using System.Data;using System.Data.SqlClient;using System.Data.SqlTypes;using Microsoft.SqlServer.Server;using SqlServerCrmClrIntegration.CrmService;using System.Collections.Generic;using System.ServiceModel.Channels;using System.ServiceModel.Security.Tokens;using System.ServiceModel;public partial class StoredProcedures{ [Microsoft.SqlServer.Server.SqlProcedure] public static void ExecuteWorkflow(SqlString sql, SqlGuid workflowId) { using (SqlConnection connection = new SqlConnection("context connection=true")) { connection.Open(); SqlCommand command = new SqlCommand(sql.Value, connection); SqlDataReader reader = command.ExecuteReader(); using (reader) { String serviceUrl = "http://localhost/FixRM/XRMServices/2011/Organization.svc"; SymmetricSecurityBindingElement security = new SymmetricSecurityBindingElement(new SspiSecurityTokenParameters()); HttpTransportBindingElement http = new HttpTransportBindingElement(); CustomBinding binding = new CustomBinding(); binding.Elements.Add(security); binding.Elements.Add(http); OrganizationServiceClient client = new OrganizationServiceClient(binding, new EndpointAddress(serviceUrl)); while (reader.Read()) { OrganizationRequest executeWorkflow = new OrganizationRequest(); executeWorkflow.RequestName = "ExecuteWorkflow"; executeWorkflow.Parameters = new ParameterCollection(); executeWorkflow.Parameters.Add(new KeyValuePair("WorkflowId", workflowId.Value)); executeWorkflow.Parameters.Add(new KeyValuePair("EntityId", reader.GetSqlGuid(0).Value)); client.Execute(executeWorkflow); } client.Close(); } } }}; Не забудьте указать корректный адрес веб-сервиса в переменной serviceUrl! Я не сторонник хардкодинга подобных вещей - данный код - это лишь пример. В своем решении, вы можете реализовать получение адреса из таблицы настроек или параметра процедуры - на свой вкус. 4. Развертывание и тестирование созданного CLR типа. Visual Studio умеет автоматически развертывать сборки и их содержимое на SQL Server. К сожалению, при этом студия зависает на неопределенное время. Возможно это специфика моего развертывания - не знаю. В любом случае, если вы столкнулись с такой проблемой вы можете воспользоваться следующим скриптом для выполнения той же самой операции: X++: IF EXISTS (SELECT name FROM sysobjects WHERE name = 'ExecuteWorkflow')DROP PROCEDURE ExecuteWorkflowIF EXISTS (SELECT * FROM sys.assemblies asms WHERE asms.name = N'SqlServerCrmClrIntegration' and is_user_defined = 1)DROP ASSEMBLY [SqlServerCrmClrIntegration]CREATE ASSEMBLY[SqlServerCrmClrIntegration] from 'С:\Путь к каталогу проекта\SqlServerCrmClrIntegration\bin\Debug\SqlServerCrmClrIntegration.dll'WITH PERMISSION_SET = UNSAFEGOCREATE PROCEDURE ExecuteWorkflow (@SQL NVARCHAR(MAX), @WORKFLOW UNIQUEIDENTIFIER)AS EXTERNAL NAME SqlServerCrmClrIntegration.StoredProcedures.ExecuteWorkflow; GO Напоминаю, что в скрипте необходимо указать корректный путь к сборке решения и корректные имена типов. Теперь остается убедиться что все работает. Для этого вы можете воспользоваться отладчиком VS или просто запустить процедуру: X++: DECLARE @SQL AS NVARCHAR(MAX) = 'SELECT account.accountidFROM FilteredAccount accountWHERE account.accountid = ''1BDFF7A2-D599-E111-A0F2-0800271D883E'''DECLARE @WORKFLOW AS UNIQUEIDENTIFIER = '53304737-2B84-4C09-8969-351218500BD1'EXECUTE ExecuteWorkflow @SQL, @WORKFLOWGO В данном примере, запрос выбирает идентификатор тестовой записи объекта Клиент и вызывает предварительно настроенный тестовый процесс. Процесс должен быть доступен для запуска в ручную! В реализации приведенной выше, я разворачивал сборки непосредственно в базе CRM, что, конечно же, не поддерживается. В реальном проекте следует использовать для этих целей отдельную базу. Второй момент: учетная запись от имени которой выполняется код - это учетная запись SQL сервера. Если база данных CRM обслуживается той же инстанцией SQL, то ее не нужно (даже запрещено) добавлять как пользовательскую учетную запись CRM. Все действия вашей сборки будут выполняться от привилегированной учетной записи SYSTEM: 4. Настройка SQL Job Последний шаг - это настройка самого задания SQL Agent Job. Это наиболее простая операция из всех здесь приведенных. Нужно указать лишь указать операцию которую необходимо выполнить: и расписание запуска: Разумеется в настройках необходимо указать какую-то разумную выборку и правильный процесс-обработчик. Процесс должен:
А теперь немного о грустном... При вызове вашей процедуры вы с большой долей вероятности получите ошибку: X++: Server: Msg 6522, Level 16, State 2, Line 1A .NET Framework error occurred during execution of user defined routine or aggregate XYZ:System.IO.FileLoadException: Could not load file or assembly 'System.ServiceModel, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. Assembly in host store has a different signature than assembly in GAC. (Exception from HRESULT: 0x80131050)System.IO.FileLoadException: Что это и почему возникает написано тут: http://support.microsoft.com/kb/949080/en-us и тут: Supported .NET Framework Libraries. Последнюю ссылку я уже приводил в этой статье. Суть проблемы в следующем: при инстанцировании сборки, SQL Server по какой-то неведомой мне причине проверяет, нет ли более новой версии в GAC. Если более новая версия найдена, то запуск валится с ошибкой. Чтобы все работало корректно, необходимо обновить сборки в базе более свежими версиями из GAC. Почему это могло случиться? На ваш сервер установились обновления .NET Framework. Надо было думать прежде чем ставить что попало на производственный сервер! Если вы думаете, что для обновления сборок в базе достаточно повторно запустить скрипт регистрации, то нет. Почему-то обновления не затрагивают сборки в каталоге инсталляции .NET Framework! Свежие версии содержаться только в GAC. Выцарапать их оттуда можно, например, командой меню RUN: X++: C:\Windows\assembly\gac_msil Теперь осталось только найти и положить в какой-либо каталог 17 сборок и повторно их зарегистрировать. Прошлые версии предварительно нужно удалить. Будьте бдительны! Некоторые сборки имеют кросс-зависимости, так что их нужно удалять в одной транзакции (возможно только через SQL, через интерфейс не получится). Вторая проблема настолько загадочна, что я даже не стану вдаваться в суть проблемы. Приеду только ссылку на ее решение: What is Microsoft.VisualStudio.Diagnostics.ServiceModelSink.dll? Если упомянутая библиотека вылазит в сообщении об ошибке - вам сюда. Итоги. Задача решаема, но о цене судите сами. Я бы не назвал это "Enterprise Ready" решением, как любит говорить мой коллега Андрей Слепицкий, но это как минимум "быстрое решение". Перед проведением своих зловещих экспериментов рекомендую ознакомиться с первоисточником: Common Language Runtime (CLR) Integration Programming Concepts. Так же хорошее практическое руководство есть тут: Invoking a WCF Service from a CLR Trigger. Источник: http://axforum.info/forums/blog.php?b=345
__________________
Расскажите о новых и интересных блогах по Microsoft Dynamics, напишите личное сообщение администратору. |
|
|
|