Dynamics 365 – Certificações (MCSA e MCSE)

Olá pessoal,

Como sempre cada versão nova vem com suas novas certificações! Em fevereiro deste ano (2017), foram lançadas as certificações para o Dynamics 365.

A principal novidade foi a volta da obtenção de certificações apenas quando conseguimos passar nos exames requeridos de cada certificação. Tínhamos este conceito na versão 4 do Dynamics CRM.

 

As nomenclaturas são as mesmas já utilizadas em outras tecnologias Microsoft:

  • MCSA – Nível inicial (base)
  • MCSE – Nível especialista

Deste modo, para obtermos a certificação de MCSA precisamos passar por alguns exames (provas) requeridos, assim, iremos obter a certificação. Da mesma forma que é requerido uma certificação MCSA para iniciar os exames da certificação MCSE. Afinal primeiro precisamos ter a base conhecimento para depois conseguirmos o nível de especialista!

Atualmente temos a seguintes estrutura:

MCSA: Microsoft Dynamics 365

Devemos passar nos exames abaixo:

Ao passarmos pelos exames requeridos ganhamos a certificação de MCSA Microsoft Dynamics 365.

MCSA: Microsoft Dynamics 365 for Operations

Devemos passar nos exames abaixo:

Ao passarmos pelos exames requeridos ganhamos a certificação de MCSA Microsoft Dynamics 365 for Operations.

MCSE: Business Applications

Como pre-requisito número principal, devemos ter obtido uma certificação MCSA (Microsoft Dynamics 365 ou Microsoft Dynamics 365 for Operations)

Devemos passar em ao menos UM dos exames abaixo:

Ao passarmos pelos exames requeridos ganhamos a certificação de MCSE Business Applications.

Bom, agora é se preparar e ir atrás do resultado!

[]’s,

Tiago

Publicado em Dynamics 365, Microsoft | Marcado com , , , | Deixe um comentário

Dynamics 365 – Acionar Plugin via JScript

Olá pessoal,

Uma ótima pergunta (ou conjunto de perguntas! rs) foi feita por Tiago Ferraz Alves, que seriam:

Fazer uso do objeto XMLHttpRequest (Ajax) para acionar Plugin é uma prática suportada? Quais são os ganhos e perdas? Você já utilizou anteriormente?

Achei interessante criar um post para que possa ajudar outras pessoas a tomar a decisão de utilizar ou não esta técnica.

Primeiramente, um pouco de base e dimensão do que estamos falando…

Vale apena lembrar que por mais que o Ajax seja capaz de se comunicar com o lado servidor (server-side), sua execução é feita no lado cliente (client-side) de um aplicação, pois é baseado em JScript e envio de pacotes de transmissão.

No entanto, plugins são baseados no server-side, deste modo, apenas serviços/aplicações dentro de um servidor web poderiam “trabalhar” com os plugins.

Como conclusão do que eu disse acima: não é possível “chamar” diretamente um plugin através do lado cliente. Mas indiretamente é absolutamente possível!

Por que indiretamente é possível?

Por mais que não seja possível fazer uma chamada explicita ao plugin, podemos fazer que ele seja executado usando uma chamada client-side. Para fazer isso, existem algumas formas diferentes de se fazer. Vamos a duas abordagens mais utilizadas:

Criar registro em uma entidade customizada para acionar um plugin genérico

Apesar de ainda ser um técnica que pode ser utilizada. Ela era muito mais poderosa quando ainda não tínhamos as Custom Actions (ações de um processo).

A ideia consiste em criar uma nova entidade que funcionará como um orquestrador para o plugin, ou seja, cada vez que um registro for criado neste entidade, um plugin será executado, assim, podemos capturar a ação requisitada (através do contexto e também de atributos da entidade) e realizar a lógica adequada para cada tipo de ação.

Este post não tem o objetivo de explicar tecnicamente como executar esta técnica, porém, este é ótimo artigo para que deseja.

Acionar uma Custom Action  que irá acionar um plugin

 

 

Esta técnica apenas tornou-se possível apenas a partir da versão 2013 do CRM. Faz uso de uma Ação.  Apenas lembrando o objetivo principal de uma ação, que seria, criar ações que um entidade não possui nativamente, possibilitando novos tipos de gatilhos aos processo de negócio.

Além de utilizar um recurso nativo do CRM, tem também a vantagem de necessitar menos codificação do que a técnica anterior. Temos menos etapas de desenvolvimento.

Novamente, o objetivo aqui não é demonstrar como fazer, mas ai vai um artigo com um pouco mais de sofisticação, mas faz uso das Actions!

Bom vamos as respostas das perguntas do Tiago…

– Em resposta a primeira pergunta (se é algo suportado): Sim, completamente suportado.

