Desenvolvendo componentes COM+ com .NET

Quando se trabalha com .NET em sistemas mistos com plataformas legadas, como ASP Classic, VB, VBScript, Delphi ou até mesmo Ole Automation do SQL Server, o .NET ainda permite interoperabilidade com estes sistemas por meio de componentes COM+, seja o .NET consumindo COM+ ou a plataforma legada consumindo um COM+ desenvolvido em .NET.

Como existem poucos artigos em português para explicar como desenvolver COM+ com .NET, procurei a demonstrar a criação de componente simples com C# no Visual Studio 2010.

Para a criação do componente, utilizei um projeto do tipo Class Library:

A este projeto adicionei referência à System.EnterpriseServices:

Nas propriedades do projeto, editei a “Assembly Information”,

especificando o Assembly como COM-Visible:

De acordo com a plataforma do sistema legado (x64, x86) que o COM+ irá trabalhar, defini esta em Plataform target:

Obs.: Esta opção é muito importante, pois é muito comum criar um componente em x64 e este não ser visível para uma aplicação VB x86, assim como tentar criar componentes x86 para tentar acessar por um VBScript que será executado em uma plataforma x64.

Por fim, criei uma chave “strong name” para o projeto:

Para uma classe .NET ser considerada componente, ela deverá ser herdada de System.EnterpriseServices.ServicedComponent, conforme abaixo, onde temos uma classe simples cujo o seu único método retorna um texto:

using System.EnterpriseServices;

namespace DemoCom
{
    public class Objeto : ServicedComponent
    {
        public string Teste()
        {
            return "Demo COM+ //By SQL From Hell";
        }
    }
}

Após realizar o build do projeto, utilizei o regsvcs de acordo com a plataforma x86/x64 e versão da .NET Framework do projeto, informando o caminho do seu assembly (DLL) onde esta “$(TargetPath)”.

C:\Windows\Microsoft.NET\Framework\v4.0.30319\regsvcs.exe $(TargetPath)

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\regsvcs.exe $(TargetPath)

Após a instalação do componente, este será visível na relação de COM+ Applications no Component Services:

Para testar o COM+, utilizarei um VBScript simples:

Dim obj
Set obj = CreateObject("DemoCom.Objeto")
MsgBox obj.Teste

Também sendo possível por Ole Automation do SQL Server:

DECLARE @obj INT

DECLARE @return INT, @text NVARCHAR(4000)

-- Criando a 'instância' do componentes

EXEC @return = sp_OACreate 'DemoCom.Objeto', @obj OUT

-- Verificando se a chamada obteve sucesso.

IF @return = 0 PRINT 'COM: OK'

-- Chamando o método Teste

EXEC @return = sp_OAMethod @obj, 'Teste', @text OUT

IF @return = 0 PRINT 'Teste: OK'

PRINT 'Resultado: ' + @text

E por fim, para desinstalar o componente, bastará utilizar o mesmo regsvcs informando “/u”.

C:\Windows\Microsoft.NET\Framework\v4.0.30319\regsvcs.exe /u $(TargetPath)

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\regsvcs.exe /u $(TargetPath)

Artigos relacionados:

Acessando dados de Web Service com Ole Automation Procedures

Anúncios

Acessando dados de Web Service com Ole Automation Procedures

Olá pessoas,

Como nem sempre será possível usar SQL CLR para acessar um Web Service no SQL Server 2005+, seja pelo fato de você estar trabalhando com um SQL Server 2000 ou por não permitirem usar SQL CLR em alguns cenários, nos restam poucas alternativas.

Uma alternativa “simples” e forma a manter este processo em T-SQL, é o uso de os componentes COM+ ou OLE (como preferir), com o qual teremos um script T-SQL um pouco estranho, inseguro e instável na maioria das vezes, semelhante a um script feito em VBScript ou ASP Classic.

Se não conhecem VBScript, abaixo segue um exemplo de script para ler o retorno de Web Service que recupera dados sobre a temperatura de uma determinada cidade:

Dim objHTTP, strResponse, url

set objHTTP = CreateObject("Microsoft.XMLHTTP")

'set objHTTP = CreateObject("Msxml2.XMLHTTP")

url = "http://www.webservicex.net/globalweather.asmx/GetWeather?CityName=CURITIBA&CountryName=BRAZIL"

objHTTP.Open "get", url, False

objHTTP.Send()

strResponse = objHTTP.responseText

MsgBox strResponse

No SQL Server, o uso de componentes COM+ é feito por procedures específicas, as Ole Automation Procedures, que não estão habilitadas por padrão (ainda bem). Logo se faz necessário habilitá-las, já que vamos fazer uso delas neste artigo:

