CRM – Criar um UII Aplication Adapter no USD


Pessoal,

Neste post, pretendo criando um exemplo com detalhes do que é, como criar e utilizar um UII Aplication Adapter (Adaptador para interagir com um Aplicação) no universo do Unified Service Desk (USD)!

Bom, primeiro devemos entender para que utilizar um Adapter…

Dentro de nossa operação de call center onde temos o USD sendo utilizado, podemos necessitar em muitas ocasiões uma interação com outras aplicações, como por exemplo um ERP, uma aplicação Web ou um sistemas Windows. porém esta aplicações não devem ser alteradas, devem continuar da forma que estão. (veja que a intenção é não mudar nada que já existe e sim aproveitar tudo que já foi construído!) Para isso, podemos criar um Adapter entre a aplicação “legada” com o USD. Deste modo, podemos recuperar informações do contexto do atendimento e passá-las para alguma aplicação externa ao USD, acessar a aplicação externa e recuperar informações que serão utilizadas no USD e muitas outras variantes das opções anteriores.

Ficou alguma dúvida? Vejam o artigo oficial: Use UII adapters to interact with external and web applications.

Então um adapter pode ser criado para gerenciar aplicações stand-alone ou Web!!!

Este post foi baseado no material oficial: Walkthrough: Create a UII Application Adapter

Vamos a um cenário de uso com exemplo. Criei um aplicação muito simples, um antigo Windows Forms com alguns campos, labels e um botão:

uii_adapter_1

Esta aplicação representa um sistema externo que queremos manipular através do USD, neste exemplo, irei recuperar informações de contexto, mais especificamente os dados os usuário logado e inserir neste formulário. Um ponto importante, é o último elemento deste formulário, uma label com o valor “VALOR ANTES DO SUBMIT”. Quando o botão Submit é pressionado o valor simplesmente é alterado para “VALOR APÓS O SUBMIT! OK!!!”. Nós iremos automatizar o clique, por isso precisava demonstrar o que acontece quando clicarmos:

uii_adapter_2

Faça o Build da aplicação. Copie o executável gerado (localizado na pasta bin) para a pasta onde foi instalado o USD, normalmente seria: “C:\Program Files\Microsoft Dynamics CRM USD\USD”.

