Olá pessoal,
Nativamente existem apenas dois tipos diferentes de Provedores de Dados (Data Providers) disponíveis para as Entidades Virtuais:
- Solution Component Data Provider
- OData v4 Data Provider
É claro que fazer uso deles, facilita bem uma integração. Porém muitas vezes, precisamos consultar outras fontes de dados com formatos/protocolos diferentes ou simplesmente não é viável modificar a API para um formato mais moderno como OData v4. Assim, precisamos partir para a criação de um provedor de dados personalizado. Como criá-lo, registrá-lo e testá-lo, serão os objetivos desta série de três posts!
Um provedor customizado não é nada mais do que um tipo de plug-in que implementa as mensagens de Retrieve (individual / form) e RetrieveMultiple (lista / grids). O plugin irá acessar a API externa, transformando o retorno da consulta em uma BusinessEntity para o Retrieve e uma BusinessCollection para o RetrieveMultiple (ambas as classes são tipos de objeto que o Dynamics já conhece) e deixando o Dynamics se encarregar de “montar” a camada visual para nós!
Para aqueles que ainda não viram meu post anterior, recomendo a leitura, pois existem tópicos que eu irei utilizar neste post.
Novamente irei utilizar a entidade Contatos e a entidade virtual ExternalActivity que utilizei no meu último post.
Bom, vamos ao que interessa!
API
Se você já possui uma API que segue as limitações das entidades virtuais, vá para a próxima etapa “Provedor de Dados Customizado“.
Já que estamos criando um provedor customizado, irei criar um WCF que retorna JSON, talvez seja o cenário mais provável para as integrações entre entidades virtuais e o Dynamics.
Aqui vai um printscreen to projeto no Visual Studio para ajudar a se localizar:
Abaixo minha Model (ExternalActivity.cs), veja que estou usando a mesma do post anterior, com isso não precisarei criar uma nova entidade virtual:
using System; namespace DemoJSON { public class ExternalActivity { public Guid ExternalActivityId { get; set; } public int ExternalActivityKey { get; set; } public Guid ContactId { get; set; } public string Subject{ get; set; } public string Details { get; set; } public string From { get; set; } public string To { get; set; } } }
Agora a Interface de Serviço (IJSONService.cs):
using System.Collections.Generic; using System.ServiceModel; using System.ServiceModel.Web; namespace DemoJSON { // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together. [ServiceContract] public interface IJSONService { [OperationContract] [WebInvoke(UriTemplate = "ExternalActivity/{externalActivityId}", Method = "GET", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] List<ExternalActivity> ExternalActivity(string externalActivityId); [OperationContract] [WebInvoke(UriTemplate = "ExternalActivities/{contactId}", Method = "GET", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] List<ExternalActivity> ExternalActivities(string contactId); } }
Acima, existem dois métodos, um para ser utilizado no Retrieve (ExternalActivity, que recebe como parâmetro externalActivityId) e outro para o RetrieveMultiple (ExternalActivities, que recebe como parâmetro contactId).
Vamos a implementação da interface com sua lógica (JSONService.cs):
using System; using System.Collections.Generic; using System.Linq; namespace DemoJSON { public class JSONService : IJSONService { public List<Guid> listExternalActivitiesIDs = new List<Guid>(); public JSONService() { listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED041")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED042")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED043")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED044")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED045")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED046")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED047")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED048")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED049")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED050")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED051")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED052")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED053")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED054")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED055")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED056")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED057")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED058")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED059")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED060")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED061")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED062")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED063")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED064")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED065")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED066")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED067")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED068")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED069")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED070")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED071")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED072")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED073")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED074")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED075")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED076")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED077")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED078")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED079")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED080")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED081")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED082")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED083")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED084")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED085")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED086")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED087")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED088")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED089")); listExternalActivitiesIDs.Add(new Guid("86B77A42-2EBF-4144-A570-0E51B69ED090")); } public List<ExternalActivity> ExternalActivity(string paramExternalActivityId) { List<ExternalActivity> ExternalActivities = new List<ExternalActivity>(); for (int i = 0; i < 1000; i++) { Guid contactId = Guid.NewGuid(); Guid externalActivityId = Guid.NewGuid(); if (i < 50) { contactId = new Guid("C4BA7D70-347B-E811-A961-000D3AE05F14"); externalActivityId = listExternalActivitiesIDs[i]; } ExternalActivities.AddRange(new List<ExternalActivity> { new ExternalActivity() { ExternalActivityId = externalActivityId, ExternalActivityKey = i, ContactId = contactId, From = RandomString(10), To = RandomString(10), Subject= RandomString(20), Details = RandomString(100), } }); } if (!string.IsNullOrEmpty(paramExternalActivityId)) { return ExternalActivities.Where(m => m.ExternalActivityId.Equals(new Guid(paramExternalActivityId))).ToList(); } else { return ExternalActivities; } } public List<ExternalActivity> ExternalActivities(string paramContactId) { List<ExternalActivity> ExternalActivities = new List<ExternalActivity>(); for (int i = 0; i < 1000; i++) { Guid contactId = Guid.NewGuid(); Guid externalActivityId = Guid.NewGuid(); if (i < 50) { contactId = new Guid("C4BA7D70-347B-E811-A961-000D3AE05F14"); externalActivityId = listExternalActivitiesIDs[i]; } ExternalActivities.AddRange(new List<ExternalActivity> { new ExternalActivity() { ExternalActivityId = externalActivityId, ExternalActivityKey = i, ContactId = contactId, From = RandomString(10), To = RandomString(10), Subject= RandomString(20), Details = RandomString(100), } }); } if (!string.IsNullOrEmpty(paramContactId)) { return ExternalActivities.Where(m => m.ContactId.Equals(new Guid(paramContactId))).ToList(); } else { return ExternalActivities; } } static Random random = new Random(); public static string RandomString(int length) { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray()); } } }
Linhas destacadas (9 à 63, 77 e 117): vejam que criei uma lista fixa (hardcoded) do objeto ExternalActivity, para garantir que os métodos irão possuir os mesmos IDs, na “vida real”, você já deve possuir os IDs únicos!
O método ExternalActivity recebe um id de ExternalActivity e retorna uma lista de ExternalActivity, embora o método retorne apenas uma lista com um item, mantive desta forma para podermos reaproveitar parte do código quando estivermos criando o provedor de dados para o Retrieve e RetrieveMultiple.
Já o método ExternalActivities recebe um id de um Contato e retorna uma lista de ExternalActivity.
Bom fim, a configuração do 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 name="ServiceBehavior"> <serviceMetadata httpGetEnabled="true"/> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> <endpointBehaviors> <behavior name="ServiceAspNetAjaxBehavior"> <webHttp /> </behavior> </endpointBehaviors> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/> <services> <service behaviorConfiguration="ServiceBehavior" name="DemoJSON.JSONService"> <endpoint address="" behaviorConfiguration="ServiceAspNetAjaxBehavior" binding="webHttpBinding" contract="DemoJSON.IJSONService"> <identity> <dns value="localhost" /> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> </services> </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>
API em JSON criada, vamos para o provedor de dados customizado! Este será meu próximo post, não percam!
Para maiores detalhes, seguem os documentos oficiais:
Custom virtual entity data providers
Sample: Generic virtual entity data provider plug-in
[]’s,
Tiago