Está chegando a hora

20/08/2010

Sei que está demorando um pouco para a ValeLigado iniciar oficialmente as atividades. Ninguém mais do que eu quer que isso aconteça o mais rápido possível, mas não tenhamos pressa.

A ValeLigado ainda nem está oficialmente funcionando, mas já existem diversos contatos para futuros projetos sendo feitos e esses contatos deverão render bastante trabalho logo no começo das atividades.

Bom, aguardem mais um pouco. Estamos com uma previsão de início das atividades para antes do início de Outubro, ou seja, pouco mais de um mês.

Fiquem ligados.


Wildcard no SharePoint – Se você ainda não precisou, um dia vai precisar.

20/08/2010

Em muitos momentos, é necessário que uma busca avançada do SharePoint funcione com “meias palavras” ou seja, dando a possibilidade de utilizar wildcard nas pesquisas.

Não é uma tarefa muito fácil se você quiser customizar a sua busca para habilitar essa função. Porém, nossos amigos do codeplex já tiveram esse trabalho e disponibilizaram uma solução interessantíssima e muito boa.

Veja a solução aqui e mais informações extremamente relevantes aqui.


Mais um Hotfix do Visual Studio 2010

10/08/2010

Esse hotfix resolve um problema de uma mensagem de falta de memória quando se tenta fazer um copy / paste no Visual Studio 2010.

Mais informações e download do Hotfix, veja aqui: https://connect.microsoft.com/VisualStudio/Downloads/DownloadDetails.aspx?DownloadID=29729


Hotfix Visual Studio 2010

10/08/2010

Pessoal,

Saiu um hotfix para o VS 2010 que resolve um problema no qual o VS dá para de funcionar quando alteramos para a view de design e a página contém diversos controles em uma região de edição como por exemplo, uma View ou um Panel.

Para baixar e atualizar o Visual Studio clique aqui.


Um bom sistema depende, também, de um banco de dados bem estruturado

28/07/2010

Nesses meus anos de trabalho com desenvolvimento de sistemas, tenho visto que cada vez menos as empresas se preocupam em estruturar um banco de dados corretamente.

Hoje em dia, boa parte dos desenvolvedores querem trabalhar com SharePoint que é a programação do momento. Porém, muitos desses desenvolvedores não sabem dizer se um sistema terá mais qualidade se desenvolvido em uma linguagem ou em outra.
Nem sempre, SharePoint é a melhor solução para o desenvolvimento de um sistema.

Outro ponto é que, quando se monta um site, ou portal, ou seja lá o que for no SharePoint, (não generalizando, mas em boa parte deles) o “desenvolvedor” entra em uma parte administrativa e fica clicando nos links para criar um novo site, criar uma nova página, adicionar uma webpart, etc, etc, etc. Nisso, ele não faz a mínima ideia de como isso está entrando no banco de dados (e muitas vezes nem tem vontade de saber) e não sabe nem como é estruturado um banco de dados para SharePoint.

Vamos deixar uma coisa clara: Não sou contra o SharePoint. Muito pelo contrário. Defendo muitas utilizações dessa ferramenta. Um exemplo básico é um portal de colaboração. Se fosse montar um portal de colaboração no .NET por exemplo, pode ter certeza que você teria muito trabalho, mas muito mesmo. Já no SharePoint, o negócio fica bem simples, ainda mais que existem diversos templates que precisariam de pouquíssimas customizações para ficar da maneira como você gostaria. Alie tudo isso a um bom Designer e você poderá ter portais ou até sites de internet com uma qualidade excepcional.

Mas o que estou discutindo aqui é o seguinte: Desenvolvedores, que trabalharam só com desenvolvimento nativo de SharePoint e depois dizem que tem experiência em bancos de dados. Isso é absurdo!

Esses dias, me deparei com uma base de dados (desenvolvido por um programador .NET se a mínima experiência em Banco de Dados e depois ajustada por um desenvolvedor SharePoint que julga saber Banco de Dados) que tinha a mesma informação em 3 tabelas diferentes, não havia relacionamentos e ainda existiam tabelas que não estavam sendo utilizadas para nada. Esse banco serviria para uma parte de um portal desenvolvido em SharePoint e essa parte é totalmente desenvolvida com webparts 100% customizadas em .NET para atender a necessidade do projeto. Conclusão, informações desencontradas, perda de informações importantíssimas e performance deplorável. Aí vem o chefe e te pergunta: “O que precisamos fazer para arrumar esses problemas?”. E claro, a resposta: “Primeiro estruturar direito esse banco de dados. Estruturando corretamente, pode ter certeza que 99% do que foi desenvolvido vai para o lixo. Em outras palavras, precisamos desenvolver do zero.”. Claro que em projeto atrasado, a sugestão não foi aceita.