– Ganhos e perdas, é algo realmente relativo e depende do contexto de utilização, mas vamos lá:

  • Prós:
    • Ambas técnicas podem ser muito eficientes em casos onde precisamos acionar um plugin através de algum controle do lado cliente, como um botão do ribbon, recurso web (web resource) e etc. Pois, usando apenas plugins para esse tipo de situação, implicaria em criar um atributo qualquer que seja alterado via JScript, fazendo o formulário ser salvo e enfim executando a regra de negócio que o plugin possui. Porém, mesmo assim, teríamos que desenvolver ainda mais para fazer algo com o retorno da regra de negócio;
    • Utilizando a proposta da primeira técnica (entidade customizada orquestradora), podemos centralizar ao máximo onde as regras de negócio serão executadas;
    • Utilizando a técnica de acionar uma action, temos como grande vantagem a possibilidade de retornar um output do plugin para a action. Tudo de forma nativa! Isso é muito mais poderoso do que apenas chamar um plugin!
  • Contras:
    • Quando utilizamos chamadas cliente para acionar algo no servidor (em nosso caso plugins). Iremos criar uma chamada extra de desenvolvimento, ou seja, na verdade nossa regra de negócio está no plugin, mas estamos acionando o plugin pelo cliente. Assim, estamos resolvendo um problema, mas inserindo outro. Pois, sempre existirá a possibilidade de ter que dar manutenção nos dois lados do desenvolvimento;
    • Na primeira técnica (entidade customizada), temos o problema de centralizar demais. Pois, considere uma equipe de desenvolvimento trabalhando em um mesmo plugin sempre! Isso não irá ser fácil…
    • É importante não extrapolar o uso desta técnicas, pois para executar qualquer regra de negócio seja necessário primeiro criar um registro em uma entidade ou invocar uma action. Pense em quando processamento adicional será necessário, além de transformar o plugin em uma espécie de Web Service, se a ideia é essa, crie um Web Services! rs
    • Lembre-se também de cenários onde o CRM será consumido por outra aplicação, provavelmente a chamada para a execução de sua regra de negócio deverá ser impactada;

– Última pergunta (se já utilizei): Não, eu não utilizei, pois ao considerar os contras em relação aos pós a resposta foi o uso de outra abordagem. Porém, já criei serviços customizados para serem chamados através de um botão no ribbon por exemplo. Neste caso, não existia uma regra de negócio que precisasse ficar em um plugin.

Bom, espero ter ajudado a esclarecer algumas dúvidas!

[]’s,

Tiago

 

Publicado em Dynamics 365 | Marcado com , | 2 Comentários

The Social Media Revolution 2017

Pessoal,

Todo ano procuro este vídeo no YouTube para me atualizar em relação as Mídias Sociais e a revolução que elas veem fazendo com nossa forma de vender, divulgar e comunicar… Realmente é algo ainda pouco assimilado por nós e principalmente pelas empresas, acredito que o tema esteja sendo muito pouco explorado no Brasil.

Ai vai…

[]’s,

Tiago Cardoso

Publicado em Social Mídia | Marcado com | Deixe um comentário

Dynamics 365 – Acessar WCF Externo Server Side (HTTP Binding + Contract)

Olá pessoal,

No post anterior, escrevi como realizar a comunicação com o serviço WCF externo do Dynamics 365 no server side (utilizei um plugin).

Neste post, irei fazer a mesma chamada, porém utilizando o que acredito que seja o mais recomendado e eficiente. Usar Biding HTML e o Contrato do serviço.

 

É imprescindível que o serviço WCF do post anterior esteja implementado. Pois, ele será a base para este. Na verdade, a única coisa que este post apresenta de diferente é o meio/forma de comunicação com o serviço!

Primeiramente, precisamos gerar o contrato do WCF, para isso, basta navegar no browser pelo endereço do serviço que veremos um tela similar a esta:

Veja que podemos criar nosso contrato utilizando o svcutil.exe (quando instalamos o Visual Studio ele é automaticamente instalado). O svcutil irá ler a interface do WCF e gerar nosso contrato.

Eu inseri alguns parâmetros para facilitar o uso do contrato:

svcutil.exe http://tiagowin20163976.cloudapp.net/WcfService/Mat.svc?wsdl ^
/namespace:*,"Dynamics365" ^
/d:"C:\Users\tiago.cardoso\Downloads"

Para executar o svcutil, precisamos abrir o Developer Command Prompt for VS (cada versão do VS tem programa) e colar o script acima:

No meu caso o arquivo Mat.cs será adicionado na pasta Downloads. Copie e adicione no MESMO PROJETO ONDE O PLUGIN IRÁ FICAR!

A última parte não poderia ser diferente de criar o plugin propriamente! Vou seguir a mesma ideia do post anterior e criar para a entidade Conta (account). Foi colocar o código e depois irei comentar:

using Dynamics365;
using Microsoft.Xrm.Sdk;
using System;
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')");

// Create Bind object
BasicHttpBinding myBinding = new BasicHttpBinding();
myBinding.Security.Mode = BasicHttpSecurityMode.None;
myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
myBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
myBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

