Dynamics 365 – Acessar WCF Externo Server Side (WebClient + SOAP)


Olá pessoal,

Por mais que para alguns seja um assunto já batido. Eu ainda acredito que faltem artigos para melhor descrever como acessar serviços (WCF’s) externos ao Dynamics 365 quando estamos do lado servidor (server side), principalmente no modo online onde existem alguns restrições (falei disso neste post).

Eu não acho que o exemplo oficial seja suficiente e bem detalhado. Pois, não acessa um serviço, não ensina como passar parâmetros…

Neste post, irei fazer/descrever como realizar chamadas à serviços externos utilizando apenas o objeto WebClient e um envelope SOAP. Provavelmente seja o método mais rudimentar, porém o mais simples de se fazer o acesso.

Bom, primeiro vamos criar o serviço WCF… Irei construir um WCF para simplesmente realizar a soma de dois números (apenas para demonstrar a ideia do post).

Primeiramente nossa, interface (IMat.cs):

using System.ServiceModel;

namespace WcfService
{
 [ServiceContract]
 public interface IMat
 {
 [OperationContract]
 int Somar (string x, string y);
 }
}

Agora o code behind do serviço (Mat.svc.cs):

using System;

namespace WcfService
{
 public class Mat : IMat
 {
 public int Somar(string x, string y)
 {
 return Convert.ToInt32(x) + Convert.ToInt32(y);
 }
 }
}

Por fim o Web.config:

<?xml version="1.0"?>
<configuration>

 <appSettings>
 <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
 </appSettings>
 <system.web>
 <compilation debug="true" targetFramework="4.5.2" />
 <httpRuntime targetFramework="4.5.2"/>
 </system.web>
 <system.serviceModel>
 <behaviors>
 <serviceBehaviors>
 <behavior>
 <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
 <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
 <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
 <serviceDebug includeExceptionDetailInFaults="false"/>
 </behavior>
 </serviceBehaviors>
 </behaviors>
 <protocolMapping>
 <add binding="basicHttpsBinding" scheme="https" />
 </protocolMapping> 
 <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
 </system.serviceModel>
 <system.webServer>
 <modules runAllManagedModulesForAllRequests="true"/>
 <!-- To browse web app root directory during debugging, set the value below to true. Set to false before deployment to avoid disclosing web app folder information. -->
 <directoryBrowse enabled="true"/>
 </system.webServer>

</configuration>

Com isso, temos nosso serviço. Faça o build, realize alguns testes localmente. Por fim, publique. Vale lembrar que o serviço tem que ser publicado externamente. Não pode ficar no localhost ou ser acessado por IP. Por isso, neste exemplo, eu usei uma máquina no Azure para hostear o serviço. O serviço ficou com o seguinte endereço:

http://tiagowin20163976.cloudapp.net/WcfService/Mat.svc

PS: Vou tentar sempre manter está maquina ligada, assim, todo mundo pode usar meu serviço para testes!

Com o serviço criado e hospedado. É hora de voltarmos para o mundo do Dynamics! rs

Crie um plugin. Neste exemplo, criei um plugin para entidade Conta (account):

using Microsoft.Xrm.Sdk;
using System;
using System.Net;
using System.ServiceModel;

namespace Plugins
{
 public class AccountCreate : IPlugin
 {
 #region Secure/Unsecure Configuration Setup
 private string _secureConfig = null;
 private string _unsecureConfig = null;

 public AccountCreate(string unsecureConfig, string secureConfig)
 {
 _secureConfig = secureConfig;
 _unsecureConfig = unsecureConfig;
 }
 #endregion
 public void Execute(IServiceProvider serviceProvider)
 {
 // Extract the tracing service for use in debugging sandboxed plug - ins.
 // If you are not registering the plug-in in the sandbox, then you do
 // not have to add any tracing service related code.
 ITracingService tracingService =
 (ITracingService)serviceProvider.GetService(typeof(ITracingService));

 // Obtain the execution context from the service provider.
 IPluginExecutionContext context = (IPluginExecutionContext)
 serviceProvider.GetService(typeof(IPluginExecutionContext));

 // The InputParameters collection contains all the data passed in the message request.
 if (context.InputParameters.Contains("Target") &&
 context.InputParameters["Target"] is Entity)
 {
 // Obtain the target entity from the input parameters.
 Entity entity = (Entity)context.InputParameters["Target"];

 // Verify that the target entity represents an entity type you are expecting. 
 // For example, an account. If not, the plug-in was not registered correctly.
 if (entity.LogicalName != "account")
 return;

 // Obtain the organization service reference which you will need for
 // web service calls.
 IOrganizationServiceFactory serviceFactory =
 (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
 IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

 try
 {
 // Plug-in business logic goes here.
 var serviceURL = "http://tiagowin20163976.cloudapp.net/WcfService/Mat.svc";

 tracingService.Trace("- CallServiceByWebClientSOAP");
 tracingService.Trace("Somar('4','5')");

 using (var client = new WebClient())
 {
 var dataEnvelope = "<s:Envelope xmlns:s='http://schemas.xmlsoap.org/soap/envelope/'>" +
 "<s:Body>" +
 "<<strong>Somar</strong> xmlns='http://tempuri.org/'>" +
 "<<strong>x</strong>><strong>4</strong></<strong>x</strong>>" +
 "<<strong>y</strong>><strong>5</strong></<strong>y</strong>>" +
 "</<strong>Somar</strong>>" +
 "</s:Body>" +
 "</s:Envelope>";

 client.Headers.Add("Content-Type", "text/xml;charset=utf-8");
 client.Headers.Add("SOAPAction", "\"http://tempuri.org/<strong>IMat/Somar</strong>\"");
 var response = client.UploadString(serviceURL, dataEnvelope);

 tracingService.Trace("Result: " + response);
 }
 }
 catch (FaultException<OrganizationServiceFault> ex)
 {
 throw new InvalidPluginExecutionException("An error occurred in MyPlug-in.", ex);
 }
 catch (Exception ex)
 {
 tracingService.Trace("MyPlugin: {0}", ex.ToString());
 throw;
 }
 }
 }
 }
}

Aqui, acredito que seja importante falar sobre o que está acontecendo dentro do “try” do plugin:

  • serviceUrl – Informamos a URL do serviço que estamos requisitando
  • client – Instanciamos e criamos um objeto da classe WebClient usando um bloco “using”
  • dataEnvelope – Criamos nosso envelope SOAP. Veja que é imprescindivel informar o método do serviço, bem como os parâmetros e seus respectivos valores (deixei isso em negrito!)
  • Header “content-type” – Informamos o conteúdo do pacote, neste caso apenas text/xml
  • Header “SOAPAction” – Informamos o formato do pacote, ele deve seguir o que a interface do serviço requisita para a implementação (deixei em negrito onde temos que trocar)
  • UploadString – Por fim, chamamos o serviço através de um upload do pacote SOAP para a URL/Serviço requisitada(o). A resposta é retornada em formato XML (devido ao formato que escolhemos anteriormente). E será esta:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><SomarResponse xmlns="http://tempuri.org/"><strong><SomarResult>9</SomarResult></strong></SomarResponse></s:Body></s:Envelope>

Assim, ao criar uma conta no CRM, quando observamos o log de execução do plugin, vemos:

Bom, é isso, espero ter ajudado! Irei escrever outro post de como fazer a mesma chamada, mas utilizando o que temos de melhor Binding e Contratos!

[]’s,

Tiago

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.