sexta-feira, 23 de abril de 2010

Acessar corretamente WCF no Silverlight

Acessar um webservice no Silverlight é fácil: a maneira default é adicionar uma referência ao service no projeto Silverlight. O Visual Studio executa a ferramenta slsvcutil.exe para gerar as classes proxy do serviço. Esta ferramenta é semelhante ao svcutil.exe porém especializada no Silverlight.

Aí, já nos deparamos com a primeira diferença: a proxy é gerada apenas com a versão assíncrona das operações. Isto faz sentido, pois não queremos travar o browser né?

Para cada operação temos um método Asynch() e um evento Completed. A sequência consiste em criar um handler para o evento e invocar o método asynch. Quando o controle retorna da invocação deste método, não podemos dizer que ele já executou, apenas que a mensagem foi enfileirada para envio ao web service.

Vejamos um trecho de código:


domingo, 18 de abril de 2010

Novidade: Descompressão automática no WCF 4.0

Participei do survey realizado pela sobre custom channels no WCF. Essa semana tive resposta à minha colaboração, onde tive o espaço para comentar sobre compressão.

Enfim, é necessário estudar alternativas viáveis para troca de informações com o webservice, que muitas vezes passa pelo link mais lento da solução, ou seja, a internet. A Microsoft produziu um sample abordando esta questão, no caso, uma implementação de um encoder que utiliza compressão gzip, o próprio framework fornece suporte para isso. Eu implementei este encoder na solução, mas o desempenho de compressão não ficou nem perto da solução original desenvolvida em outra plataforma.

Exemplo de channel stack: o encoder Gzip fica logo acima do Transport e comprime dados de outro encoder

sexta-feira, 16 de abril de 2010

Enviar uma imagem no Silverlight do cliente ao serviço

Precisava implementar a carga de uma imagem no meu cadastro em Silverlight. No meu serviço WCF, tem as operações que enviam e recebem as entidades do Entity Framework geradas com o modelo Self-Tracking Entities. A propriedade imagem é um byte[].

Mas como fazer um binding do byte[] para um controle Image? Eu nem tinha trabalhado com imagens ainda, que dirá enviar uma pro serviço. Vamos então à como eu consegui chegar na solução.

Primeiro, como fazer binding? Bom, a Image possui um atributo Source, que pode receber um objeto BitmapImage. Como eu recebo um objeto byte[], eu precisava instanciar uma BitmapImage desta forma. Como meu cadastro não tinha nenhuma imagem ainda, tive que implementar a carga primeiro. Aí começou o drama. O Silverlight implementa um modelo de sandbox para execução no browser, que ao menos pra me atrapalhar, funcionou redondinho. Não posso chamar métodos de System.IO por exemplo. Então veio a calhar o IsolatedStorageFile. Descobri o caminho no file system, xcopy umas imagens pra lá, e voilá! Já posso ver umas imagens na tela.

quinta-feira, 15 de abril de 2010

Charter Member para exames beta

Acabei de passar no blog MeuFonte.com onde o blogueiro diz que quem passar nos exames beta também receberá a distinção de Charter Member.

O que é um Charter Member? São aqueles profissionais que atendem todos os requisitos da certificação durante os 6 primeiros meses da publicação oficial de todos os exames. Mais informações aqui. Tudo bem compreendido, mas e os exames beta, como ficam? Eles ocorrem meses antes da publicação oficial, então quem passar teve que ralar muito mais, garimpando a relativamente pouca informação, treinar nas ferramentas Beta, etc. No mínimo devem ganhar a distinção de Super-sayadin Charter Member ou Yoda Charter Member.

Eu questionei isto no blog do Gerry O'Brien. Ele me repondeu que não. Mas as informações são desencontradas. Fiquei um pouco mais tranquilo ao ler nas FAQs que quem passar nos exames beta automaticamente se qualifica. Muito bom! Charter Member é uma ótima recompensa, muito melhor que carteirinha ou outro regalo qualquer, pois é um reconhecimento ao sacrifício. Em tempo: não sou Charter Member (ainda).

Por acaso alguém aí passou em exame beta, atendeu os requisitos e recebeu o título de Charter Member?

A data dos exames que prestarei estão chegando, o primeiro será dia 20. Back to work!

En passant: referenciar assemblies .NET no Silverlight

Não é possível.

E a questão não é nem técnica, pois o formato dos assemblies e o bytecode é essencialmente é o mesmo.

Ocorre que, apesar da naturalidade com que desenvolvemos no Silverlight, esta é uma plataforma paralela ao .NET, possui seus próprios assemblies, mscorlib, System, etc. Compartilha linguagens, compiladores e outras ferramentas.

Como possui um conjunto de assemblies diferente, o Visual Studio 2010 RC não aceita e alerta o desenvolvedor quando este tenta referenciar um assembly da outra plataforma. Embora há quem tente burlar o mecanismo de detecção de plataforma (o VS faz isso pela versão do assembly), no SL não temos o assembly System.Data por exemplo, então não tem jeito de fazer um assembly com Self-Tracking Entities para ser usado no SL. E vice-versa, no .NET não se tem System.Windows.Browser por exemplo.

Uma solução então, é criar um assembly com target Silverlight, e incluir os arquivos como referência, opção Add existing item, Add as Link. Claro que isso não resolve quando estes arquivos precisam de um assembly não existente no SL.

terça-feira, 13 de abril de 2010

En passant: System.Service.Web e .NET Framework profiles

Precisei adicionar uma referência ao assembly System.ServiceModel.Web.dll na minha service library recém criada. Estranhamente não aparecia na aba Recent, mesmo eu já a tendo adicionado a outra library na mesma solution. Tentei adicionar o caminho direto do assembly, mas fica um sinal de warning junto à referência. É a segunda vez que resolvo este problema, então estou escrevendo aqui o caso resolvido.

