quinta-feira, 30 de junho de 2011

Key Violation em nested datasets

Resolva de uma vez por todas o problema de "key violation" em datasets "detail" ligados a datasetfileds através de providers.

Cenário: você tem duas tabelas num relacionamento 1:n tipo mestre-detalhe no seu banco de dados, por exemplo Pedidos e ItensDoPedido.
A chave primária de ambas as tabelas é auto-numerada, por exemplo com ajuda de um generator do firebird. A Tabela itens tem uma chave estrangeira PedCodigo, que é o código do pedido, mas para controle interno também tem uma chave primária CodItem, autonumerada.
Você colocou no formulário dois SQLDatasets, um fazendo o select em Pedidos e outro fazendo o select em ItensDoPedido, com um "where CodPedido = :CodPedido" para fazer a "ligação". Você ligou o SQLDataset detalhe (ItensDoPedido) a um datasource ligado ao SQLDataset mestre. Você ligou também um DatasetProvider ao SQLDataset de Pedidos e ligou um ClientDataset cdsPedidos a esse provider, criando um dataset field (campo do tipo dataset), talvez chamado sqlItens, e ligando o ClientDataset cdsItem a este dataset field, criando assim o cdsDetahle.

Você talvêz tenha recebido alguma mensagem de que o campo CodItem é requerido e não pode ser deixado null, então tomou providências para deixar a propriedade required do campo = false ou atribuido algum valor randômico a ele. Se fez isso é sinal de um design fraco. Há maneiras mais elegantes de se resolver isso, veja as soluções 2 e 3.

Problema: Quando você insere o segundo item no ClientDataSet cdsItem recebe a exception "Key Violation".

Causa: O cdsItem (detalhe) também possui uma chave primária, CodItem. Quando você dá o post no segundo item ele é gravado com o mesmo valor do registro anterior: null ou zero se você definiu para atribuir zero no evento "onNewRecord" ou "beforepost". Como chave primária ele não pode se repetir no cdsItem, mesmo sabendo que os códigos serão criados no banco de dados automaticamente depois do applyupdates e do commit da transação. Antes disso não há valor definido para a chave primária do item.

Há 3 maneiras de solucionar:

1) Atribua um número randômico (pode até ser negativo) ou GetTickCount à chave primária no evento OnNewRecord.
Desvantagens: essa é a gambiarra mais feia e porca que existe. Além de poluir o código, caso o número randômico se repita, o que estatisticamente pode acontecer, ocorrerá o key violation. Caso use GetTickCount este pode trazer um número maior do que o suportado pelo integer field, já que retorna um cardinal sem sinal, podendo causar o erro de integer overflow. Portanto a solução 1 está aqui para fins "didáticos" (ou não). Nunca a use.

2) Para que você precisa trazer o campo da chave primária do item? Na maioria das vezes isso não é necessário. Você pode incluir todos os campos no select exceto a PK, já que ela ainda não está definida. E você não precisa exibi-la caso ela esteja null. Isso resolve o problema de uma maneira performática e elegante.

3) Metadados: Como o ClientDataset sabe que tal campo é chave primária se ele não é conectado diretamente ao banco de dados? Ele "sabe" isso porque provavelmente a propriedade GetMetadata do SQLDataset deve estar true. Colocando essa propriedade para false  o ClientDataset não "saberá" que esse campo não pode se repetir, muito menos que ele é requerido. Além disso o ClientDataset abre muito mais rápido caso essa propriedade esteja false, pois não tem que fuçar nos metadados da tabela. Essa técnica é ideal para quando você precisa mostrar o número da PK logo depois da inserção e persistência. Como por exemplo mostrar um número de comanda num sistema de restaurante ou mostrar o número da "carteirinha" de um cliente recém inserido.
Desvantagem: se por algum motivo, como campo requerido, not null ou qualquer outro você precisa que os metadados sejam trazidos para que sejam tratados no client e não no server esta técnica não deve ser usada, recorra à 2.

Mencionei o problema ocorrendo em nested datasets mas ele pode ocorrer em ClientDatasets "normais" ligados a uma única tabela.

Uma mistura das soluções 2 e 3 quando aplicável é a melhor e mais performática solução. Quando nenhuma das duas pode ser usada, ou seja, você precisa dos metadados por algum motivo e ainda precisa exibir a chave primária criada logo depois do registro salvo, considere usar um outro tipo de arquitetura, ou usar chaves primárias definidas via programação em vez de generators.

Happy Coding  \(^.^)/

segunda-feira, 27 de junho de 2011

O que você melhoraria no Delphi? - parte 2

Na parte 1 desse post, há muito tempo, fiz uma pequena descrição do que eu acho que poderia ser melhorado no Delphi.

Resumindo:
1) Alguns componentes e bibliotecas mais comuns "nativos" em vez de "incorporados".
2) Literatura orientada mais a POO e a boas práticas da Engenharia de Software do que a RAD. A literatura disponível nos helps, exemplos, snippets e consequentemente nos blogs e fóruns é toda voltada a RAD, arrastar e  soltar componentes. E como todos sabemos RAD é bom para protótipos e projetos pequenos, ou com requisitos fixos, com baixa frequencia de manutenção.
3) O aspecto que eu acho mais importante é (a falta de) um framework de persistência nativo da Embarcadero, para se fazer o mapeamento objeto relacional de maneira padronizada e ao mesmo tempo rápida.


