1 Comentário

Sobre Abstração de Bancos de Dados em Aplicações .NET

Estive fazendo a migração da base de dados de um projeto ASP.Net do Oracle para o SQL Server. A princípio acreditei que, por termos utilizado a tecnologia do Entity Framework 3.5, o trabalho seria, simplesmente, trocar o provider EF do Oracle para o SQL Server. No entanto, por várias razões que eu citarei ainda neste artigo, foi bem mais complicado do que isso.

Intrigado pela dificuldade de lidar com o Entity Framework 3.5 neste cenário, comecei a buscar alternativas que fossem capazes de realizar a total abstração do banco de dados para o ambiente .NET.

Depois de muitas leituras, fiquei surpreso ao perceber que a única tecnologia capaz de fazer isso de forma eficiente é, de fato, o Entity Framework. Os dados que me levaram a esta conclusão, assim como as demais tecnologias testadas por mim, estão descritos abaixo.

1. DbProviderFactory (System.Data.Common).

Depois de uma conversa com o Ivan Kurt, descobri a existência do DbProviderFactory. O DbProviderFactory é uma classe que surgiu no .NET 2.0 que abstrai as classes ADO.Net de acesso ao banco de dados (EntityProvider, Odbc, OleDb, OracleClient e SqlClient). Isso quer dizer que, ao invés de utilizar essas classes específicas para cada banco de dados na sua camada de acesso a dados, você utiliza o DbProviderFactory. Este, por sua vez, através do polimorfismo e de uma chave definida no arquivo de configuração da aplicação, criará o provider correto para acessar o banco de dados, de forma transparente. Sim, exatamente como no padrão de design “Factory”. Sim, não é coincidência.

Fiquei entusiasmado com a ideia e cheguei a implementar um DbProviderFactory para o SQLite em um dos meus programas. No entanto, após finalizá-lo, percebi que, mesmo utilizando o meu provider factory, uma grande quantidade de trabalho poderia ser necessária para convertê-lo para, digamos, Oracle.

Isto porque, no final das contas, você TEM que especificar o comando SQL para a propriedade DbCommand.CommandText. Isso pode até funcionar bem entre bancos que suportam Stored Procedures, onde o comando de chamada pode permanecer sempre o mesmo. Não é o caso do SQLite, e de outras dezenas de banco de dados. Não é uma solução genérica.

O melhor artigo que eu encontrei sobre o assunto (ótimo, diga-se de passagem), inclusive com o passo a passo sobre como construir o seu próprio DbProviderFactory, está aqui:
http://msdn.microsoft.com/en-us/library/ms971499.aspx