EXEC sp_configure 'show advanced options', 1
GO
RECONFIGURE
GO
EXEC sp_configure 'Ole Automation Procedures', 1
GO
RECONFIGURE
GO
EXEC sp_configure 'show advanced options', 0
GO
RECONFIGURE
GO

Por padrão as Ole Automation Procedures são exclusivas do “sysadmin”, mas é possível dar permissão para executá-las aos usuários do banco de dados “master”, exemplo (para permissão destas procedures em outros bancos de dados, confira os artigos relacionados):

USE master
GO

CREATE LOGIN [Maria] WITH PASSWORD=N'1234'
, CHECK_EXPIRATION=OFF
, CHECK_POLICY=OFF
GO

CREATE USER [Maria] FOR LOGIN [Maria]
GO

GRANT EXECUTE ON sys.sp_OACreate TO [Maria]
GO

GRANT EXECUTE ON sys.sp_OAMethod TO [Maria]
GO

GRANT EXECUTE ON sys.sp_OADestroy TO [Maria]
GO

EXECUTE AS LOGIN = 'Maria'
GO

Para o nosso exemplo, vou utilizar a mesma ideia do que foi feito em VBScript, para recuperar a temperatura da cidade de Curitiba do Web Service pelo SQL Server:

DECLARE @objHTTP INT, @url VARCHAR(255)

DECLARE @return INT, @responseXml INT, @text NVARCHAR(4000), @xml XML

-- Criando a 'instância' do componentes

EXEC @return = sp_OACreate 'Microsoft.XMLHTTP', @objHTTP OUT

-- Componente alternativo

-- EXEC @return = sp_OACreate 'Msxml2.XMLHTTP', @objHTTP OUT

-- Verificando se a chamada obteve sucesso.

IF @return = 0 PRINT 'COM... OK'

SET @url = 'http://www.webservicex.net/globalweather.asmx/GetWeather?CityName=CURITIBA&CountryName=BRAZIL'

-- Chamando o método Open, informando a url

EXEC @return = sp_OAMethod @objHTTP, 'Open', NULL, 'GET', @url, 0

IF @return = 0 PRINT 'Open... OK'

-- Chamando o método Send

EXEC @return = sp_OAMethod @objHTTP, 'Send'

IF @return = 0 PRINT 'Send... OK'

-- Recuperando a resposta do Web Service

EXEC @return = sp_OAMethod @objHTTP, 'ResponseXML', @responseXml OUT

IF @return = 0 PRINT 'ResponseXML... OK'

-- Extraindo uma parte específica da resposta

EXEC @return = sp_OAMethod @responseXml, 'Text', @text OUT

IF @return = 0 PRINT 'Text... OK'

-- Recuperando a temperatura

SET @xml = CAST(@text AS XML)

SELECT @xml.value('(/CurrentWeather/Temperature)[1]', 'varchar(20)')

GO

Assim como qualquer outra feature, use-a com moderação, e esperamos que ela não venha mais existir em futuras versão do SQL Server já que pode ser facilmente e melhor substituida por SQL CLR.

Artigos relacionados:

Delegando permissões e acesso entre bancos de dados por meio de certificados no SQL Server

Delegando permissões e acesso entre bancos de dados por meio do Cross DB Ownership Chaining no SQL Server

Trabalhando com uma transação em várias conexões

Pode se tratar de uma feature deprecated (não existirá em edições futuras SQL Server), mas é bem útil quando você necessita em várias conexões (ou seções) compartilhar objetos/registros que estão com lock exclusivo por uma transação, ou iniciar uma transação em uma aplicação e concluir em outra aplicação.

A primeira procedure para esta solução é a sp_getbindtoken, que permite recuperar o token da sessão.

A segunda procedure é a sp_bindsession, permite em outra conexão utilizar o token recuperado da transação, para juntar as duas seções.

Para um exemplo simples, temos a seção 53, iniciando uma transação. Nesta transação, criamos uma tabela com dois registros, por fim, recuperamos o Token da sessão:

DECLARE @bind_token varchar(255)

BEGIN TRAN

CREATE TABLE T (
	ID INT
)

INSERT INTO T
VALUES (1),(2),(3)

EXEC sp_getbindtoken @bind_token OUTPUT

SELECT @bind_token

Numa nova seção (56), juntamos esta à seção 53 por meio do Token, para acessarmos a tabela criada na transação. E por fim damos ROLLBACK na transação iniciada na seção 53, por meio desta nova seção.

EXEC sp_bindsession 'c86>CI;@aAK;SNG1-3^@Y]5---/gG---'
GO

SELECT * FROM T

ROLLBACK

Mas é com pesar que perderemos esta feature nas futuras edições do SQL Server.

Agradecimentos ao Darlan O. da Luz pelo desafio.