Como nossa aplicação criada, vamos registrá-la no USD, precisaremos criar um hosted control para inicializar a aplicação (Configurações > Unified Service Desk > Hosted Controls > Novo:

uii_adapter_3

uii_adapter_4

 

Poucas informações são necessárias, apenas:

  • Nome – Nome que o hosted control será identificado
  • USD Component Type – CCA Hosted Control
  • Hosted Application – External Hosted Application
  • Application is Global – Marcar a caixa de seleção (neste caso, queremos que o hosted control seja inicializado junto com o USD, pois iremos pegar valores do contexto do usuário logado
  • Diplay Panel – MainPanel
  • External App URI – O nome do executável de nossa aplicação, no meu caso é “WindowsFormsApplication.exe”

A primeira parte deste post terminou, criamos uma aplicação e ela será inicializada em uma aba do USD quando ele for aberto. É claro que a aplicação na maioria dos casos vocês já possuem, certo? Então vamos para o que interessa, o UII Application Adapter!

Existem alguns pré-requisitos que devemos fazer antes de programar, seguem:

O artigo de referencia da Microsoft que foi utilizado neste post, não detalha problemas, que faz com que tenhamos o pré-requisito versões inferiores do WINDOWS 10, seguem minhas considerações/constatações:

No cenário que foi criado para este post, que envolve o uso de uma aplicação Windows externa. Segundo o artigo oficial, o uso do Inspector (ferramenta de mapeamento de controles) para o Visual Studio, deve ser selecionado o DDA (Data Driven Adapter) apropriado que neste caso seria o “WindowsDataDrivenAdapter“.

Em todos os testes realizados por mim (utilizando as versões 2012, 2013 e 2015 do Visual Studio e WINDOWS 10), em nenhum momento foi possível utilizar o componente Inspector para mapear os controles que desejamos interagir no WINDOWS 10, mesmo quando realizei o mapeamento em outro sistema operacional, as automações realizadas não surtiram efeito quando testadas em máquinas WINDOWS 10.

Como workaround, realizei o mapeamento dos controles fazendo o uso de uma máquina WINDOWS 7, ao qual funcionou perfeitamente, bem como, o funcionamento das automações.

Deste modo, neste momento, posso dizer que o mapeamento utilizando o Inspector e o funcionamento das automações está impactado para o sistema operacional WINDOWS 10, eu também acredito que para as versões 8 e 8.1 exista o mesmo problema (não foi testado). Acredito que a API do Windows tenha sofrido alguma mudança que devido a isso não conseguimos utiliza-la.

Após a devida instalação dos componentes dos pré-requisitos, vamos no Visual Studio uma ExternalApplication e um UII Application Adapter!

Abra o Visual Studio, clique em “Arquivo > Novo > Projeto… > Templates > Visual C# > UII > ExternalApplication”:

uii_adapter_6

Clique em “OK”. Será, necessário informar onde encontra-se o executável que será nossa aplicação externa:

uii_adapter_7

Clicando em “OK”, podemos ver o projeto criado. Vejam que possuimos alguns nós no arquivo “Initstring.xml”:

uii_adapter_8

Agora, devemos criar nossos mapeamentos dos controles, clique com o botão direito em cima do nome do projeto em seguida em Inspect:

uii_adapter_9

Ao clicar, nossa aplicação será aberta, bem como o Inspector, altere o DDA (Data Driven Adapter) para WindowsDataDrivenAdapter, pois estamos utilizando uma aplicação externa Windows:

uii_adapter_10

Para capturar os controles, posicione o ponteiro do mouse no controle que seja recuperar e pressione o “CTRL”, um caixa amarela irá aparecer ao redor do controle, veja que o temos um XML carregado no binding. Recomendo alterar o nome do controle para facilitar a recuperação depois. Por fim, clique em “Add Control”:

uii_adapter_11

uii_adapter_12

Repita o mesmo procedimento para os dois outros textboxes e para o botão. No final teremos como valores do DataDrivenAdpterBidingCollection, algo parecido com isso:

uii_adapter_13

Não se esqueça deste XML, iremos precisar dele à seguir, por enquanto deixe-o parado.

Devemos criar o projeto do UII Application Adapter!

Na mesma solution que já criamos, crie um novo projeto, clicando com o botão direito no nome da solução e navegando em “Adicionar… > Novo Projeto… > Templates > Visual C# > CRM SDK Templates > Unified Service Desk > UII Application Adapter”:

uii_adapter_14

uii_adapter_15

*Caso as referencias do projeto não tenham sido localizadas, podemos utilizar as DLL’s dentro da pasta Bin do SDK de UII.

Devemos criar duas variáveis globais, criar override do método Initialize, ajustar o método NotifyContextChange e criar nossa codificação no método DoAction:

uii_adapter_16*Da mesma forma que disse para as outras DLL’s. Pegue a referencia das DLL’s “Microsoft.Uii.HostedApplicationToolkit.DataDrivenAdapter” e “Microsoft.Xrm.Tooling.Connector”, na pasta Bin do SDK de UII.

uii_adapter_17uii_adapter_18

Vou incluir o código também e depois faço alguns comentários:

// =====================================================================
//  This file is part of the Microsoft Dynamics CRM SDK code samples.
//
//  Copyright (C) Microsoft Corporation.  All rights reserved.
//
//  This source code is intended only as a supplement to Microsoft
//  Development Tools and/or on-line documentation.  See these other
//  materials for detailed information regarding Microsoft code samples.
//
//  THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
// =====================================================================

using System.Diagnostics;
using Microsoft.Uii.Csr;
using Microsoft.Uii.Desktop.Core;
using Microsoft.Uii.HostedApplicationToolkit.DataDrivenAdapter;
using Microsoft.Uii.AifServices;
using System;
using System.Collections.Generic;

namespace UIIApplicationAdapter
{
    public class AppAdapter : ApplicationAdapter, IDesktopUserActionsConsumer
    {
        #region Vars
        ///

<summary>
        /// Pointer to the Uii Desktop System 
        /// </summary>


        private IDesktopUserActions uiiDesktop;

        protected Context _context;
        protected DataDrivenAdapterBase _dda;

        #endregion

        #region Constructor

        ///

<summary>
        /// Default Constructor to wire up process event. 
        /// </summary>


        public AppAdapter()
        {
            // Called any time a process change event occurs. 
            this.AdapterProcessChangedEvent += new AdapterProcessChanged(AppAdapter_AdapterProcessChangedEvent);
        }

        #endregion

        public override bool Initialize()
        {
            //Initialize Data Driven Adapter with proper top level window handle
            _dda = DataDrivenAdapterBase.CreateInstance(ApplicationInitString, this.DdaApplicationObject);
            return _dda != null;
        }

        #region Uii Methods

        ///

<summary>
        /// Context change notification
        /// </summary>


        /// <param name="context">New Context</param>
        public override bool NotifyContextChange(Context context)
        {
            _context = context;
            return base.NotifyContextChange(context);
        }

        ///

<summary>
        /// Action Handler for Uii
        /// </summary>


        /// <param name="args">Event Args.</param>
        /// <param name="action">App Adapter Action</param>
        /// <returns></returns>
        public override bool DoAction(Microsoft.Uii.Csr.Action action, RequestActionEventArgs args)
        {
            switch (args.Action)
            {
                case "default":

                    // Get currently Agent properties and Set in Context
                    this.SetAgentPropertiesInContext();

                    if (_dda.FindControl("MSAA:txtNome"))
                    {
                        _dda.SetControlValue("MSAA:txtNome", _context["systemuser.fullname"], "");
                    }

                    if (_dda.FindControl("MSAA:txtTelefone"))
                    {
                        _dda.SetControlValue("MSAA:txtTelefone", _context["systemuser.address1_telephone1"], "");
                    }

                    if (_dda.FindControl("MSAA:txtEmail"))
                    {
                        _dda.SetControlValue("MSAA:txtEmail", _context["systemuser.internalemailaddress"], "");
                    }

                    if (_dda.FindControl("MSAA:btnSubmit"))
                    {
                        _dda.ExecuteControlAction("MSAA:btnSubmit", false, "");
                    }

                    break;
            }

            return base.DoAction(action, args);
        }

        #endregion

        #region IDesktopUserActionsConsumer Members

        ///

<summary>
        /// Raised when the desktop loading is complete
        /// </summary>


        /// <remarks>
        /// this is only called on Global Hosted Controls
        /// </remarks>
        public void DesktopLoadingComplete()
        { }

        ///

<summary>
        /// Provides access to system level Uii Functions
        /// </summary>


        /// <param name="desktopAccess"></param>
        public void SetDesktopUserActionsAccess(IDesktopUserActions desktopAccess)
        {
            if (uiiDesktop == null)
                uiiDesktop = desktopAccess;
        }

        #endregion

        #region User Code here

        ///

<summary>
        /// Get currently Agent properties and Set in Context
        /// </summary>


        private void SetAgentPropertiesInContext()
        {
            // Get Agent ID
            var desktopFeatureAccess = AifServiceContainer.Instance.GetService<IAgentStateService>();
            Guid agentID = desktopFeatureAccess.GetAgentId();

            // Get in the Context CRM Service Client
            var clientService = AifServiceContainer.Instance.GetService<AuthenticationService>().CrmServiceClient;

            // Get Data for the currently Agent
            Dictionary<string, object> data = clientService.GetEntityDataById("systemuser", agentID, null);

            // Loop all properties
            foreach (var pair in data)
            {
                // Fullname
                if (pair.Key == "fullname")
                {
                    _context["systemuser.fullname"] = pair.Value.ToString();
                }

                // Main Phone
                if (pair.Key == "address1_telephone1")
                {
                    _context["systemuser.address1_telephone1"] = pair.Value.ToString();
                }

                // Email
                if (pair.Key == "internalemailaddress")
                {
                    _context["systemuser.internalemailaddress"] = pair.Value.ToString();
                }
            }
        }

        ///

<summary>
        /// Called when a process change occurs
        /// </summary>


        /// <param name="process">Updated Process.</param>
        private void AppAdapter_AdapterProcessChangedEvent(Process process)
        {
            //** Your code **//
        }

        #endregion

    }
}

Vou comentar apenas o que tive que incluir e alterar…

  • As variáveis “_dda” e “_context”, são responsáveis pelos objetos do Adapter e do Contexto respectivamente, colocando elas de forma global, consegui manipula-las dentro dos meus métodos. Fiquem à vontade para outras abordagens, o importante é a ideia.
  • O override do método “Initialize” para capturarmos o objeto do Adapter e utilizamos seus métodos e propriedades.
  • O ajuste no método “NotifyContextChange” é simples, pego o contexto que pode ser atualizado a qualquer momemento por qualquer controle do USD e atribuo a nossa variável global “_context”.
  • O método “DoAction” é o mais importante de todos, nele interceptamos uma Action Call que foi disparada e adicionamos nossa lógica. Neste exemplo, eu deixei a Action Call “default”, ele já é criada para qualquer controle e tem um adicional ela é sempre executada na inicialização do USD, ou seja, nossa lógica será executada junto com a abertura do USD.
    • Vejam que recuperamos os controles que foram definidos no XML de nossa ExternalApplication, veja que temos um prefixo que deve ser respeitado para acessar os controles;
    • Existe uma chamada ao método “SetAgentPropertiesInContext”, sua descrição será feita no próximo item;
  • O método “SetAgentPropertiesInContext” foi criado para recuperar o Agente do Contexto, pesquisar outras propriedades no CRM como Nome Completo, Telefone e Email e por fim, adicioná-las no Contexto do USD, assim podemos recuperá-las através de qualquer controle

É isso, UII Application Adapter concluído, faça o Build da aplicação. Copie a DLL gerada (localizado na pasta bin) para a pasta onde foi instalado o USD, normalmente seria: “C:\Program Files\Microsoft Dynamics CRM USD\USD”.

Bom temos nossa External Application e nosso UII Application Adapter construídos! Vamos apenas voltar ao CRM e atualizar nosso hosted control que foi criado no início deste post!

uii_adapter_19

uii_adapter_20

Apenas devemos incluir nosso Adapter e o XML da External Application, altere para:

  • Adapter – Use Adapter
  • URI – O nome da DLL do Adapter, no meu caso “UIIApplicationAdapter”
  • Type – O nome da Classe do Adapter, no meu caso “UIIApplicationAdapter.AppAdapter”
  • Automation XML – Copie todo o conteúdo do elemento DataDrivenAdpterBidingCollection de nossa External Application, ou seja, todos os “nós” do elemento

Salve o formulário e abra o USD!!!

uii_adapter_21

Nosso aplicativo foi inicializado com o USD, todos os campos foram preenchidos de acordo com o Agente que está conectado (no caso eu mesmo!) e o botão “Sumit” foi clicado, pois o valor apresentado é “VALOR APÓS O SUBMIT! OK!!!’.

Vale lembrar que podemos recuperar valores da aplicação, utilizar outros métodos que não seja o click de um botão, fazer uma aplicação externa trabalha com outra aplicação e inúmeras outras possibilidades!

Post grande, mas acredito que tenha uma boa quantidade de detalhes!

Mais alguns links oficiais que utilizei para construir este post:

Use data driven adapters (DDAs)
Use UII inspector to create bindings for the hosted application
Use CrmServiceClient constructors to connect to CRM
Use XRM tooling to retrieve data

[]’s,

Tiago Cardoso

 

 

 

 

 

2 comentários em “CRM – Criar um UII Aplication Adapter no USD

  1. Olá Tiago,

    Gostei desse seu post, está completo com vários detalhes porém, ao tentar realizar em um POC me deparei com erro de conflito entre DLL´s, você chegou a se deparar com isso?
    Estou usando VS 2012, com o CRM SDK 2016 e USD 2.0.2


    System.IO.FileLoadException was unhandled by user code
    HResult=-2146234304
    Message=Não foi possível carregar arquivo ou assembly ‘Microsoft.Xrm.Tooling.Connector, Version=1.0.0.0,

    Culture=neutral, PublicKeyToken=31bf3856ad364e35’ ou uma de suas dependências. A definição do manifesto do assembly

    localizado não corresponde à referência do assembly. (Exceção de HRESULT: 0x80131040)
    Source=UIIApplicationAdapter
    FileName=Microsoft.Xrm.Tooling.Connector, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
    FusionLog==== Informações sobre estado pré-associação ===
    LOG: DisplayName = Microsoft.Xrm.Tooling.Connector, Version=1.0.0.0, Culture=neutral,

    PublicKeyToken=31bf3856ad364e35
    (Fully-specified)
    LOG: Appbase = file:///C:/Program Files/Microsoft Dynamics CRM USD/USD/
    LOG: PrivatePath inicial = NULL
    Chamando assembly: UIIApplicationAdapter, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
    ===
    LOG: esta associação começa no contexto de carregamento default.
    LOG: usando arquivo de configuração de aplicativo: C:\Program Files\Microsoft Dynamics CRM USD\USD

    \UnifiedServiceDesk.exe.Config
    LOG: usando arquivo de configuração de host:
    LOG: usando arquivo de configuração da máquina de C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config

    \machine.config.
    LOG: referência pós-política: Microsoft.Xrm.Tooling.Connector, Version=1.0.0.0, Culture=neutral,

    PublicKeyToken=31bf3856ad364e35
    LOG: tentando download de nova URL file:///C:/Program Files/Microsoft Dynamics CRM

    USD/USD/Microsoft.Xrm.Tooling.Connector.DLL.
    AVI: a comparação de nome de assembly resultou na incompatibilidade: Major Version
    ERR: falha ao concluir configuração do assembly (hr = 0x80131040). Probing encerrado.

    StackTrace:
    em UIIApplicationAdapter.AppAdapter.SetAgentPropertiesInContext()
    em UIIApplicationAdapter.AppAdapter.DoAction(Action action, RequestActionEventArgs args)
    em Microsoft.Uii.Csr.ExternalApplication.DoAction(RequestActionEventArgs args)
    InnerException:

    Curtir

    1. Olá Daniel,

      Este seu erro está na compilação ou na execução do USD?

      Confirme se realmente todas as referencias de DLL’s estão presentes em seu projeto, depois verifique a versão (32 ou 64 bits).

      Quando fiz este post usei CRM 2016, VS 2015 e USD 2.0.

      []’s,
      Tiago

      Curtir

Deixe um comentário

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