Bom… Vamos voltar ao foco do nosso post. Um bom sistema desenvolvido em algum ferramenta que não seja SharePoint, é 90% uma boa estrutura de banco e de sistema. Os outros 10% é a qualidade da lógica de programação do(s) seu(s) desenvolvedor(es).

Vamos colocar em ordem macro o que devemos nos atentar bastante no desenvolvimento de um projeto.

1. Modelagem do banco de dados
Se você monta um banco de dados muito bem estruturado (que não é simples, mas essencial), você previne alguns erros que os desenvolvedores podem vir a cometer, além de diminuir o tempo do seu desenvolvimento do sistema, aumentar a sua performance, a possibilidade de expansão estruturada do seu sistema, etc.
Perca um pouco mais de tempo na modelagem do seu banco para não perder muito mais tempo no final do projeto para corrigir os problemas que poderiam (e deveriam) ter sido resolvidos com algum tempo a mais olhando para o seu banco.

2. Estrutura de classes do projeto
Aqui faço uma pergunta: Pra que criar uma estrutura extremamente complexa, com diversas camadas se o seu sistema não terá integração com nenhum outro sistema e acessará apenas um banco tipo de banco de dados?
Veja bem: Se existe a possibilidade de esse seu sistema um dia acessar outro tipo de banco de dados ou ter integração com algum outro sistema, aí sim você precisa mesmo pensar em algo mais complexo, mas em boa parte dos projetos que participei, esses não eram os casos.
Se você cria uma estrutura extremamente complexa para um sistema extremamente simples, corre-se o risco de perder muito, mas muito tempo mesmo no meio do projeto em diversos casos, como por exemplo, a troca de um recurso de desenvolvimento. Até o recurso novo entender a estrutura inteira do projeto para a simplicidade do resultado que o projeto vai oferecer lá se foram dias ou até semanas de tempo do projeto.
Não estamos dizendo que não temos priorizar a qualidade, mas estou dizendo que temos que priorizar o máximo da simplicidade que o projeto permita. Isso pode aumentar sensivelmente a qualidade do seu projeto, levando em consideração que provavelmente nem todos os recursos tem a mesma experiência que você.
O que realmente tem que ser muito bem pensando em todos os projetos que se iniciam é a estrutura base de classes para poder amparar as classes mais específicas, como as classes de webforms por exemplo. Perca um pouco mais de tempo aqui também para prevenir duplicação de código, facilitar a manutenção e também melhorar o tempo de desenvolvimento do meio para o final do projeto.
Um exemplo aqui seria uma classe base que fornece os perfis do usuário logado para todas as classes de formulários, métodos comuns, globalization, localization, e por aí vai.

3. Teste, teste e teste novamente
Nessa fase, muitos gerentes de projeto erram em um ponto crucial do projeto. Nem sempre o teste é mais rápido do que o tempo de desenvolvimento. Um exemplo. Suponhamos que temos um relatório que mostrará valores diferentes, dependendo de onde o dado foi colocado e do perfil do usuário que colocou o dado em cada um dos lugares em poderia. O desenvolvedor vai pensar em tudo isso e colocar no relatório, testar alguns perfis e alguns lugares que foi colocado o dado. Não encontrando o nenhum problema, poderá passar para o tester. Na sua vez, o que o tester deverá fazer? Testar apenas alguns pontos de dados em alguns perfis? Ou ele terá que testar todos os pontos de inserção de dados em todos os perfis? Quem demora mais nessa tarefa? Desenvolvedor ou tester? Pior que esse exemplo colocado é quando há alteração no projeto. Às vezes, o desenvolvedor levar 2 horas para fazer uma alteração no projeto, mas o tester vai levar 2 dias para ver se tudo está correto. 
Por isso que eu digo que muitos gerentes de projetos erram. Costumam generalizar o tempo do tester em 20% ou 30% do tempo de desenvolvimento e em 99% das vezes, seu cronograma vai falhar aqui.
Outra coisa que muitas vezes acontece é que o tester se preocupa muito em achar problemas no funcionamento do sistema e esquecem de uma parte básica da coisa. O layout do sistema que ele está testando. Um erro de português, uma tabela desalinhada, diferenças de tamanhos de campos são tão “erros” quanto um campo preenchido incorretamente na montagem de uma página.

