Oi pessoal,
Este é mais um artigo da série sobre o BCS. Aprenda a criar ECTs via Assembly .NET no SharePoint 2010 através do Business Connectivity Services!
Esta abordagem irá tirar proveito da interface IContextProperty para obter os detalhes do BDC Connection definido no BDC Model.
Divirtam-se com a leitura!
Criação de ECTs via Assembly .NET
A criação de ECTs via Assembly .NET necessita apenas o VS2010. O SPD2010 não será usado.
Esta abordagem pode ser aplicada em cenários que:
-
Precisem da criação de regras de negócios, que podem ser desenvolvidas no BDC model;
-
Utilizem databases diferentes do SQL Server, que podem ser implementadas na camada de acesso a dados;
-
Exijam o deploy de pacotes WSP para a ativação de features, que é automaticamente gerado pelo VS2010;
Se você possui alguns destes cenários, esta implementação tem um maior nível de dificuldade pelo nível de detalhes na criação do assembly .NET, que é exibido nas próximas seções do artigo.
Trabalhando com o Visual Studio 2010
O template Business Data Connectivity Model será usado neste artigo. Inicie pela abertura do VS2010 e crie um novo projeto de acordo com a Figura 1:
Figura 1 – Criação de um novo projeto e solução
Na seqüência um Wizard aparece para ajudar no setup do projeto. A URL precisa ser validada para debugging, e a solução será criada como uma Farm Solution, de acordo com a Figura 2:
Figura 2 – Wizard
OBS: Uma vez que o projeto for criado, apague as classes Entity1 e Entity1Service. Não se esqueça de apagar a entidaded criada que está disponível no BDC Designer, pois novas classes serão geradas em um novo modelo.
Camada de Acesso a Dados
Pelo fato da feature gerar um pacote WSP, decidi por disponibilizar o modelo de acesso a dados no mesmo projeto. Para a manipulação do database é necessária a adição de alguns objetos, e para propósitos de demonstração usarei o LINQ to SQL pois ser mais simples de implementar.
Adicione o LINQ to SQL ao projeto e nomeie de Dev.dbml, então adicione uma nova conexão pelo Server Explorer que utilize Windows Authentication, abra o database e arraste a tabela Contact (Parte II)de acordo com a Figura 3:
Figura 3 – Adição da tabela Contact
Antes de manipularmos os dados da tabela Contact, uma classe que contenha métodos CRUD precisam ser criados. Crie a classe ContactManager e adicione o código abaixo, que está bem documentado:
Code Snippet
- public class ContactManager
- {
- /// <summary>
- /// Gets all the Contacts
- /// </summary>
- /// <returns>Contacts Array</returns>
- public Contact[] GetContacts()
- {
- var contacts = new List<Contact>();
-
- using (DevDataContext dev = new DevDataContext())
- {
- contacts = (from cont in dev.Contacts
- select cont).ToList();
- }
- return contacts.ToArray();
- }
-
- /// <summary>
- /// Gets a specific Contact
- /// </summary>
- /// <param name="contactId">Contact Id</param>
- /// <returns>Returns the Contact</returns>
- public Contact GetContactById(int contactId)
- {
- var contact = new Contact();
-
- using (DevDataContext dev = new DevDataContext())
- {
- contact = (from cont in dev.Contacts
- where cont.ContactID == contactId
- select cont).First();
- }
- return contact;
- }
-
- /// <summary>
- /// Updates a specific Contact
- /// </summary>
- /// <param name="contact">Contact to be updated</param>
- public void UpdateContact(Contact contact)
- {
- var contactDB = new Contact();
-
- using (DevDataContext dev = new DevDataContext())
- {
- contactDB = (from cont in dev.Contacts
- where cont.ContactID == contact.ContactID
- select cont).First();
-
- // Alters the object
- contactDB.Address = contact.Address;
- contactDB.City = contact.City;
- contactDB.CompanyName = contact.CompanyName;
- contactDB.ContactName = contact.ContactName;
- contactDB.ContactTitle = contact.ContactTitle;
- contactDB.Country = contact.Country;
- contactDB.Email = contact.Email;
- contactDB.Fax = contact.Fax;
- contactDB.Phone = contact.Phone;
- contactDB.PostalCode = contact.PostalCode;
- contactDB.Region = contact.Region;
-
- // Submitting the changes
- dev.Refresh(System.Data.Linq.RefreshMode.KeepChanges, contactDB);
- dev.SubmitChanges();
- }
- }
-
- /// <summary>
- /// Adds a Contact
- /// </summary>
- /// <param name="contact">New Contact</param>
- public void AddContact(Contact contact)
- {
- using (DevDataContext dev = new DevDataContext())
- {
- dev.Contacts.InsertOnSubmit(contact);
- dev.SubmitChanges();
- }
- }
-
- /// <summary>
- /// Deletes a Contacts
- /// </summary>
- /// <param name="contactId">Contact Id</param>
- public void DeleteContact(int contactId)
- {
- using (DevDataContext dev = new DevDataContext())
- {
- var contact = (from cont in dev.Contacts
- where cont.ContactID == contactId
- select cont).First();
- dev.Contacts.DeleteOnSubmit(contact);
- dev.SubmitChanges();
- }
- }
- }
OBS: Este mesmo código já foi explicado na criação de ECTs via Web Services na parte II, porém por ser importante nessa demo, o mesmo código foi reutilizado.
Crie uma partial class chamada DevDataContext, pelo fato de já termos essa classe gerada automaticamente pelo Visual Studio. Crie um novo construtor, de acordo com o código abaixo:
Code Snippet
- public partial class DevDataContext
- {
- public DevDataContext() :
- base(BdcModel1.ContactService.GetConnection(), mappingSource)
- {
- OnCreated();
- }
- }
OBS: O novo construtor contém uma chamada ao método GetConnection(), que pertence à classe ContactService e retorna uma connection string, obtida diretamente pelo BDC Metadata Model (veremos adiante).
BDC Model
O Modelo de Metadados será criado utilizando os novos painéis do BDC no Visual Studio. Será criado com base na Camada de Acesso a Dados, pelo fato de utilizarmos a entidade Contact.
Utilize o painel do BDC Designer, de acordo com a Figura 4:
Figura 4 – Adição da entidade Contact
Após a criação da entidade Contact, adicione um Identificador e nomeie de ContactID, também adicione os seguintes Métodos: GetConnection, ReadItem, ReadList, Delete, Update e Create. O menu de contexto nos ajuda nesse caso, conforme a Figura 5:
Figura 5 – Adição de Identificador e Métodos
Para adicionar e configurar métodos, a maneira mais conveniente é a utilização do painel BDC Method Details (pelo fato de gerar XML automaticamente) conforme Figura 6:
Figura 6 – Painel BDC Method Details
A Tabela 1 abaixo mostra um resumo das configurações iniciadas na Figura 6. Contendo todos os métodos e assinaturas:
Option | Method | Parameters | Direction | Type Descriptor |
Create Creator Method | Create | returnContact | Return | ReturnContact |
| | newContact | In | NewContact |
Create Deleter Method | Delete | contactID | In | ContactID |
Create Finder Method | ReadList | contactList | Return | ContactList |
Create Specific Finder Method | ReadItem | contact | Return | Contact |
| | contactID | In | ContactID |
Create Updater Method | Update | contact | In | Contact |
Create Blank Method | GetConnection | parameter | Return | connection |
Tabela 1 – Geração automática dos métodos da entidade Contact OBS: Por padrão um Blank Method criará um método chamado Method. Renomeie para GetConnection.
Antes de configurar os demais métodos, assegure-se de que o tipo do identificador de ContactID é System.Int32. Inicie pelo método ReadItem para configuração e clique sobre o parâmetro (conhecido como type descriptor) Contact, para a seleção de seu tipo conforme a Figura 7:
Figura 7 – Tipo do TypeDescriptor
O próximo passo é a adição de novos Type Descriptors ao Type Descriptor Contact através do painel BDC Explorer, que basicamente representa seus atributos. A Figura 10 mostra como adicionar Type Descriptors e a Tabela 2 seus detalhes:
Figura 8 – Adição de Type Descriptors
Type Descriptor | Tipo |
Address | System.String |
City | System.String |
CompanyName | System.String |
ContactID | System.Int32 |
ContactName | System.String |
ContactTitle | System.String |
Country | System.String |
Email | System.String |
Fax | System.String |
Phone | System.String |
PostalCode | System.String |
Region | System.String |
Tabela 2 – Tipos de Type Descriptors Quando criados, os Type Descriptors serão reutilizados automaticamente ao criar outros métodos. O código abaixo exibe o XML gerado automaticamente da entidade Contact (após a configuração de todos os métodos). Extraído do arquivo BdcModel1.bdcm:
OBS: O arquivo BdcModel1.bdcm pode ser visualizado em XML diretamente no Visual Studio, utilize o menu de contexto Open With... e então XML (Text) Editor.
BDC Model Connection
Os detalhes de conexão podem ser definidos dentro das propriedades do LobSystemInstance se você utilizar um database ou web service. Como isso se aplica no cenário dessa demo, temos o código abaixo:
Code Snippet
- <LobSystemInstance Name="BdcModel1">
- <Properties>
- <Property Name="AuthenticationMode" Type="System.String">PassThrough</Property>
- <!-- AuthenticationMode can be set to PassThrough, RevertToSelf, RdbCredentials, or WindowsCredentials. -->
- <Property Name="DatabaseAccessProvider" Type="System.String">SqlServer</Property>
- <!-- Options: SQL Server, OleDB, Oracle, or ODBC. -->
- <Property Name="RdbConnection Data Source" Type="System.String">W2008R2Sub1</Property>
- <!-- Type the database server name or the SQL Server instance name in the format SQLServer\Instance. -->
- <Property Name="RdbConnection Initial Catalog" Type="System.String">Dev</Property>
- <!-- Type the database name. -->
- <Property Name="RdbConnection Integrated Security" Type="System.String">SSPI</Property>
- <!-- Type SSPI for Integrated Security. -->
- <Property Name="RdbConnection Pooling" Type="System.String">false</Property>
- <!-- Type true or false for Pooling -->
- </Properties>
- </LobSystemInstance>
OBS: Se você quiser saber mais sobre os tipos de autenticação disponíveis através do BCS, verifique a seção Referências. Basicamente o XML acima contém propriedades comumente usadas na criação de connection strings.
Métodos da Entidade do BDC Model
Durante a criação dos métodos através dos painéis do BDC, o Visual Studio ao mesmo tempo foi gerando automaticamente a classe ContactService e seus respectivos métodos. Eles ainda não estão implementados, pois estão gerando a exceção do tipo NotImplementedException.
Antes de codificarmos, você se lembra do código que contém o método GetConnection? Ele precisa retornar a connection string de um database, mas como? Exploraremos as propriedades do LobSystemInstance definidas previamente.
Mas como ler o XML que contém essas propriedades? Fácil! Apenas implemente a interface IContextProperty na classe ContactService e o BDC se encarrega do resto.
OBS: Quando um assembly possui uma classe que implementa IContextProperty, suas propriedades são inicializadas automaticamente no momento que o assembly é executado no BDC. Isso é uma mão na roda!
Adicione a referência ao assembly Microsoft.Business, que está disponível no diretório 14: \ISAPI\Microsoft.BusinessData.dll.
Observe o código abaixo, que exibe a classe ContactService:
Code Snippet
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using ContactServices.BdcModel;
- using Microsoft.BusinessData.SystemSpecific;
- using Microsoft.BusinessData.MetadataModel;
- using Microsoft.BusinessData.Runtime;
- using Microsoft.BusinessData.MetadataModel.Collections;
-
- namespace ContactServices.BdcModel.BdcModel1
- {
- public partial class ContactService : IContextProperty
- {
- private static IMethodInstance methodInstance;
- private static ILobSystemInstance lobSystemInstance;
- private static IExecutionContext executionContext;
-
- #region IContextProperty implementation
-
- public IMethodInstance MethodInstance
- {
- get { return methodInstance; }
- set { methodInstance = value; }
- }
-
- public ILobSystemInstance LobSystemInstance
- {
- get { return lobSystemInstance; }
- set { lobSystemInstance = value; }
- }
-
- public IExecutionContext ExecutionContext
- {
- get { return executionContext; }
- set { executionContext = value; }
- }
-
- #endregion
-
- public static Contact ReadItem(int contactID)
- {
- // Implement your own business rules
- return new ContactManager().GetContactById(contactID);
- }
-
- public static IEnumerable<Contact> ReadList()
- {
- // Implement your own business rules
- return new ContactManager().GetContacts();
- }
-
- public static Contact Create(Contact newContact)
- {
- if (string.IsNullOrEmpty(newContact.ContactName) || string.IsNullOrEmpty(newContact.Email) || string.IsNullOrEmpty(newContact.Phone))
- throw new ObjectNotFoundException("ContactName, Email and Phone are mandatories.");
-
- var manager = new ContactManager();
- manager.AddContact(newContact);
- return manager.GetContactById(newContact.ContactID);
- }
-
- public static void Update(Contact contact)
- {
- if (string.IsNullOrEmpty(contact.ContactName) || string.IsNullOrEmpty(contact.Email) || string.IsNullOrEmpty(contact.Phone))
- throw new ObjectNotFoundException("ContactName, Email and Phone are mandatories.");
-
- new ContactManager().UpdateContact(contact);
- }
-
- public static void Delete(int contactID)
- {
- // Implement your own business rules
- new ContactManager().DeleteContact(contactID);
- }
-
- public static string GetConnection()
- {
- INamedPropertyDictionary properties = lobSystemInstance.GetProperties();
-
- string template = "Data Source={0};Initial Catalog={1};Integrated Security={2};";
-
- string dataSource = "RdbConnection Data Source";
- string initialCatalog = "RdbConnection Initial Catalog";
- string integratedSecurity = "RdbConnection Integrated Security";
-
- if (!properties.ContainsKey(dataSource) || !properties.ContainsKey(initialCatalog) || !properties.ContainsKey(integratedSecurity))
- throw new Exception("LobSystemInstance does not contain a connection string");
-
- return string.Format(template, properties[dataSource].ToString(), properties[initialCatalog].ToString(), properties[integratedSecurity].ToString());
-
- }
- }
- }
Em exceçào ao método GetConnection, todos os outros chamam métodos CRUD da classe ContactManager. Ainda, nos métodos Create e Update existe uma validação de campos obrigatórios ContactName, Email e Phone, que não podem ser nulos ou vazios pois geram uma exceção do tipo ObjectNotFoundException.
Como esperado, o método GetConnection obtém as propriedades de LobSystemInstance, que contém os detalhes de conexão do BDC, e retorna a connection string utilizada no construtor da Partial Class DevDataContext.
Criando o Pacote de Deploy
O deploy de pacotes WSP no mundo real é feito ou manualmente ou utilizando scripts, e não diretamente com a intervenção do Visual Studio. Estou apenas explicando isso para propósitos de demonstração e entendimento do STSADM.
Antes de gerar o pacotes WSP, mais algumas configurações são necessárias. Renomeie a feature para BdcModelFeature e adicione a propriedade SiteUrl no arquivo XML BdcModelFeature.Template.xml, de acordo com a Figura 9:
Figura 9 – Propriedades da Feature
Finalmente gere o pacote WSP ao selecionar Package no menu de contexto do Visual Studio. Pegue o pacote no diretório Bin e utilizando o STSADM faça o deploy utilizando os seguintes comandos:
stsadm -o addsolution -filename "ContactServices.BdcModel.wsp"
stsadm -o deploysolution -name "ContactServices.BdcModel.wsp" -allowGacDeployment –immediate
stsadm -o execadmsvcjobs
stsadm -o installfeature -filename "ContactServices.BdcModel_BdcModelFeature\feature.xml" –force
stsadm -o activatefeature -name "ContactServices.BdcModel_BdcModelFeature"
OBS: Faça o download da solução aqui.
Configurando a External List
Nesse estágio já é possível a criação da External List que proverá uma interface visual para os dados externos no SharePoint 2010. Tanto pelo SharePoint Designer quanto pela interface do SharePoint é possível a criação de External Lists, na Parte II mostrei como utilizar o SharePoint Designer para essa tarefa, e agora vou mostrar como utilizar a interface visual do SharePoint para isso.
Vá até o web site e acesse Site Actions > More Options. A Figura 10 exibe um modal dialog para selecionarmos a External List:
Figura 10 – Seleção da External List
Então escolha o External Content Type ContactServices.BdcModel.BdcModel1.Contact e crie a Lista Contacts, conforme exibido na Figura 11:
Figura 11 – Criação da External List Contacts
Depois disso oteremos a Lista Contacts criada. É só isso? Não, precisamos definir as permissões no BDC Service Application para este Assembly, do contrário obteremos a seguinte tela:
Figura 12 – Acesso negado pelo Business Data Connectivity
Definindo as Permissões do BDC
Após o deploy do Assembly .NET, precisamos definir permissões apropriadas no BDC Service Application para que os usuários visualizem os dados externos. Vá ao BDC Service Application em Central Administration e encontre o Assembly .NET:
Figura 13 – Procurando o .NET Assembly
Então defina os usuários que possuirão permissão para visualizar o conteúdo dos dados externos, conforme a Figura 14:
Figura 14 – Definindo permissões no BDC
Ao fazer isso, dê um refresh na External List (Figura 12) e você obterá os dados externos.
Esse artigo foi de um nível mais avançado pessoal, daqui para frente vocês estarão aptos a criarem seus próprios ECTs via Assemblies .NET.
A série sobre o BDC ainda nào está finalizada, da próxima vez falarei sobre como interfacear ECTs com Client Applications. Até a próxima!
Referências:
http://msdn.microsoft.com/en-us/library/ee556826(v=office.14).aspx
http://msdn.microsoft.com/en-us/library/microsoft.businessdata.systemspecific.icontextproperty(office.14).aspx
[]'s
Marcel Medina
Clique aqui para ler o mesmo conteúdo em Inglês.