// Create endpoint
EndpointAddress endPointAddress = new EndpointAddress(serviceURL);

// Create Channel to access Encrypt Service Interface
ChannelFactory<IMat> factory = new ChannelFactory<IMat>(myBinding, endPointAddress);
IMat channel = factory.CreateChannel();

// Call Encrypt Service
var response = channel.Somar("4", "5");

tracingService.Trace("Result: " + response);

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

Vamos lá:

  • A primeira linha faz uma referência (namespace) ao contrato que criamos com o svcutil;
  • O que acontence dentro do “try“:
    • serviceUrl – Informamos a URL do serviço que estamos requisitando
    • BasicHttpBinding – Criamos o binding que propriamente faz a conexão com o serviço WCF. Note que não estou utilizando nenhum método de autenticação ou proxy neste exemplo;
    • endPointAddress – Criamos um objeto com o endpoint do serviço;
    • factory – Informamos o contrato e endpoint para a classe ChannelFactory. Veja a conversão (unboxing) para o tipo do nosso contrato IMat;
    • channel – Cria o canal com o endpoint, ou seja, cria/abre a conexão com o serviço;
    • channel.Somar – Por fim, chamamos o método do serviço. Veja que por usarmos o contrato do serviço o método e parâmetros são reconhecidos! (tipados)

A resposta da chamada, nada mais é do que o que método retorna. Neste exemplo será um número! O número 9 (5+4)!

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

Bom, é isso! Acredito que esta forma seja bem mais robusta e utilize melhores práticas de programação bem como funcionalidades mais atuais!

[]’s,

Tiago

Publicado em Dynamics 365, Dynamics CRM | Marcado com , , , , , | Deixe um comentário

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

Publicado em Dynamics 365, Dynamics CRM | Marcado com , , , , , | Deixe um comentário

Dynamics 365 – Business Process Error ao exportar uma solução

Olá pessoal,

Um erro bem estranho começou a ocorrer em uma solução que estamos trabalhando. Ao clicarmos em exportar a solução, e concluirmos todas as etapas (publicar as exportações, selecionar o tipo de solução gerenciada/não gerenciada e etc). Antes do CRM iniciar a exportação do arquivo .zip uma mensagem de erro é sempre retornada:

E o mais interessante, se clicamos em “Exibir detalhes técnicos” (Show Techinical Details). Nada é apresentado:

Após um pouco de troubleshooting, meu colega de trabalho Justin Marshall, encontrou o problema. Processos de negócio (Business Process Flows) não publicados, em “Rascunho” (Draft) impedem que a solução seja exportada.

Além disso, existe outro agravante, a Solução Padrão (Default Solution) do CRM, simplesmente não inicia o processo de exportação. Ou seja, qualquer Processo de Negócio em rascunho impedirá a exportação!

Ao meu ver é um bug. Sendo assim, criei um bug no Connect.

Entre por favor no Microsoft Connect, e me ajudem a promover este bug, votando! Assim ele poderá ser rapidamente resolvido pela Microsoft :)

[]’s,

Tiago

Publicado em Dynamics CRM | Marcado com , , | Deixe um comentário

Dynamics 365 – Panel de Navegação não exibe todos as áreas (Vendas, Marketing, Serviços)

Olá pessoal,

A grande vantagem de utilizar o Dynamics 365 online certamente é a facilidade e quantidade de inovações que nossas organizações recebem periodicamente. Porém, em alguns casos, somos “pegos de surpresa”.

Tive bastante dificuldade de como criar o título deste post. Continuo não achando ele completo. Mas, como escrever em poucas palavras que ao criar um novo trial do Dynamics, quando seleciono o cenário Vendas, ao abrir o CRM (com o mesmo usuário que criou a trial, ou seja, o Admin), não vejo as demais áreas básicas do CRM na barra de navegação, como o Marketing e Serviços? Bom abaixo o que aconteceu:

 

Apenas para mostrar o cenário que eu escolhi:

Bom, após um pouco de navegação no próprio e pesquisa, percebi que agora temos um novo botão “Promover a Administrador”, quando estamos na área de segurança do CRM (Configurações > Segurança):

A funcionalidade foi introduzida no Update 1 do CRM 2016. E de fato é muito útil para adicionar e remover direitos de administrador de um ou mais usuários. Porém, o que eu não espera que por padrão, mesmo o usuário que criou o trial do CRM não seja um Administrador do CRM automaticamente!

Se selecionarmos o usuário e clicarmos em “Promover para Administrator”:

Ao confirmar e ATUALIZAR a página do navegador:

Todas ás áreas irão aparecer!!!

Não está claro do por que por padrão este é o comportamento (pelo menos para mim). Mas, fazendo as etapas acima, teremos o mesmo comportamento que já estávamos acostumados.

[]’s,

Tiago

Publicado em Dynamics 365 | Marcado com , , , | Deixe um comentário