O .NET Framework possui agora o conceito de framework profile, basicamente é o framework especializado para atender um cenário, então agora temos full profile e o client profile. Por padrão o Visual Studio 2010 RC configura o projeto WCF Service Library para usar o .NET Framework 4 Client Profile. Este client profile obviamente é menor, então reduz tempo de distribuição.



Alguns recursos inclusos no client profile:

  • Windows Forms
  • WPF
  • WCF
  • EF
  • WF
  • MEF

Ficam de fora:

  • ASP.NET
  • System.ServiceModel.Web e outros tópicos avançados de WCF

Tem bastante coisa mesmo no client profile, talvez por causa disso passe despercebido. Eu precisei criar um serviço usando REST para resolver a questão de segurança de cross-domain com o Silverlight. Bastou mudar o Target framework para o .NET Framework 4 que então eu pude adicionar o assembly. Talvez isso seja assunto de outro post no futuro.

Links:



sexta-feira, 9 de abril de 2010

Cancelar alterações em associações

O método RevertChangesSingleEntity() tem suas restrições, por exemplo não trata o caso da entity ter sido adicionada. Este cenário deve ser revertido com o detach da entity de seu context, embora a entidade continuará a existir fora dele. Importante frisar que o método ObjectContext.Detach() atua em uma única entity por vez, e desintegra o grafo que participava, pois não pode haver parte do grafo no contexto e outra parte fora. Pode pensar nisso como o gato de Schrödinger. Isto significa que o se eu tiver um Pedido e uma coleção ItemDoPedido adicionados no contexto, para reverter esta inclusão é preciso detach cada objeto individualmente. Entretanto o Pedido ficaria desconectado de seus ítens. Outra característica do detach é que a entrada (classe ObjectStateEntry) no change tracker permanece com a EntityKey e o State anterior, porém a propriedade Entity será nula pois foi desconectada.

Uma outra maneira de parar o change tracking dos objeto é executar IEntityWithChangeTracker.SetChangeTracker() passando null como o único parâmetro. Isto remove o IEntityChangeTracker que monitora e registra as alterações no objeto. A vantagem disso é que o grafo permanece intacto, e a desvantagem é que parte do grafo continua registrando as alterações e parte não mais.

Acontece que adicionar/deletar uma entidade em um relacionamento implica em manter outras entradas no ObjectStateManager referente aos relacionamentos, como podemos ver na imagem a seguir.



Segue código para tratar a reversão das associações:

public static void RevertChanges(this ObjectContext db)


{
    foreach (var ose in db.ObjectStateManager.GetObjectStateEntries(EntityState.Added))
        ose.Delete();
    foreach (var ose in db.ObjectStateManager.GetObjectStateEntries(EntityState.Modified))
    {
        if (!ose.IsRelationship)
        {
            var org = ose.GetUpdatableOriginalValues();
            var props = ose.GetModifiedProperties();
            foreach (var prop in props)
            {
                var x = ose.Entity.GetType().GetProperty(prop);
                x.SetValue(ose.Entity, org.GetValue(org.GetOrdinal(prop)), null);
            }
        }
    }
    foreach (var ose in db.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted))
        if (!ose.IsRelationship)
            ose.ChangeState(EntityState.Unchanged);


    db.AcceptAllChanges();
}

(to be continued...)

quinta-feira, 8 de abril de 2010

Cancelar alterações na entity (ou delta ou changeset)

Você já tentou criar um cadastro mestre-detalhe com o Entity Framework? Pois bem, eu estou tentando chegar lá...

Como eu venho de um background data-centric, onde datasets são a pattern mais utilizada para a) persistir dados em memória; b) manter changeset; c) prover dados para as telas, o trabalho de controlar a edição de objetos é bastante simples. Cancelar as alterações de um registro ou de um conjunto inteiro de registros é moleza. Isto se deve a uma outra estrutura conhecida como delta (ou ainda changeset), que mantém cada inclusão/edição/exclusão de dados. Para reverter as alterações em memória, basta limpar o buffer do registro corrente ou então limpar o delta.

No Entity Framework as coisas não são simples assim. Não importa se o projeto segue uma abordagem code-first, database-first ou model-first. Se fosse fácil haveria um método RevertChanges() ou na entidade ou no ObjectContext. Um CRUD fica complicado de se implementar ou no mínimo moroso, sem auxílio da camada de apresentação. Claro que isso tem um aspecto positivo, força o desenvolvedor a planejar um modelo de apresentação independente do modelo de dados. Mas voltemos ao assunto do post.

O cenário mais básico é reverter as edições em atributos escalares ou string, ou seja, todos exceto os relacionamentos com outras entidades. O ObjectStateManager armazena uma entrada para cada entidade no contexto, juntamente com os valores originais. Podemos copiar os valores originais novamente na entidade:

public static void RevertChangesSingleEntity(this ObjectContext db, EntityObject entity)
{
    // reverte alterações em propriedades escalares de uma entidade
    ObjectStateEntry ose;
    if (db.ObjectStateManager.TryGetObjectStateEntry(entity, out ose))
    {
        var org = ose.GetUpdatableOriginalValues();
        var props = ose.GetModifiedProperties();
        foreach (var prop in props)
        {
            var x = entity.GetType().GetProperty(prop);
            x.SetValue(entity, org.GetValue(org.GetOrdinal(prop)), null);
        }
        ose.AcceptChanges();
    }
}

Bom, então se bastam estas linhas de código, porque não está incluído no EF? Acho que o time responsável pelo framework não tem conhecimento preciso das necessidades de um cenário como este para um ORM...

(to be continued...)