Resumo sobre o DbProviderFactory:

  • Pontos Fortes:
    • Bem estruturado e elegante, baseado simplesmente no design pattern Factory;
    • Funciona bem e é suportado pelo Mono (http://www.mono-project.com/Compatibility);
    • Suportado desde a versão 2.0 do .NET Framework;
    • Pode ser utilizado até mesmo no Membership Provider (deve existir um DbProviderMembershipProvider).
  • Pontos Fracos:
    • Exige que o comando SQL seja especificado na propriedade CommandText;
    • Ajuda, mas não é a solução final para generalização de banco de dados.


2. Data Source Controls

Os Data Source Controls são abordados pelo mesmo artigo do MSDN mencionado acima para o DbProviderFactory. A sua grande vantagem em relação ao DbProviderFactory é que os comandos para CRUD já estão embutidos nos componentes (SqlDataSource, AccessDataSource, ObjectDataSource, XmlDataSource, e SiteMapDataSource).

Também é possível criar uma Factory manualmente para os Data Source Controls utilizando-se a interface IDataSource e um pouco de reflection para a criação dinâmica dos controles. No entanto, ele é bastante engessado no que diz respeito aos códigos para acesso aos dados (queries T-SQL por exemplo). No artigo o autor utiliza esses controles em uma camada que realmente não parece uma camada de acesso a dados. Me parece que eles foram feitos mais para prover dados de forma simples para páginas e grids.

Um grande ponto negativo desses controles é que a pesquisa no banco de dados é feita de forma assíncrona, isto é, você precisa especificar uma função de callback para tratar os dados retornados, o que deixa a idéia de utilizá-los em uma camada de acesso a dados ainda mais bizarra. No entanto, aqui vai o resumo sobre os Data Source Controls:

  • Pontos Fortes:
    • Facílimo de configurar/utilizar;
    • Pode ser generalizado para deixar o banco de dados completamente transparente para o sistema;
    • Suportado desde a versão 2.0 do .NET Framework;
    • Funciona bem e é suportado pelo Mono.
  • Pontos Fracos:
    • Performance um tanto quanto duvidosa pela falta de parâmetros para configuração do controle;
    • Comandos são executados de forma assíncrona (difícil utilização em camadas de acesso a dados);
    • Não oferece muito controle sobre os comandos executados (não suporta joins ou qualquer query mais complexa);
    • Os comandos precisam ser pré-embutidos nos controles (complexidade alta para criar, digamos, um SQLiteDataSource);
    • Não parece ser uma solução pensada para ser utilizada em uma camada de acesso a dados.


3. .NET Entity Framework

Voltamos ao .NET Entity Framework. Das três, esta é a tecnologia com a qual eu mais trabalhei. Como mencionei anteriormente, acabei de converter um sistema de complexidade média que foi desenvolvido utilizando o Entity Framework Provider Oracle para o SQL Server. Encontrei diversos entraves e problemas, mas grande parte deles foram por questões alheias à tecnologia em si. Pessoalmente, acredito que esta tecnologia seja a única resposta factível para a total abstração do banco de dados utilizado para uma aplicação .NET.

Antes de enumerar os pontos fortes e fracos da tecnologia, gostaria de enumerar os problemas que tive na troca do banco de dados do sistema de Oracle para SQL Server:

  • Várias chaves estrangeiras não estavam definidas (ou estavam definidas de outra forma) no banco de dados em relação ao modelo do Entity Framework (edmx). Isto fez com que diversas propriedades acessíveis através dessas chaves (ex: Funcionario.Perfis) não ficassem disponíveis e gerassem centenas de erro em tempo de compilação.
  • Vários tipos de campo foram alterados na conversão do banco de dados. Alguns campos que estavam definidos como NUMBER no Oracle foram remodelados para INT no SQL Server, o que fez com que o engine do Entity Framework transformassem todas as propriedades do tipo Decimal (baseadas em NUMBER) para o tipo Int32 (baseadas em INT). Isto também pode ter ocorrido por conta de o driver Entity Framework para Oracle ter sido feito por mim a partir de um exemplo na Internet e em tempo recorde. No entanto, o tipo NUMBER do Oracle aceita casas decimais e, portanto, nunca poderia ser traduzida como Int32 para o .NET. Este é um exemplo de como os DBAs precisarão, cada vez mais, manter a padronização de tipos entre bancos de dados diferentes (nas raras ocasiões em que isto for necessário).
  • Também precisei editar o XML do arquivo .edmx para alterar os tipos nativos de cada campo (VARCHAR2 para VARCHAR, NUMBER para INT/NUMERIC, etc). Não sei dizer, mais uma vez, se isto foi necessário por conta de algo que não foi implementado no meu driver EF para Oracle ou se isso é algo que realmente sempre precisará ser feito, ou ainda se isso foi algo que não estava implementado na versão 3.5 (beta) do Entity Framework e que agora na versão 4.0 foi implementada.

Bem, com isso fora do caminho, podemos partir para os pontos fortes e fracos do Entity Framewok:

  • Pontos Fortes:
    • Extremamente robusto e integrado ao Visual Studio;
    • Queries são geradas dinamicamente (feature que, pelo que dizem, melhorou muito na versão 4.0 e melhorou mais ainda na versão 4.5);
    • Abstrai completamente o banco de dados com o mínimo (ou talvez nenhum) esforço;
    • Possui uma extensão LINQ já desenvolvida pra ela (LINQ to Entities);
    • Parece ser o futuro (recebeu bastante atenção da equipe do ADO.Net na versão 4.0 e mais ainda na 4.5).
  • Pontos Fracos:
    • Ainda pouco difundida;
    • Na versão 3.5, não suporta trabalhar com POCO (Plain Old CLR Object).
    • Na migração de banco de dados, exige que o banco de dados possua tipos de dados compatíveis e não tenha mudanças em chaves estrangeiras (OK, é justo);
    • Não possui drivers gratuitos para muitos bancos (No entanto, existem drivers gratuitos para MySQL e SQLite);
    • Só é suportado pelo .NET Framework a partir da versão 3.5 SP1;
    • Não é suportado pelo Mono (http://www.mono-project.com/Compatibility).

Bem, é isso!

Abraços!

Deixe um comentário

Pensamento por Reflexão e Base 64

À primeira vista, pode parecer que as perguntas “para quê isso serve?” e “por quê isso existe?” têm o mesmo significado, mas, pelo menos pra mim, elas são indagações bem diferentes. Consideremos, por exemplo, o sistema de codificação Base 64 (baseado, obviamente, no sistema numérico de base 64).

“Para quê o sistema de codificação Base 64 serve?”. Essa pergunta é um exemplo do que eu chamo de linha de pensamento objetiva, e a sua resposta, portanto, é a seguinte:

“É um sistema de codificação baseado no sistema numérico de base 64 que visa representar dados binários em formato texto utilizando os caracteres da tabela ASCII mais comuns entre diversos sistemas”.

“Por quê o sistema de codificação Base 64 existe?”. Já essa pergunta é um exemplo do que eu chamo de linha de pensamento por reflexão, e, para mim, é aí que todas as coisas começam a ficar interessantes…

Imagine, por um momento, que você nunca ouviu falar no esquema de codificação em Base 64, e que você se encontra na mesma situação na qual eu me encontrava há 4 anos: Eu precisava desenvolver um controle ActiveX para rodar embutido em uma página web, que se comunicaria com um digitalizador de mesa (scanner) e enviaria os dados binários das imagens digitalizadas de volta para o servidor, através de um post-back ou coisa parecida.

O grande problema, nesse e em muitos outros casos, era que o meio-de-campo entre o ActiveX, a página e o servidor teria de ser feito com JavaScript. No entanto, como vocês já devem imaginar, não é possível transferir arranjos (arrays) entre JavaScript e ActiveX, o que já nos limita bastante, pois um binário é, basicamente, um arranjo de bytes (Byte[]). Além disso, mesmo que fosse possível transferir tal arranjo de alguma forma do ActiveX para o JavaScript, como seria possível enviá-lo do JavaScript para o servidor? A solução, em ambos os casos, é uma só: representar o binário de uma outra maneira, preferencialmente através de uma string.

O grande problema na representação de um binário em uma string é que a string interpreta os valores ASCII que são armazenados nela. Isto é, se o binário possuir um byte com o valor 10 (dez), a string irá interpretar esse caractere como uma quebra de linha (LF = Line Feed). Se o binário possuir um byte com o valor 0 (zero), a string entenderá como o seu fim. Se o binário possuir um caractere de valor 8 (oito), a string irá interpretar esse valor como um backspace e, de fato, irá sumir com o caractere anterior!

Dessa forma, a maneira mais simples imaginada por mim na época foi representar o valor do byte (que varia de 0 a 255) através do texto simples do valor do byte, isto é: o valor 246 viraria “246″. Alguns problemas surgem imediatamente após a idéia dessa abordagem: como saber se a string “20165″ na verdade significa 2, 0 e 165, ou 20 e 16 e 5? Poderíamos separar com algum caractere, por exemplo “2,0,165″ ou ainda poderíamos fixar o número de bytes por byte em 3, de maneira que a string ficaria “002000165″. Entretanto, a desvantagem desse método é um tanto óbvia: o tamanho do binário representado desta forma é, simplesmente, multiplicado por 3.

Mas espera aí… de 0 a 255 é um número conhecido em hexadecimal até para quem só trabalha com cores em HTML: 0 = 0×00 e 255 = 0xff. Então eu poderia representar os valores 2, 0 e 165 como simplesmente “0200a5″! Genial! Dessa forma, o tamanho do binário não seria multiplicado por 3, mas apenas por 2! Ainda assim, dá aquele peso no coração ver o nosso arquivo de 5MB se transformar em 10MB de texto hexadecimal. Não é um algoritmo eficiente. Nesse sistema, eu estou precisando de 8 bits (1 byte) para representar apenas 4 bits (1 nibble).

No entanto, quem disse que estamos presos ao sistema hexadecimal (base 16)? De repente podemos utilizar um sistema de base 32, de 0 a 9 e de ‘a’ a ‘x’. Assim eu representaria 5 bits com 8 bits, o que já é um ganho. Aliás, podemos fazer melhor do que isso! Podemos utilizar os caracteres de ’0′ a ’9′, de ‘a’ a ‘z’, de ‘A’ a ‘Z’ e mais os caracteres ‘+’ e ‘/’ para conseguir uma base numérica de 64 caracteres e representar 6 bits com 8 bits! É claro que, como somos inteligentes, tomamos o cuidado de escolher os 64 caracteres da tabela ASCII mais comuns entre todos os sistemas digitais, aumentando sensivelmente o suporte potencial à nossa Base 64!

Pronto, é por isso que o sistema de codificação em Base 64 existe :)

Esse sistema ainda aumenta o nosso binário em 25% em relação ao seu tamanho original, mas garante que uma grande gama de sistemas, softwares e dispositivos que suportaram inicialmente somente texto, passem a suportar também conteúdo binário. Esse sistema foi amplamente utilizado na época das BBSs, e ainda é bastante utilizado por softwares e protocolos (POP, SMTP, clientes de e-mail, imagens embutidas em e-mail, representação textual de conteúdo criptografado (incluindo senhas, chaves de criptografia, assinaturas digitais, MD5, SHA1, etc), data URIs, binários embutidos em XML, entre outros).

É claro que ainda poderíamos diminuir um pouco a gama de sistemas suportados e utilizar um sistema de codificação em Base 128, por exemplo, utilizando 8 bits para representar 7 bits do binário. No entanto, é um tanto difícil encontrar mais 64 caracteres legíveis na tabela ASCII, principalmente porque essa tabela, originalmente, só utilizava 7 bits para a definição dos caracteres.

Deixe um comentário

IE9 com aceleração por hardware mais lento que com aceleração por software

Neste fim de semana formatei minha nova máquina e, como parte dos updates básicos de segurança do Windows 7 x64, instalei o IE9. Para a minha surpresa, o IE9 estava extremamente lento, até mesmo na rolagem de páginas. Assim sendo, desinstalei o update do IE9 e testei o IE8. Um avião. Baixei a instalação do IE9 para instalá-lo manualmente, e, após a instalação, pude constatar a mesma lentidão obtida anteriormente.

Procurei em diversos lugares na internet, mas não encontrei nenhuma informação sobre este problema. Atualizei o driver da placa de vídeo (uma GeForce 540M), mas de nada adiantou. Então resolvi desabilitar a aceleração por hardware do IE9 para diagnosticar se o problema era realmente a placa de vídeo, e, para a minha surpresa, era! O IE9 ficou extremamente veloz com a aceleração por hardware desabilitada.

Procurei novamente no Google por qualquer informação que ligasse o meu modelo de placa de vídeo, ou até mesmo a NVidia, com esse problema do IE9, mas só achei algumas referências de problemas com as versões beta que, aparentemente, tinham sido resolvidas na versão final (que era a que eu estava instalando).

Por fim, resolvi desabilitar algumas funcionalidades da placa de vídeo a fim de detectar qual era o problema. Foi então que eu lembrei que a minha placa de vídeo tinha suporte a renderização em 3D. Após desabilitar essa opção, o IE9 finalmente funcionou como era esperado: extremamente rápido na renderização de páginas.

Então fica a dica: se você enfrentar problemas no IE9 com placas de vídeo com suporte a 3D, desabilite essa opção ou deixe habilitada a renderização por software no IE9.

Deixe um comentário

O Open Data Protocol (OData)

Lembro-me de certa vez estar discutindo com o Fernando Morais (um colega de trabalho) sobre como seria interessante a utilização de objetos JSON para a transmissão de dados, pois eles não possuem toda aquela sobrecarga de tags e metadados inerentes ao XML e, consequentemente, aos protocolos de WebService baseados em XML como o SOAP.

Após o surgimento do conceito de RESTfull WebServices – que são nada mais do que WebServices que utilizam protocolos mais “lights” do que os protocolos atuais de WebServices – eu sempre tive a impressão de que estávamos a um passo do surgimento de um protocolo baseado em JSON para a transmissão de dados via WebServices.

Eis que, novamente, o mundo de TI não nos decepcionou e surgiu o Open Data Protocol (OData), que é mais do que um protocolo de transmissão de dados em JSON: é um protocolo para criação, atualização, exclusão e recuperação de dados de um repositório de dados qualquer, em JSON!

Mais do que rapidamente, surgiram artigos no MSDN Magazine sobre como utilizar o BCS conectado a um WebService utilizando o OData e como trabalhar com o jQuery integrado ao OData. Muito interessante.

Deixe um comentário

Técnica para Gerar CAML Programaticamente

Eu gostaria compartilhar com vocês uma técnica para gerar CAML programaticamente que tem me salvado várias vezes. O CAML é chato de entender e mais chato ainda de tentar gerar, seja na mão ou programaticamente.

Essa técnica foi desenvolvida por mim durante a criação do Iteris.Framework e tem se mostrado muito útil desde então. Ela é extremamente simples e permite adicionar ou remover campos à vontade, em qualquer momento, em qualquer posição.

Tudo começa com um StringBuilder que será usado para gerar o CAML e uma variável de contador de campos:

StringBuilder clausulaWhere = new
StringBuilder();
Int32 contadorCampo = 0;

E então começa a brincadeira. Ela pode ser, por exemplo, um conjunto de possibilidades de ID de, neste exemplo, Tipo de Documento (um conjunto de ORs, ou, no T-SQL, o famoso operador IN ()):

// Monta a cláusula CAML <Where> intercalando os <Or>s.
foreach (SPListItem tipoDocumento in tiposDocumento) {
clausulaWhere.Append(“<Eq>”);
clausulaWhere.Append(“<FieldRef Name=\”TipoDocumento\” LookupId=\”TRUE\”/>”);
clausulaWhere.Append(“<Value Type=\”Lookup\”>”);
clausulaWhere.Append(tipoDocumento.ID.ToString());
c
lausulaWhere.Append(“</Value>”);
clausulaWhere.Append(“</Eq>”);
if (++contadorCampo > 1) {
clausulaWhere.Insert(0, “<Or>”);

clausulaWhere.Append(“</Or>”);

}

}

Depois nós podemos querer filtrar apenas os que são documentos (sem as pastas) desses tipos de documentos…

// Filtra somente documentos (sem pastas).
clausulaWhere.Append(“<Eq><FieldRef Name=\”FSObjType\”/><Value Type=\”Text\”>0</Value></Eq>”);
if (++contadorCampo > 1) {
clausulaWhere.Insert(0, “<And>”);
clausulaWhere.Append(“</And>”);
}

Podemos também exigir que os documentos encontrados sejam de uma unidade e/ou de uma área específica, caso o usuário tenha informado a área…

// Adiciona os filtros de Unidade e Área
if (ddlArea.SelectedIndex > 0) {
clausulaWhere.Append(“<Eq>”);
clausula
Where.Append(“<FieldRef Name=\”Unidade\” LookupId=\”TRUE\”/>”);
clausulaWhere.Append(“<Value Type=\”Lookup\”>”);
clausulaWhere.Append(ddlArea.SelectedValue);
clausulaWhere.Append(“</Value>”);
clausulaWhere.Append(“</Eq>”);
if (++contadorCampo > 1) {
clausu
laWhere.Insert(0, “<And>”);
clausulaWhere.Append(“</And>”);
}
}

clausulaWhere.Append(“<Eq>”);
clausulaWhere.Append(“<FieldRef Name=\”Area\” LookupId=\”TRUE\”/>”);
clausulaWhere.Append(“<Value Type=\”Lookup\”>”);
clausulaWhere.Append(item["Area"].ToString().Split(‘;’)[0]);
clausulaWhere.Append(“</Value>”);
clausulaWhere.Append(“</Eq>”);
if (++contadorCampo > 1) {
clausulaWhere.Insert(0, “<And>”);
clausulaWhere.Append(“</And>”);
}

Finalmente, inserimos tudo dentro das tags <Where> para que possamos executar a query…

if (clausulaWhere.Length > 0) {
clausulaWhere.Insert(0, “<Where>”);
clausulaWhere.Append(“</Where>”);
}

query.Query = clausulaWhere.ToString();
query.ViewFields = “<FieldRef Name=\”ID\” />”;
query.Folder = web.GetFolder(item.Url).ParentFolder;
items = lista.GetItems(query);

Voilà!

Deixe um comentário

5 Dicas de Ouro para o Desenvolvimento em SharePoint 2007

1 – A classe estática Microsoft.SharePoint.Utilities.SPUtility possui MUITOS membros interessantes. Eu ia listar aqui os mais interessantes, mas acabei notando que, na verdade, TODOS são interessantes. Então vou colocar o link para a página de membros no MSDN. Leia, você não vai se arrepender. Quer dizer, talvez vá se arrepender de ter reinventado a roda anteriormente.

http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.utilities.sputility_members(v=office.12).aspx

2 – Edição de página escondida: Essa é velha conhecida, mas quando você quiser editar uma página do SharePoint 2007 e a opção “Editar Página” não estiver disponível ou desabilitada, tente informar a seguinte QueryString na URL: ToolPaneView=2.

3 – As propriedades Built-In dos objetos do SharePoint. Coisas extremamente úteis quando não se pode prever a linguagem com a qual se está trabalhando ou quando um objeto não possui um ID fixo. Eu destacaria os seguintes:

- SPBuiltInFieldId.<Constante com o ID de campos nativos>
- SPBuiltInContentTypeId.<Constante com o ID de content types nativos>
- SPWeb.Site*, são eles:
 - SPWeb.SiteAdministrators
 - SPWeb.SiteGroups
 - SPWeb.SiteLogoDescription
 - SPWeb.SiteLogoUrl
 - SPWeb.SiteUserInfoList (Essa propriedade é fantástica! Não existe outra maneira simples de obter essa lista no escopo da web, nem de adivinhar o nome e nem o ID da lista no site raiz)
 - SPWeb.SiteUsers

4 – Os controles pré-prontos do Namespace Microsoft.SharePoint.WebControls. Existem preciosos controles lá que não são muito conhecidos. O último deles que eu utilizei foi o ListViewByQuery, que permite montar uma view com todas as facilidades de ordenação e filtros de uma lista, mas é baseada em uma query CAML para montar o seu resultado. Isto permite utilizar operações como o “contains” em um determinado campo da lista.

5 – Faça certo da primeira vez: Quando estiver trabalhando com WebParts, Page Layouts e Master Pages, invista um tempo definindo as descrições e os grupos corretos para cada objeto e cada feature. Após o primeiro deploy, seja via Visual Studio ou via linha de comando, este conteúdo não será sobrescrito pelos próximos deploys e as descrições temporárias poderão se tornar definitivas sem que você perceba.

Deixe um comentário

Reproduzindo Áudio via Browser – O Problema

Estou trabalhando em um cliente Web para controlar o meu robô Spykee em .NET. Este cliente é uma aplicação web baseada em HTML 5, multiplataforma (Windows/Linux) e focada nos browsers mais recentes (IE 9, Firefox 4, Chromium).

Antes de começar a trabalhar neste projeto eu estipulei vários requisitos sobre o funcionamento esperado deste cliente e até agora tenho conseguido manter todos, mesmo com as dificuldades do padrão HTML 5 que será tema de um outro post mais tarde. Mas estou começando a perceber que, pelo menos por enquanto, terei de abrir mão de alguns requisitos porque (PASMEM) o .NET Framework me deixou na mão. Isto porque uma das funcionalidades do meu robô é a possibilidade de envio e recebimento de áudio (VoIP) em tempo real, isto é, em streaming, o que eu descobri ser uma área bastante desleixada na tecnologia atual, conforme explicarei adiante.

Ainda pensando em como eu resolveria este problema de envio e reprodução de áudio, resolvi simplificar a minha vida e utilizar aquela velha técnica da engenharia de “dividir um problemão em probleminhas menores”, e foquei inicialmente na reprodução de áudio enviado do meu robô para o meu cliente Web. O que eu preciso nesse caso, tecnicamente falando, é enviar um buffer de áudio (array de bytes) em formato bruto (RAW) a 16KHz, 1 Canal, de 4000 em 4000 bytes, enviados a cada 0,125 segundos (4000 * 1 / 0,125 = 32000 bytes) para um dispositivo de áudio presente no computador cliente. Como o formato é bruto, não preciso de nenhum codec ou driver especial. Seria algo simples como, por exemplo, enviar esse buffer para o dispositivo /dev/audio* no Linux. É engraçado a esta altura imaginar o quanto isso deveria ser simples mas o quanto os engenheiros do Windows complicaram essa função no sistema operacional. Isso sem falar que eu precisaria reproduzir esse conteúdo via browser, o que adiciona mais um grau de complexidade no projeto.

Depois de muitas horas de pesquisa e diversos testes em diversas situações, creio que estas são TODAS as tecnologias atuais que te permitem reproduzir áudio no browser (não necessariamente são soluções multiplataforma):

 1 – A tag do HTML – Pela microsoft está “deprecated” pela utilização da tag <object>. Os outros browsers não interpretam corretamente a tag <object> (apesar de estar definido no padrão HTML). Alguns testes funcionaram bem, mas este método depende do player instalado na máquina e deixa a responsividade do browser muito ruim, tornando inviável utilizá-lo com outras funções (como andar com o robô). O IE não suporta a utilização de data URIs com este elemento e a única forma de fazê-lo funcionar é imprimir seguidamente tags via javascript no corpo da página. De vez em quando era possível ouvir algumas interrupções por conta do alto grau de atualização do conteúdo (1 vez a cada 0,125 segundos, ou 8 vezes por segundo). Utilização do processador muito alta por este método. 

2 – A tag <object> do HTML – Funcionamento parecido com a tag , só que não é suportado por outros browsers que não o IE. É possível especificar um player para reproduzir o áudio.

3 – A tag <bgsound> do IE – Funciona como as duas anteriores, só suporta arquivos WAVE e só é suportada pelo IE.

4 – Silverlight utilizando a classe System.Media.SoundPlayer – À primeira vista achei que todos os meus problemas estavam resolvidos. Uma classe que aceita um Stream com o conteúdo do áudio e possui um método Play(), 100% suportado pelo .NET Framework e, portanto, multiplataforma (Mono Moonlight)! Mas depois de montar toda a infra-estrutura do código notei que o som saía “picado”, isto é, era interrompido a cada 0,125 segundos. Depois de várias modificações na tentativa de melhoria da performance (inclusive instanciando mais de uma thread com mais de uma instância da classe SoundPlayer iniciando de forma intercalada), desisti. Pesquisei na internet e descobri que a terrível performance de execução dessa classe é algo conhecido e bastante dicutido nos fóruns de .NET, tornando essa solução que parecia tão promissora inviável.

5 – Silverlight utilizando o elemento MediaElement – Também parecia uma boa alternativa multiplataforma, mas só suporta reproduzir MP3 e WMA. Existe uma maneira de escrever um driver específico para fazê-lo tocar formato bruto RAW ou WAV (que são de fácil conversão entre um e outro), mas não sei qual é o tamanho da encrenca. Talvez vire um projeto maior do que o projeto de fato.

6 – Silverlight utilizando APIs do Windows – A partir deste ponto, a solução passa a deixar de ser multiplataforma. Parece que ninguém até hoje criou uma camada de abstração prestável para os dispositivos multimídia da máquina. No entanto, esta não é uma solução válida, pois o Silverlight é LIMITADÍSSIMO em suas classes .NET e, além disso, não consegue chamar APIs windows.

7 – WPF utilizando as APIs do Windows – Esta parece ser uma alternativa viável, mas ainda não fiz testes. Talvez o usuário tenha de instalar o WPF a partir de um pacote XBAP. As APIs do Windows são mais complicadas de mexer, mas a performance é a melhor possível. O cliente nativo do robô, feito em C++, importa as funções de WaveOut do arquivo winmm.dll, o que nos faz perceber que eles também utilizaram essas APIs para capturar/reproduzir o áudio.

8 – ActiveX utilizando as APIs do Windows – O velho ActiveX já me salvou algumas vezes, inclusive na comunicação com um scanner, e acho que me salvará mais essa. É necessário a instalação do ActiveX na máquina local, mas é o preço a se pagar por uma interatividade maior com o cliente. Não é multiplataforma e outras alternativas teriam de ser desenvolvidas para fazer funcionar no Linux.

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.