Alguns dos diretores da minha empresa sempre defendem que a diferença entre 99% e 100% é imensa. Demorou um pouco para eu entender o que isso queria dizer, mas no momento em que diversos clientes começaram a procurar a empresa para fazer um segundo, terceiro, quarto, ou enésimo projeto, a diferença começou a ficar clara.
Se você achar que 99% já está bom, pode ter certeza que uma hora, esses “1%” vai te fazer falta.

Bom, hoje vou ficando por aqui.

[]´s


Passando valores de uma procedure para uma trigger utilizando o CONTEXT_INFO do SQL

24/07/2010

Em alguns contextos, por exemplo, auditoria de exclusão de registro, pode ser necessário ter que utilizar parâmetros que foram passados para uma procedure em uma trigger que seria responsável por fazer a auditoria da exclusão.

Porém como fazer para que o parâmetro passado para a procedure chegue até a trigger?

Aí entra uma função chamada CONTEXT_INFO. Essa função insere informações binárias na sessão atual. A sintaxe do comando é a seguinte:

SET CONTEXT_INFO { binary_str | @binary_var }

Vou dar um exemplo de como utilizar essa função em uma tabela de auditoria de itens removidos de uma tabela. Suponhamos que temos duas tabela no sistema. Uma chamada Teste e uma que armazenará os dados excluídos da tabela Teste onde ficará armazenado quem removeu o registro e o motivo.

create table Teste
(
       IDTeste int identity(1,1) not null,
       Descricao varchar(50) null,
       CriadoPor int null
)

create table DeletedTeste
(
       IDDeletedTeste int identity(1,1) not null,
       IDTeste int null,
       Descricao varchar(50) null,
       Motivo varchar(100) null,
       ExcluidoPor int null,
       DataExclusao smalldatetime null
)

Para remover um registro da tabela Teste o usuário deverá utilizar uma procedure, onde informa qual o registro que deve ser excluído, quem está fazendo a exclusão e qual o motivo dessa exclusão.

A procedure deverá ser como a seguinte:

create procedure up_DeleteTeste
       @IDTeste int,
       @IDUsuario int,
       @Motivo varchar(100)
as
begin

       — declaracoes para o CONTEXT_INFO
       declare @xml xml, @varXML varbinary(128)

       — dados e conversao para o CONTEXT_INFO
       select @xml = (select @IDUsuario as ‘@IDUsuario’, @Motivo as ‘@Motivo’ for xml path(‘Values’))
       select @varXML = cast(cast(@xml as varchar(max)) as varbinary(128))

      — insercao dos dados no CONTEXT_INFO
       set context_info @varXML

       — remocao do registro da tabela Teste
       delete
       from   Teste
       where  IDTeste = @IDTeste

end
go

Antes de apresentar a trigger que faz a auditoria, vamos explicar como utilizar a função CONTEXT_INFO.

Primeiro, criamos uma instrução XML para armazenar os dois dados que a trigger precisará, no nosso caso, ID do usuário que está excluíndo e o motivo.

select @xml = (select @IDUsuario as ‘@IDUsuario’, @Motivo as ‘@Motivo’ for xml path(‘Values’))

Depois, convertemos esse XML em uma variável do tipo VARBINARY(128) e inserimos no CONTEXT_INFO.

select @varXML = cast(cast(@xml as varchar(max)) as varbinary(128))
set context_info @varXML

Agora vamos a trigger que fará a auditoria:

alter trigger trg_Teste_DEL on Teste
after delete
as
begin

       set nocount on; 

       declare      @varXML varchar(max),
                            @xml as xml,
                            @IDUsuario int,
                            @Motivo varchar(50)

       select @varXML = cast(context_info() as varchar(max))
       select @xml = cast(substring(@varXML, 1, charindex(‘>’, @varXML)) as xml)
      

       select        @IDUsuario = x.v.value(‘@IDUsuario[1]’,‘int’),
                           @Motivo = x.v.value(‘@Motivo[1]’,‘varchar(50)’)
       from          @xml.nodes(‘/Values’) x(v)
      

       insert into DeletedTeste(IDTeste, Descricao, Motivo, ExcluidoPor, DataExclusao)
       select        IDTeste,
                           Descricao,
                           @Motivo,
                           @IDUsuario,
                           getdate()
       from          deleted

end

A trigger é disparada toda vez que algum registro da tabela Teste é removido. Nesse caso, a trigger pega os dados do CONTEXT_INFO e transforma-o em um VARCHAR.