Continuando, uma coisa que eu acho muito importante são as ações de marketing. No Brasil parece que o Delphi está queimado justamente com quem mais deveria acreditar nele: os professores.
Digo isso por experiência própria, pois meu projeto de conclusão de curso da pós graduação passou por esse problema.
Minha monografia era para ser "frameworks de mapeamento objeto relacional em Delphi". Meu orientador, professor de engenharia de software e outras matérias. Delpheiro de carteirinha e especialista em Delphi me perguntou "Por Que Delphi?". Ele questionou que meu trabalho, focado em Delphi, seria para um público muito restrito e que o Delphi está fadado a cair no desuso (nas palavras dele).
Não concordo com o ponto de vista dele sobre o Delphi, mas concordei que eu teria que expandir meus horizontes se quisesse fazer um trabalho academicamente mais amplo. Por isso meu tema foi trocado para "Processo de criação de frameworks de mapeamento objeto relacional em ambientes open-source".

O ponto em questão é que os professores, em vez de recomendar Delphi, estão "des-recomendando" (se é que esse termo existe). E agora como convencê-los do contrário?

Outro ponto é quanto ao preço do Delphi, mais caro que a concorrência. Quando saiu a edição "starter" do Delphi realmente o preço saiu bastante tentador. Mais barato que uma licença do windows 7 ultimate. Mas meu interesse na versão starter despencou quando eu soube que esta verão não possui dbexpress. Qual a utilidade do Delphi, para se fazer sistemas de informação, sem o dbExpress? Se conectar com MySql através de ADO+ODBC? BDE? Não, obrigado. Terei que comprar a suíte de componentes Unidac (que eu recomendo, muito boa).

Conversando com uns amigos no evento de lançamento da Embarcadero no Brasil chegamos a seguinte conclusão: por que não vender o Delphi "pelado", apenas IDE e compilador, por um preço simbólico e vender os componentes e add-ons sob demanda? Pense nisso:
Hoje em dia estão na moda as AppStores, onde você compra uma aplicação a um preço justo, e esta aplicação é sua, sendo sincronizada em seus dispositivos com o mesmo sistema operacional. Se você formata / reseta seu sistema, suas aplicações já pagas/compradas da appStore são baixadas e instaladas novamente. Tudo é re-sincronizado automaticamente. O mundo da Apple funciona assim, com o iPhone, iPad, iPod  e com o iMac. O mundo do Google Android também funciona assim, e até a Intel entrou na brincadeira.
Qual é o maior problema quando um desenvolvedor Delphi tem que formatar sua máquina? Ele perde um dia inteiro, ou dias, reinstalando, além do Delphi, todos os componentes, open-source ou não, bibliotecas e adds que precisa para trabalhar produtivamente. Muitos tentam exportar chaves de registro, e copiar diretórios de bpl's e dcp's a fim de criar uma instalação do Delphi personalizada para poder instalar depois, ou fazem ghost da máquina. A situação se torna pior se a empresa tem vários programadores Delphi, com várias máquinas diferentes e várias licenças pra instalar. Pior ainda se lida com versões diferentes do Delphi.
Solução: por que não criar um instalador mínimo do Delphi e um appStore de componentes e bibliotecas? Assim, bastaria instalar o Delphi e logar-se com a sua conta na Embarcadero para re-instalar e sincronizar todos os componentes. E isso serviria quem sabe para você publicar um componente seu e ganhar com ele, porque não? Até mesmo para se fazer "Component Contests".
Isso poderia ajudar a garantir o controle de qualidade, testes etc de um componente, além da procedência, e garantir, principalmente para os não-open-source, uma sobrevida e um tempo de suporte maior, além de tornar a instalação do Delphi muito mais fácil e a busca, aquisição e instalação de componentes muito mais fácil.
Quando eu digo componentes me refiro à bibliotecas "normais" também, daquelas que você precisa instanciar uma classe, ou usar uma função, mas que não tem um componente em uma paleta pra colocar na form.
A maioria dos componentes precisam que arquivos sejam colocados em certos diretórios, e que certos caminhos sejam adicionados ao library path e browsing path do windows. Além disso os instaladores dos componentes são todos diferentes, quando há instalador, pois a maioria você deve compilar a partir dos dpk's.
Agora imagine um ambiente com um gerenciador de componente que encontra o componente que você precisa, faz as devidas inclusões dos paths, compila e instala se for open-source ou simplesmente instala se não  for, e te livra de todos os passos chatos, demorados ou complicados.

Essas são algumas coisinhas que eu acho que poderiam ser melhoradas. E vocês, o que acham?

quarta-feira, 22 de junho de 2011

Grande verdade sobre developers


Além de encarar qualquer problema de frente, assumimos a responsabilidade pelos bugs, sejam eles nossos, de "estimação", ou não.

Postagens populares

Marcadores

delphi (60) C# (31) poo (21) Lazarus (19) Site aos Pedaços (15) sql (13) Reflexões (10) .Net (9) Humor (9) javascript (9) ASp.Net (8) api (8) Básico (6) Programação (6) ms sql server (5) Web (4) banco de dados (4) HTML (3) PHP (3) Python (3) design patterns (3) jQuery (3) livros (3) metaprogramação (3) Ajax (2) Debug (2) Dicas Básicas Windows (2) Pascal (2) games (2) linguagem (2) música (2) singleton (2) tecnologia (2) Anime (1) Api do Windows (1) Assembly (1) Eventos (1) Experts (1) GNU (1) Inglês (1) JSON (1) SO (1) datas (1) developers (1) dicas (1) easter egg (1) firebird (1) interfaces (1) introspecção (1) memo (1) oracle (1) reflexão (1)