select @varXML = cast(context_info() as varchar(max))

Depois disso, a variável é “limpa”, pois caso você veja o tamanho real da variável, vai perceber que existem diversos caracteres além do XML e esses caracteres, mesmo que ocultos, impedem que seja feita uma conversão de VARCHAR para XML.

Nesse caso, usei essa instrução para fazer essa limpeza e já converter para uma variável do tipo XML.

select @xml = cast(substring(@varXML, 1, charindex(‘>’, @varXML)) as xml)

Feito isso, já temos um XML com os dados passados pela procedure. Agora utilizamos XQuery para pegar os dados do XML, no nosso caso, o ID do usuário e o motivo.

select @IDUsuario = x.v.value(‘@IDUsuario[1]’,‘int’),
             @Motivo = x.v.value(‘@Motivo[1]’,‘varchar(50)’)
from   @xml.nodes(‘/Values’) x(v)

Feito isso, nas variáveis @IDUsuario e @Motivo, já temos os dois dados que precisamos para colocar na tabela de auditoria e o resto dos dados vem da tabela deleted da própria trigger. Assim, inserimos os dados como a seguir:

insert into DeletedTeste (IDTeste, Descricao, Motivo, ExcluidoPor, DataExclusao)
select  IDTeste,
              Descricao,
              @Motivo,
              @IDUsuario, 
              getdate()
from    deleted

Esse é apenas um exemplo do uso do CONTEXT_INFO. Agora é só utilizar para o propósito que for necessário.

Em breve, outras dicas de programação.


Convertendo um array de string para dados de uma tabela no SQL Server

19/07/2010

Em diversas ocasiões, precisamos converter um array de strings passados como parâmetro de uma procedure para uma tabela, nos casos que precisaríamos utilizar esse array em uma condição “IN”. Existem duas formas de se fazer isso.

1. Montando uma query do formato de texto e utilizar o sp_executesql para executar essa consulta

2. Modificando esse parâmetro para ser uma tabela e nesse caso, utilizando normalmente na sua consulta.

Nesse post, mostrarei como fazer a segunda opção.

Primeiramente, criamos uma função para transformar strings separados com algum delimitador para dados de um parâmetro do tipo TABLE.

A função para executar essa ação é a seguinte: 

CREATE FUNCTION udf_StringToTable
    (
      @ParamaterList VARCHAR(MAX),
      @Delimiter CHAR(1)
    )
       RETURNS @ReturnList TABLE
    (
      FieldValue VARCHAR(MAX)
    )
AS
BEGIN
    DECLARE
@ArrayList TABLE
        (
          FieldValue VARCHAR(MAX)
        )

    DECLARE @Value VARCHAR(MAX)
    DECLARE @CurrentPosition INT

    SET @ParamaterList = LTRIM(RTRIM(@ParamaterList)) + CASE WHEN RIGHT(@ParamaterList, 1) = @Delimiter THEN   ELSE @Delimiter END
    SET @CurrentPosition = ISNULL(CHARINDEX(@Delimiter, @ParamaterList, 1), 0) 

 
    IF @CurrentPosition = 0

        INSERT  INTO @ArrayList ( FieldValue )
                SELECT  @ParamaterList
    ELSE
        BEGIN
            WHILE
@CurrentPosition > 0
                BEGIN
                    –tenha certeza que o delimitador esta na string passada
                    SET @Value = LTRIM(RTRIM(LEFT(@ParamaterList, @CurrentPosition – 1)))

                    IF LEN(@ParamaterList) > 0 AND @CurrentPosition <= LEN(@ParamaterList)
                        BEGIN
                            INSERT  INTO
@ArrayList ( FieldValue )
                                    SELECT  @Value
                        END

                    SET @ParamaterList = SUBSTRING(@ParamaterList, @CurrentPosition + LEN(@Delimiter),  LEN(@ParamaterList))
                    SET @CurrentPosition = CHARINDEX(@Delimiter,  @ParamaterList, 1)

                END

        END

    INSERT  @ReturnList ( FieldValue )
            SELECT  FieldValue FROM    @ArrayList

    RETURN

END

Essa função faz com que você possa utilizar um array de strings em uma consulta simples, conforme o exemplo a seguir:

DECLARE @ArrayList VARCHAR(MAX)    

SET @ArrayList = ‘tim,zach,chris,wendi,brad’ 

SELECT * FROM udf_StringToTable (@ArrayList, ‘,’)

Vale lembrar que nem sempre, a utilização desse tipo de função é melhor no que diz respeito a performance.