Certificação Microsoft 70-487: Objetivo 1.5 – Create and implement a WCF Data Services service

A discussão sobre WCF Data Services no objetivo 1.1 foi curta porque há um objetivo específico para ele, o Create and implement a WCF Data Services service, que será coberto nesse post.

Esse objetivo cobre como endereçar recursos, criar uma query, acessar formatos de carga (incluindo JSON) e trabalhar com interceptores e operadores de serviço. Vamos lá?

Endereçando Recursos

Para criar um WCF Data Service, basicamente você deve seguir os seguintes passos:

  • Criar uma aplicação ASP.NET (para hostear o Data Service);
  • Usar o EF para criar seu EntityModel;
  • Adicionar um WCF Data Service;
  • Especificar um tipo para o serviço (que é o nome do model container do projeto do EF);
  • Habilitar acesso ao Data Service configurando explicitamente algumas propriedades do DataServiceConfiguration (SetEntitySetAccessRule, SetServiceOperationAccessRule e DataServiceBehavior)

Quando você adicionar um WCF Data Service, você verá classes geradas assim: public class ExamSampleService : DataService. Essas classes herdam de System.Data. Services.DataService, que recebem um tipo genérico. O código gerado terá também o método InitializeService com alguns comentários que ajudam na sua configuração:

// This method is called only once to initialize service-wide policies.
public static void InitializeService(DataServiceConfiguration config) {
// TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.
// Examples:
// config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead);
// config.SetServiceOperationAccessRule("MyServiceOperation", ServiceOperationRights.All); config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}

Como já citado, é importante se familiarizar com os métodos SetEntitySetAccessRule e SetServiceOperationAccessRule e a propriedade DataServiceBehavior.MaxProtocolVersion.

SetEntitySetAccessRule

Esse método recebe dois parâmetros: uma string com o nome do EntitySet e um enum EntitySetRights. Como o enum é decorado com Flags, você pode passar mais de um valor dessa forma: config.SetEntitySetAccessRule("Courses", EntitySetRights.AllRead | EntitySetRights.WriteMerge);. Os possíveis valores desse enum são:

  • None: todos os acessos são revogados;
  • ReadSingle: um item pode ser lido;
  • ReadMultiple: sets de dados podem ser lidos;
  • WriteAppend: novos itens podem ser adicionados;
  • WriteReplace: dados podem ser atualizados;
  • WriteDelete: dados podem ser deletados;
  • WriteMerge: dados podem ser mergeados;
  • AllRead: tudo pode ser lido;
  • AllWrite: tudo pode ser escrito;
  • All: todas as operações são permitidas.

SetServiceOperationAccessRule

Esse método dá permissões para uma ServiceOperation e funciona da mesma forma que o método SetEntitySetAccessRule, mas o enum é um ServiceOperationRights. Os valores desse enum são:

  • None: todos os acessos são revogados;
  • ReadSingle: um item pode ser lido;
  • ReadMultiple: sets de dados podem ser lidos;
  • AllRead: um e vários itens podem ser lidos;
  • All: todos os direitos são garantidos;
  • OverrideEntitySetRights: indica se sobrescreve os valores configurados em SetEntitySetAccessRule.

DataServiceBehavior.MaxProtocolVersion

Essa propriedade indica qual versão do protocolo OData será usado no serviço. Os possíveis valores do enum DataServiceProtocolVersion são V1 e V2.

Criando Uma Query

Uma das principais vantagens do WCF Data Services é que seus recursos são endereçáveis por URIs via OData, e você pode fazer queries nas URIs de uma forma bem simples.

Suponha que você criou um WCF Data Service com o nome ExamPrepService baseado num Entity Model que tem tópicos, questões e respostas como entidades.

Para obter todos os registros de uma entidade, a query Service/EntitySetName pode ser usada. Para obter todos os tópicos do serviço, por exemplo, a seguinte query pode ser usada:
http://servicehost/ExamPrepService.svc/Topics

A query Service/EntitySetName(‘KeyValue’) retorna a entidade que tem uma key com o valor passado. Por exemplo, para obter o tópico que tem o nome First, a query fica http://servicehost/ExamPrepService.svc/Topics('First').

Se você precisa somente da propriedade Description, a query fica http://servicehost/ExamPrepService.svc/Topics('First')/Description. Se quiser somente o valor primitivo e não o XML, basta adicionar /$value no final: http://servicehost/ExamPrepService.svc/Topics('First')/Description/$value.

As queries funcionam bem com relacionamentos, se esses estiverem definidos no Entity Model. Para obter todas as Answers da Question com a key 1, por exemplo, a seguinte query deve ser usada:
http://servicehost/ExamPrepService.svc/Questions('1')/Answers

O caminho inverso também é possível: http://servicehost/ExamPrepService.svc/Answers('1:4')/Question.

Até agora, só filtramos os resultados pela key da entidade. Mas o protocolo OData e o WCF Data Service oferecem várias opções de query que se traduzem em queries SQL. É bem importante entender o efeito de todos eles no resultado.

  • $orderby: define uma ordem para o resultado. Uma ou mais propriedades podem ser usadas, separadas por vírgula: /Questions?$orderby=Id,Description;
  • $top: define quantos resultados serão retornados: /Questions?$top=5
  • $skip: indica quantos registros devem ser ignorados. Suponha que há 30 tópicos, mas você quer ignorar os 10 primeiros e os 10 últimos. A query fica assim: /Questions?$skip=10&$top=10;
  • $filter: especifica uma ou mais condições: /Questions?$filter=Id gt 5;
  • $expand: indica quais entidades relacionadas serão retornadas: /Questions?$expand=Answers;
  • $select: por padrão, todas as propriedades de uma entidade são retornadas. Usando $select, você pode especificar quais propriedades serão retornadas separando por vírgula: /Questions&$select=Id,Text,Description,Author;
  • $inlinecount: retorna o número de registros retornados no elemento <count>: /Questions?$inlinecount=allpages.

Você também pode executar queries por meio de código usando a classe DataServiceQuery:

String ServiceUri = "http://servicehost/ExamPrepService.svc";
ExamServiceContext ExamContext = new ExamServiceContext(new Uri(ServiceUri); DataServiceQuery = ExamContext.Question
.AddQueryOptions("$filter", "id gt 5")
.AddQueryOptions("$expand", "Answers");

Acessando Formatos De Carga

o WCF Data Service dá suporte aos formatos Atom e JSON de forma fácil.

Basta usar a opção $format na query com o valor atom ou json. Caso esteja consultando via WebClient ou queira especificar isso em um request header, funciona da mesma maneira. Para JSON, coloque o header Content-Type para application/json. Para Atom, application/atom+xml.

Trabalhando Com Interceptor e Service Operator

O WCF Data Service permite que você intercepte um request para prover uma lógica customizada.

Há dois tipos de Interceptors: ChangeInterceptor, que serve para interceptar operações NON-Query, e QueryInterceptor, que intercepta operações de Query.

ChangeInterceptors têm retorno nulo e aceitam dois parâmetros: o tipo da entidade e um UpdateOperations, que referencia o request. A definição de um método ChangeInterceptor fica assim:

[ChangeInterceptor("Topics")]
public void OnChangeTopics(Topic topic, UpdateOperations operations)

QueryInterceptors são decorados com QueryInterceptor e retornam uma Expression>, que servem como filtro para a entidade. A assinatura fica assim:

[QueryInterceptor("Topics")]
public Expression> FilterTopics(){}

A dica para o exame é que, para identificar se um método é ChangeInterceptor ou QueryInterceptor, basta olhar para o tipo do retorno.
Para saber qual Interceptor deve ser implementado para fazer alguma coisa, é necessário saber o comportamento e determinar se é uma operação NON-Query ou Query. Por exemplo, DeleteTopics seria um ChangeInterceptor; já um método GetAdvancedTopics seria um QueryInterceptor.

Qualquer dúvida, fique à vontade para fazê-la nos comentários.
Fique ligado para o post do próximo objetivo, Manipulate XML data structures.

Até mais!

Leia Mais

Certificação Microsoft 70-487: Objetivo 1.4 – Implement data storage in Windows Azure

Olá pessoal! Vamos ao primeiro (e um dos poucos) objetivo que fala de Azure na certificação de Azure, o Implement data storage in Windows Azure. Para acompanhar os objetivos já postados, clique aqui.

O primeiro tópico desse objetivo é sobre armazenamento de dados. Comparado com mecanismos de armazenamento de dados tradicionais, as soluções do Azure oferecem um suporte bem parecido com o que já existe. Mas é claro que coisas novas geram mudanças, ainda mais com um paradigma mais recente que é a nuvem. Por exemplo, acesso à internet é fundamental para que qualquer armazenamento no Azure funcione.

As opções de armazenamento de dados no Azure são: Local Storage; Blob, Table e Queue (fazendo parte do Windows Azure Storage); e SQL Database. Para o exame, é bem provável que você encontre alguma pergunta onde um cenário é dado e você deve escolher quais desses itens resolveriam o problema. Vamos às explicações básicas: quando você ler vídeo, áudio ou imagem, a pergunta está se tratando de Blob. Quando é algum dado estruturado, pode ser Table ou SQL Database, se a estrutura for relacional. Queue se trata de garantir um mecanismo de envio de mensagens em fila. Local Storage é um armazenamento temporário igual a um sistema de arquivos. Vamos nos aprofundar na implementação desses itens.

Blob Storage

A estrutura de Blob foi feita para armazenamento de dados binários, como os já mencionados vídeo, áudio e imagem. É possível também armazenar um arquivo Excel como binário sem nenhum problema. Um dos principais benefícios é que as informações são acessíveis via HTTP. Basta ter a URL e você pode acessar uma imagem, por exemplo. Os arquivos podem ter o tamanho de até 100 TB. Acho que já deu para entender o conceito, mas as coisas ideais de serem armazenadas em Blob são: imagens que podem ser vistas diretamente no browser, documentos, backups de banco de dados, vídeos e áudios.

A estrutura do Blob funciona assim: no nível mais alto está a Storage Account. Dentro dela temos Containers e, dentro dos Containers, Blobs. Há o limite de 100 TB em uma Storage Account, porém ela pode ter quantos Containers forem necessários. Pense em um Container como um subdiretório para organizar os blobs (a diferença é que Containers não podem conter Containers). Um Blob é um arquivo, sem restrição de formato. Há duas categorias de Blobs: Block (limite de 200 GB) e Page (limite de 1 TB).

Você pode acessar um Blob de duas formas. A primeira, como citado, é por uma URL HTTP. A URL tem o seguinte formato: http://.blob.core.windows.net/{container}/{blob}.

A segunda maneira é pela API do Azure SDK. Você deve adicionar uma referência ao assembly Microsoft.WindowsAzure.Storage.dll. A Storage Account é representada pela classe CloudStorageAccount. Para instanciá-la, você pode usar um de seus dois construtores. O primeiro recebe uma StorageCredentials e três URIs representando o endpoint do Blob, Queue e Storage, nessa ordem. O segundo construtor recebe uma StorageCredentials e um bool que indica se será utilizado HTTPS. Vamos ao código:

Outra maneira (bem melhor) de instanciar a classe CloudStorageAccount é usando o método estático Parse ou TryParse, que possui dois overloads: é possível usar a classe CloudConfigurationManager e chamar o método GetSetting(), que obtém a informação do arquivo .CSCFG do seu projeto Windows Azure Cloud, ou passar somente uma connection string no formato certo (<add name=”StorageConnection” connectionString=”DefaultEndpointsProtocol=https;AccountName=ACCOUNT_NAME_GOES_HERE;AccountKey=ACCOUNT_KEY_GOES_HERE” />).

Depois de instanciada a classe CloudStorageAccount, você precisa criar um CloudBlobClient e obter uma referência ao CloudBlobContainer. Lembre-se de que há dois tipos de Blobs e cada um tem seu método para obter a referência (GetBlockBlobReference e GetPageBlobReference).

Do Container é possível obter a referência para o blob desejado e salvar o conteúdo do arquivo chamando o método UploadFromStream. Há outros dois métodos para realizar o upload (UploadFromByteArray e UploadFromFile). Todos eles sobrescrevem os dados se eles já existirem e têm sua versão assíncrona (com Async no final do nome), o que é ideal para fazer o upload de Blobs grandes. É também recomendado envolver a chamada de qualquer Upload em um try/catch para tratar quaisquer erros de rede. Para fazer o download de um Blob, basta chamar o método DownloadToStream. Para deletar um Blob, há os métodos Delete e DeleteIfExists.

A criação padrão de um Container é com acesso privado. Você pode mudar a permissão chamando o método SetPermissions de um CloudBlobContainer, passando um BlobContainerPermissions com PublicAccess habilitado, por exemplo. Você pode também distribuir assinaturas de acesso temporárias com o método GetSharedAccessSignature, tanto de um Container quanto de um Blob.

Table e Queue Storage

Uma Table storage serve armazenar dados não relacionais, como os bancos de dados NoSQL (CouchDB ou MongoDB, por exemplo). Uma Queue storage serve para armazenar mensagens no esquema de fila, onde o primeiro item que entra é o primeiro a sair (FIFO, First In First Out). Queues são úteis em sistemas de entrega de mensagens ou longos processamentos.

As APIs de Table e Queue são bem parecidas com as do Blob. Você instancia a classe CloudStorageAccount, chama o método CreateCloudTableClient ou CreateCloudQueueClient e voilà. Tables e Queues são sempre privadas.

Queues são mais simples porque não há métodos de busca. Além de poder obter uma quantidade aproximada de itens na fila, basicamente há métodos para colocar e obter mensagens da fila (push-and-pop). Um aspecto interessante é que obter uma mensagem da fila não a remove completamente, só a deixa invisível por um período de tempo (1 minuto por padrão). Se seu processamento demorar mais do que esse tempo, você deve atualizar o status da mensagem como escondida para que outros processos não a vejam na fila. Após terminar seu processamento, aí sim você pode deletar a mensagem da fila.

Tables são um pouco mais interessantes pois é possível interagir com os dados. O jeito principal de obter um registro é executar uma TableOperation, mas você pode obter vários registros com uma TableQuery. Vamos a um trecho de código que adiciona um registro, obtém esse registro e o deleta. Note que a classe Record herda de TableEntity, sendo possível criar classes que representam os dados na Table.

Distribuição de Dados Usando Windows Azure Content Delivery Network (CDN)

O Windows Azure Content Delivery Network (CDN) é uma maneira de cachear conteúdos estáticos (Blobs). A ideia é usar uma localidade próxima ao client para melhorar a performance e largura de banda. Os dois principais benefícios, segundo a MSDN, são:

  • Melhorar a performance para usuários que estão longe da fonte de conteúdo e estão utilizando aplicações que necessitam de muitas requisições;
  • Escalar a distribuição para lidar melhor com uma alta carga instantânea, como um lançamento de produto.

Para usar o CDN, você deve habilitar no portal do Azure. Depois, o CDN vai funcionar assim: quando um request for feito para um Blob, ele vai redirecionar transparentemente o request para a localização mais próxima, para evitar uma viagem muito grande. O primeiro request será feito normalmente, mas os próximos serão cacheados naquela região. O tempo de expiração é importante, pois como os itens são cacheados, alguns problemas podem acontecer caso o item seja atualizado frequentemente. O que controla o tempo de expiração é a configuração Time To Live (TTL) no portal.

Para deixar um conteúdo disponível para CDN, ele deve estar em um container público e disponível para acesso anônimo. Containers públicos podem ser setados via API, como citado acima. A propriedade PublicAccess deve ser configurada como BlobContainerPublicAccessType.Container.

Com tudo configurado, basta acessar o Blob. A URL tradicional vai funcionar normalmente (http://.blob.core.windows.net/{container}/{blob}), mas uma nova URL ficará disponível pelo CDN: http://.vo.msecnd.net/{container}/{blob}.

Só relembrando: quanto mais um item for dinâmico, menos provável que ele seja um bom candidato para usar CDN, por causa do cache.

Windows Azure Caching

Cacheamento no nível do Windows Azure é bem diferente do que temos em nível de aplicação, como nesse objetivo. Esse cacheamento é role-based, ou seja, disponível em qualquer role no Windows Azure.

Há dois mecanismos: dedicated topology e co-located topology. Em uma dedicated topology, você define uma role que será especificamente designada para lidar com cache, por exemplo, toda a memória de uma Worker Role. Em um cenário de co-located topology, apenas parte dos recursos podem ser utilizado para cache. Caso você tenha 4 Worker Roles, por exemplo, pode usar 25% de cada para lidar com cache.

Existe também um serviço opcional chamado Windows Azure Shared Caching. Diferente dos anteriores, ele existe em nível de serviço, e não de role.

Lidando Com Exceções Usando Retries (SQL Database)

SQL Database, sendo um banco de dados na nuvem, tem vários benefícios como escalabilidade e facilidade de configuração. Uma desvantagem é que, com a distância física entre o servidor de aplicação e o de banco de dados, a latência aumenta, podendo causar timeouts na conexão com o banco. Como esse erro é temporário, faz sentido ter uma lógica de retentativa (retry) nas conexões com o banco de dados.

O Azure oferece um framework para implementação de lógicas de retentativa. Segue um código de retentativa em deadlocks e timeouts com ADO.NET:

Isso é tudo para esse objetivo 🙂 Obrigado pela leitura e fique ligado no post do próximo objetivo, Create and implement a WCF Data Services service.
Até lá!

Leia Mais

Certificação Microsoft 70-487: Objetivo 1.3 – Implement transactions

Olá pessoal! Nesse post vou falar sobre o objetivo 1.3 do exame de certificação Microsoft 40-487, Implement transactions. Para acompanhar desde o começo e ver os primeiros posts, clique aqui.

Para o exame, é necessário que você saiba características gerais de transações e como implementá-las com ADO.NET e Entity Framework. Vamos lá?

Entendendo Transações

Basicamente, transações servem para fazer os dados do banco de dados serem consistentes. Elas são necessárias em caso de falha no meio de uma execução de comandos, servindo para que todos eles sejam revertidos. Por exemplo, ao tentar fazer a inserção de uma Account e depois uma AccountDetails, as duas inserções devem ser feitas com sucesso para que não haja um registro de AccountDetails sem Account e vice-versa. Entender esse conceito é fundamental para esse objetivo.

Outro aspecto importante de transações é se elas são simples ou distribuídas. Essencialmente, uma transação simples envolve apenas um banco de dados, enquanto uma distribuída envolve diversos bancos de dados, provavelmente de aplicações diferentes. Uma limitação da classe System.Data.SqlClient.SqlTransaction é que ela só trabalha com bancos de dados SQL Server.

Há também o conceito de nível de isolamento (isolation level) de uma transação. O enum IsolationLevel serve para controlar o comportamento de lock na execução de um comando. Esse enum existe em dois namespaces (System.Data e System.Transaction), mas ambos fazem a mesma coisa e têm os mesmos valores. A diferença apenas é que eles trabalham com as classes dos seus respectivos namespaces. O IsolationLevel pode ser mudado durante a execução da transação sem problemas. Vamos ao valores e comportamento deles:

  • Unspecified: Não determinado. O nível de isolamento será determinado pelo driver do banco de dados.
  • Chaos: As alterações pendentes de transações mais altamente isoladas não podem ser substituídas. Esse valor não é suportado no SQL Server ou Oracle, por isso tem uma utilização bem limitada.
  • ReadUncommitted: Nenhum shared lock é emitido. Isso implica que a leitura pode ser imprecisa, o que geralmente não é desejado.
  • ReadCommitted: Emite shared locks nas leituras. Isso evita leituras imprecisas, mas os dados ainda podem ser mudados antes do fim da execução da transação. Pode resultar em nonrepeatable reads ou phantom data.
  • RepeatableRead: Locks são colocados em todos os dados usados na query, o que previne quaisquer alterações durante a execução da transação. Isso acaba com o problema de nonrepeatable read, mas phantom data ainda pode ocorrer.
  • Serializable: Um range lock é colocado em um específico DataSet, o que previne quaisquer inserções ou alterações até que a transação termine. Deve ser usado rapidamente.
  • Snapshot: Uma cópia dos dados é feita, então uma execução de transação pode ler enquanto outra pode modificar os mesmos dados. Não é possível ver os dados de outras execuções de transações, mesmo rodando a query novamente. O tamanho disso pode também causar problemas se não for usado rapidamente.

Implementando Transações com TransactionScope (System.Transactions)

TransactionScope é simples e poderoso. Vamos ao código e algumas características importantes:

  • O método Complete() faz com que todas as alterações sejam comitadas. Se não for chamado, todas elas são desfeitas.
  • Caso alguma Exception seja lançada dentro do bloco using, todas as alterações serão desfeitas também.
  • A transação é promovida de simples para distribuída automaticamente quando a segunda SqlConnection é aberta.
  • Mesmo numa transação distribuída, há algumas limitações óbvias. Cópias de arquivos e chamadas para web services, por exemplo, não são revertidas (alguns web services podem até suportar transações, mas isso não acontece automaticamente). Há também o requerimento de que o banco de dados suporte transações, o que não acontece com todos.

Implementando Transações com EntityTransaction

Você pode implementar transações com a classe EntityTransaction para as classes EntityCommand e EntityConnection. Ela tem duas principais propriedades: Connection e IsolationLevel. Tem também dois métodos importantes e óbvios: Commit() e Rollback().

Um ponto não óbvio e importantíssimo é que, usando DbContext ou ObjectContext (explicados aqui), não é necessário usar a classe EntityTransaction. Em qualquer DbContext.SaveChanges(), por exemplo, o Entity Framework usa uma transação para salvar todas as modificações feitas. Caso aconteça algum erro, uma Exception é lançada e todas as alterações são desfeitas.

Caso queira usar a classe EntityTransaction para explicitamente fazer uma transação, o caminho é bem simples. Basta criar uma EntityCoonnection, declarar uma Entity Transaction chamando BeginTransaction() e, no final, chamar o método Commit() ou Rollback(). Segue o código:

Implementando Transações com SqlTransaction

O comportamento de uma SqlTransaction é quase idêntico ao de uma EntityTransaction. As mudanças estão nos nomes dos objetos e seus tipos.

Você deve criar uma SqlConnection, chamar o método BeginTransaction() passando um IsolationLevel e criar um ou mais SqlCommands passando como parâmetro o CommandText, a SqlConnection e a SqlTransaction. Ao invés de um CommandText, também é possível passar o nome de uma Stored Procedure e mudar o tipo do CommandType para StoredProcedure.

Depois de toda a lógica feita, basta chamar o método Commit() ou Rollback().

Uma última dica para o exame: se você for perguntado qual é o melhor cenário para aplicar uma transação entre uma query longa que envolve várias tabelas e uma query rápida que envolve uma tabela, escolha a última opção. Embora aparentemente uma query que envolve várias tabelas pareça o ideal, o livro alega que a duração e complexidade geraria colocaria problemas de lock e contenção. Então tá, né?

O próximo post será sobre o objetivo 1.4, Implement data storage in Windows Azure.
Até lá!

Leia Mais

Certificação Microsoft 70-487: Objetivo 1.2 – Implement caching

O objetivo 1.2 do exame 70-487: Developing Microsoft Azure and Web Services, Implement caching, trata-se de implementações de cache. Nesse objetivo, é necessário que você se familiarize com dois objetos de cache: ObjectCache e o HttpContext.Cache.

ObjectCache

O ObjectCache provê a funcionalidade de cache com extrema facilidade. Basta obter a instância dele usando MemoryCache.Default e usar os métodos que têm nomes amigáveis como Add, AddOrGetExisting, Get e Set. Pode não ser fácil memorizar todos os overloads, mas já chegamos lá. Primeiro dê uma olhada num uso simples do ObjectCache:

Note que é necessário primeiro remover o item do cache antes de chamar o método Add. Isso ocorre porque internamente o método Add usa o método AddOrGetExisting, então ele não substitui o valor anterior. Para obter um valor, você pode usar o método Get ou chamar diretamente o indexador (cache[key]).

Vamos às diferentes formas de adicionar um item no cache:

  • Add(CacheItem, CacheItemPolicy)
  • Add(String key, Object value, DateTimeOffset absoluteExpiration)
  • Add(String key, Object value, CacheItemPolicy policy, String regionName)
  • AddOrGetExisting(CacheItem, CacheItemPolicy)
  • AddOrGetExisting(String key, Object value, DateTimeOffset absoluteExpiration)
  • AddOrGetExisting(String key, Object value, CacheItemPolicy policy, String regionName)
  • Set(CacheItem, CacheItemPolicy)
  • Set(String key, Object value, CacheItemPolicy policy, String regionName)

Embora numerosos, esses overloads são bem parecidos. Certifique-se de memorizá-los.

O efeito de todos eles é bem parecido. A principal diferença é que o método Add, por internamente usar o método AddOrGetExisting, retorna um bool indicando se o item foi inserido no cache ou se ele já existia. Já o método Set não tem retorno e atualiza o valor se o item já estiver no cache.

O objeto CacheItem tem três propriedades: Key, Value e RegionName.

No objeto CacheItemPolicy, você pode setar a política de expiração usando a propriedade AbsoluteExpiration ou a SlidingExpiration. A diferença entre as duas é que, com AbsoluteExpiration, você determina exatamente a data em que o item vai ser removido do cache. Já com SlidingExpiration, o item é removido do cache se não for utilizado após o tempo (TimeSpan) determinado.

Ainda no CacheItemPolicy, você pode configurar também a propriedade Priority com o enum CacheItemPriority. No System.Runtime.Caching (ObjectCache), ele tem dois possíveis valores: Default (que é o marcado por padrão) e NotRemovable. A propriedade Priority funciona assim: quando os recursos de processamento estão baixos, o runtime pode limpar algumas coisas da memória para melhorar o processamento. Os itens do cache estão incluídos nessa limpeza. Esse é o comportamento padrão, adotado quando a Priority está com o valor Default. Os itens do cache adicionados com o valor NotRemovable na propriedade Prority nunca são removidos do cache pelo runtime. Isso é perigoso pois a aplicação pode crashar devido à falta de memória.

O objeto CacheItemPolicy conta ainda com uma lista de ChangeMonitor. Esse objeto, como o nome sugere, lida com mudanças (eventos) que causam atualização do cache. ChangeMonitor é uma classe base que tem várias implementações:

  • CacheEntryChangeMonitor: serve como base para a construção de classes derivadas que monitoram os CacheItems
  • FileChangeMonitor: monitora um arquivo checando se há mudanças para atualizar o cache
  • HostFileChangeMonitor: monitora mudanças em diretórios e caminhos de arquivos, como o nome ou tamanho de um arquivo.

HttpContext.Cache

O HttpContext.Cache foi criado para ser usado especificamente numa aplicação web ASP.NET (fica em System.Web.Caching). Tome cuidado para não confundir os dois no exame. Se alguma questão mencionar o objeto HttpContext.Cache, Page.Cache ou somente Cache, você deve assumir que se trata de uma aplicação ASP.NET. Quaisquer outras menções à cache, pode se assumir que se trata do ObjectCache.

O jeito mais simples de adicionar ou atualizar um item no HttpContext.Cache é assim: Cache["FullNameKey"] = "John Q. Public";. Há também os métodos Add e Insert, que contêm várias sobrecargas (e infelizmente você também deve memorizar todas elas):

  • Insert(String Key, Object Value)
  • Insert(String Key, Object Value, CacheDependency dependencyItem)
  • Insert(String Key, Object Value, CacheDependency dependencyItem, DateTime absoluteExpiration, TimeSpan slidingExpiration)
  • Insert(String Key, Object Value, CacheDependency dependencyItem, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemUpdateCallback updateCallback)
  • Insert(String Key, Object Value, CacheDependency dependencyItem, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback removeCallback)
  • Add(string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback)

O método Add e a última forma do método Insert são virtualmente idênticos. A principal diferença está no método Insert que usa um CacheItemUpdateCallback, ao contrário do método Add que usa um CacheItemRemovedCallback. Em RemoveCallback é chamado quando o item é removido do cache, enquanto UpdateCallback é invocado um pouco antes do item ser removido do cache. Para a maioria das aplicações isso não faz muita diferença, mas a ideia é que se você precisa se certificar que um item nunca seja removido do cache, por exemplo, use UpdateCallback.

Assim como no ObjectCache, temos aqui também o enum CacheItemPriority, que funciona da mesma forma. A diferença é que aqui há mais opções de valores, com nomes bem sugestivos: Low, BelowNormal, Normal, AboveNormal, High, NotRemovable e Default. O valor padrão é Normal, que funciona da mesma forma que Default.

Para finalizar, há o parâmetro CacheDependency, que lida com cache de arquivos (como o HostFileChangeMonitor). Você pode passar também um objeto bem importante, o SqlCacheDependency. Ele tem dois construtores: um que aceita um SqlCommand e outro que aceita duas strings, a primeira sendo o nome do banco definido no web.config e a segunda é o nome da tabela que o cache será associado. Há muitos requisitos para se usar SqlCacheDependency, e alguns cuidados devem ser tomados.
Para o primeiro construtor, o banco de dados deve ser SQL Server 2005 ou superior. Dentro do SqlCommand, as tabelas devem ser referenciadas com o owner (dbo.Customer, por exemplo). SELECT * é proibido e a página não pode ter output caching.
O segundo é ainda mais complicado. Deve haver uma connection string no web.config da aplicação (obviamente). O banco de dados e a tabela devem ter notificações habilitadas no SQL Server e não é possível associar o cache com mais de uma tabela.

E é isso. O conteúdo desse objetivo foi bem simples, apesar de ser difícil decorar todos os overloads dos métodos de inserção.

Nos vemos no post do próximo objetivo, Implement transactions.
Até mais!

Leia Mais

Certificação Microsoft 70-487: Objetivo 1.1 – Choose data access technologies (Parte 3, WCF Data Services)

EDIT: Essa é a parte 3 de três posts sobre o objetivo 1.1. Você pode conferir a parte 1 aqui (ADO.NET) e a parte 2 aqui (Entity Framework).

Esse é o último post sobre o objetivo 1.1 da certificação Microsoft 70-487. Vou falar sobre a terceira opção de acesso à dados que contempla o primeiro objetivo, o WCF Data Services. As outras duas opções, ADO.NET e Entity Framework, você pode ver aqui e aqui.

WCF Data Services

WCF Data Services, antes chamado ADO.NET Data Services, é um serviço da Microsoft acoplado ao Entity Framework que permite expor seus dados na web via HTTP utilizando tecnologias como OData, REST e JSON.

Para criar um WCF Data Services, os seguintes passos são necessários:

  • Criar uma aplicação web ASP.NET;
  • Usar o Entity Framework para criar um EDM;
  • Adicionar um Data Service na aplicação;
  • Habilitar acesso ao serviço.

Com o WCF Data Services criado, o consumo desses dados pode ser feito simplesmente chamando URLs num browser. Por exemplo, para obter todos os Customers do seu banco de dados, basta acessar a URL http://my company uri/MyApplication/MyService.svc/Customers. Para retornar todos os Customers com o LastName ‘Doe’, a URL fica http://my company uri/MyApplication/ MyService.svc/Customers(‘Doe’).

Mais detalhes desses filtros na URL serão cobertos em outros objetivos.

Resumo Do Objetivo 1.1

Como o conteúdo sobre WCF Data Services é pequeno, vou aproveitar para colocar aqui o resumo do objetivo 1.1 do exame, Choose data access technologies. Lembrando que o resumo contempla as três opções de acesso à dados: ADO.NET, Entity Framework e WCF Data Services.

  • ADO.NET é a mais antiga das três opções e tem várias vantagens. Não requer conexão contínua com o banco de dados, é possível fazer conexão com qualquer banco de dados e possui objetos específicos para consumir os dados (DataSet e DataTable);
  • Com Entity Framework, é possível que os desenvolvedores foquem no código da aplicação, e não no banco de dados utilizado. Você pode criar seu modelo baseado num banco de dados existente ou contruir o banco de dados a partir de um modelo conceitual;
  • WCF Data Services permite que você exponha seus dados na web. O consumo dos dados, por ser via HTTP, não é atrelhado a nenhuma tecnologia. WCF Data Services é usado com Entity Framework no back end e provê um jeito fácil de construir e consumir um serviço na web.

E chegamos ao fim do objetivo 1.1 Smile Por ser mais longo, tive que dividir o tema em três partes, mas a ideia é que os próximos objetivos sejam cobertos em um só post.

Fique ligado no próximo objetivo, que será Implement caching. Até mais!

Leia Mais

Certificação Microsoft 70-487: Objetivo 1.1 – Choose data access technologies (Parte 2, Entity Framework)

EDIT: Essa é a parte 2 de três posts sobre o objetivo 1.1. Você pode conferir a parte 1 aqui (ADO.NET) e a parte 3 aqui (WCF Data Services).

Nesse post vou falar do Entity Framework, a segunda opção de acesso à dados que estará no exame da certificação Microsoft 70-487 dentro do objetivo 1.1, Choose data access technologies. A primeira parte, sobre ADO.NET, está nesse post.

Entity Framework

O EF foi criado para permitir que os desenvolvedores focassem no código da aplicação, não no código necessário para se comunicar com o banco de dados. Essa necessidade surgiu devido às diferenças estruturais entre a programação orientada a objeto e o modelo relacional dos bancos de dados.

Para o exame, é necessário conhecer a estrutura do EF. O model do Entity Framework é dividido em três partes: o CSDL (conceptual schema definition language), que contempla o modelo conceitual, o SSDL (store schema definition language), que trata dos aspectos da estrutura do banco de dados, e o que mapeia as duas partes é chamado de MSL (mapping specification language). As três partes ficam em um único arquivo com a extensão .edmx.

Há dois modos de criar seu modelo conceitual no EF, chamado de Entity Data Model (EDM): Database First, onde você cria seu banco de dados e depois cria seu modelo conceitual, e Model First, onde você primeiro constrói o modelo conceitual e permite que as ferramentas do EF criem a estrutura do banco de dados para você.

Quatro ferramentas estão disponíveis para você criar e modificar seu EDM. São elas:

  • Entity Model Designer, item que cria o arquivo .edmx e permite que você manipule quase todos os aspectos do model (criar, atualizar e deletar entidades, criar associações, etc.);
  • Entity Data Model Wizard, que é o ponto de partida para a criação do seu EDM no modelo Database First;
  • Create Database Wizard é o contrário do item anterior; essa ferramenta permite que você crie primeiro todo o seu modelo conceitual e depois gera o banco de dados necessário (Model First);
  • E Update Model Wizard, que permite modificações no EDM após o modelo estar criado.

Nota importantíssima: no exame (e no Visual Studio), o Entity Framework pode ser chamado de ADO.NET Entity Data Model, ou ADO.NET EF. Não se confunda com essa nomeclatura! Ambos estão citando apenas o Entity Framework.

Veja o código para acessar o banco de dados com o EF. Mais detalhes sobre a API do EF serão cobertos no capítulo 2, que é todo dedicado ao Entity Framework.


O EF consegue mapear heranças de duas principais formas: Table per Hierarchy (TPH) e Table per Type (TPT). No TPH, ele cria somente uma tabela que contém todos os derivados de uma herança (criando colunas nullable e uma coluna para indicar o tipo). Já no TPT, para cada filho, uma tabela é criada referenciando o pai com um ID. No EF é possível também criar Tipos Complexos (Complex Types). Numa tabela que tem as colunas DataInicio e DataFim, por exemplo, você pode identificar essas colunas como um tipo complexo e elas se tornarão uma entidade DataRange no seu modelo conceitual.

ObjectContext

No código acima, e nas versões novas do Entity Framework, você geralmente trabalhará com o DbContext (a classe TestEntities). Mas no exame outro (antigo, velho e sujo) modo de usar o EF será cobrado: o ObjectContext. Veja como ele pode ser utilizado abaixo:

O contexto da classe ObjectContext tem uma propriedade chamada ContextOptions que contém 5 propriedades que você pode manipular. São elas:

  • LazyLoadingEnabled. Marcada por padrão, essa propriedade controla se as entidades serão carregadas por demanda ou não. O EF faz o Lazy Loading pelas Navigation Properties. Por exemplo, ao carregar um Customer, suas Transactions não são carregadas. Ao iterar pela propriedade Transactions (uma Navigation Property), para cada Transaction na lista, um hit no banco é feito para obter os dados com o LazyLoadingEnabled setado true. Caso seja false, você precisa explicitamente carregar esses dados com o método LoadProperty() ou Include();
  • ProxyCreationEnabled determina se classes POCO (plain on commom object) devem ser criadas para dados customizados. Detalhes serão cobertos no capítulo 2 do livro;
  • UseConsistentNullReferenceBehavior funciona da seguinte forma: caso seja true e você sete uma Navigation Property que não esteja carregada como null, ao salvar, o EF tentará persistir isso no banco de dados (o que gerará um erro se a coluna não for nullable);
  • UseCSharpNullComparisonBehavior muda o jeito que as comparações com null são feitas no banco de dados. Veja a diferença:

    Nesse caso, as seguintes queries serão feitas no banco de dados:
    SELECT COUNT(*) FROM Account WHERE AccountAlias <> ‘Home’ — Disabled
    SELECT COUNT(*) FROM Account WHERE AccountAlias <> ‘Home’ OR AccountAlias IS NULL — Enabled
  • Por último, UseLegacyPreserveChangesBehavior controle o que acontece quando uma entidade já existe no contexto e é attachada novamente. Por padrão, o EF compara as duas entidades e, se alguma propriedade tiver valor diferente, o estado dessa propriedade vira Modified. Com o UseLegacyPreserveChangesBehavior, o valores diferentes não são copiados.

Você precisa se familiarizar também com as classes geradas pelo ObjectContext, assim como as geradas pelo DbContext (já mostradas acima). Cada entidade do modelo conceitual tem uma classe que herda de EntityObject. Essas classes são decoradas com três atributos:

  • EdmEntityTypeAttribute indica que a classe faz parte de um EDM. Contém o namespace e o name da entidade;
  • SerializableAttribute. As classes geradas pelo EF precisam ser serializáveis;
  • DataContractAttribute é necessário para se trabalhar com WCF (mais detalhes no objetivo 1.5).

As propriedades são decoradas com dois atributos: DataMemberAttribute, para fins de WCF, e EdmScalarPropertyAttribute, que seta duas propriedades (EntityKeyProperty e IsNullable). O código da classe fica assim:

Por Que Entity Framework?

  • Com as ferramentas se desenvolvendo cada vez mais, atualizar o modelo é muito menos trabalhoso do que com ADO.NET;
  •  Você pode focar na lógica do negócio da aplicação, e não com o armazenamento dos dados;
  • Atualmente, o EF permite a desacoplação entre o modelo conceitual e o armazenamento dos dados;
  • O banco de dados pode ser mudado sem ter a necessidade de reescrever todo o código da aplicação;
  • O EF foi pensado para trabalhar em conjunto com WCF.

O próximo post será sobre o último ponto do objetivo 1.1 da certificação, que é sobre o WCF Data Services.

Até mais!

Leia Mais

Certificação Microsoft 70-487: Objetivo 1.1 – Choose data access technologies (Parte 1, ADO.NET)

EDIT: Essa é a parte 1 de três posts sobre o objetivo 1.1. Você pode conferir a parte 2 aqui (Entity Framework) e a parte 3 aqui (WCF Data Services).

O primeiro tópico da certificação Microsoft 70-487 é Accessing data. Você pode conferir todos os tópicos e seus objetivos aqui.

Vamos falar do primeiro objetivo, Choose data access technologies, que se trata de como escolher uma tecnologia de acesso a banco de dados baseado nos requisitos da aplicação.

As três opções que serão cobradas no exame são:

Farei um post para cada uma das opções, pois esse primeiro objetivo é bem extenso.

ADO.NET

ADO.NET é a tecnologia mais antiga das três e, portanto, considerada a mais madura e performática.

A primeira característica abordada no livro é a de que ADO.NET usa um modelo desconectado quanto a conexões no banco de dados. Ou seja, a cada comando (INSERT, UPDATE ou DELETE) ou query (SELECT), uma conexão é aberta e imediatamente fechada após o uso. Isso é uma vantagem pois evita todos os erros provenientes de se manter longas conexões abertas com o banco de dados, como processamento, rede e limite de conexões abertas ao mesmo tempo.

Outro ponto positivo é que ADO.NET é compatível com vários sistemas de banco de dados. No pacote System.Data, há classes específicas para alguns deles, como System.Data.SqlClient e System.Data.OracleClient, e também genéricas como System.Data.OleDb ou System.Data.Odbc.

Arquitetura

O .NET framework, no pacote System.Data, oferece várias classes apelidadas “data providers” que são designadas para lidar com as questões básicas de banco de dados, como fazer uma conexão, executar um comando e trabalhar com os dados que vieram do banco.

Nesse momento, o livro foca na diferença entre duas classes para obter dados: DataSet (ou DataAdapter) e DataReader. Note a diferença entre como as duas funcionam:

As principais vantagens do DataReader são:

  • Como internamente um DataAdapter usa um DataReader, o último tem melhor performance;
  • DataReader possui métodos assíncronos (BeginExecuteNonQuery, BeginExecuteReader, BeginExecuteXmlReader), enquanto DataAdapter não;
  • O método Fill de um DataAdapter, conforme mostrado acima, só popula DataSets e DataTables. Com DataReaders, trabalhar com objetos de negócio é mais fácil.

Já as vantagens do DataAdapter são:

  • É possível mapear os relacionamentos do banco de dados usando a propriedade Relations;
  • O método Fill lê todos os registros do comando de uma vez, quando no DataReader você deve iterar por todos os registros manualmente (while (reader.Read()) …);
  • DataSets são serializáveis. Eles podem ser carregados ou persistidos de um XML, por exemplo. Podem também ser salvos na Session ou em um ViewState (você não vai querer fazer isso).

Por Que ADO.NET?

No exame, muitas perguntas são do tipo “dada uma situação, escolha as tecnologias que seriam as mais apropriadas”. Às vezes é difícil identificar, mas o livro é bem focado e já apresenta as informações dessa forma. Vamos às vantagens de se utilizar ADO.NET:

  • Consistência: ADO.NET é a mais antiga e estável das opções disponíveis. Foi muito utilizada e testada.
  • Estabilidade em termos de evolução e qualidade. ADO.NET está bem estabelecida e é pouco provável que mude ou tenha novas features.
  • ADO.NET é fácil de entender. Sabendo o conceito de como funciona, não há muitos segredos. Por existir há muito tempo, há providers para quase todos os tipos de bancos de dados e uma documentação bem abrangente.
  • É possível utilizar ADO.NET com Azure SQL sem problema algum.

Fique ligado no próximo post, que será sobre a segunda opção de tecnologia para acesso à banco de dados: o Entity Framework.

Até mais!

Leia Mais

Certificação Microsoft 70-487: Developing Microsoft Azure and Web Services

EDIT: Completei as postagens sobre cada objetivo do exame. Os links estão aqui.

Olá pessoal!

Devido a um desejo pessoal e necessidade na Lambda3, recentemente tracei um objetivo para a minha carreira de me tornar MCSD em Web Applications.

Para obter essa certificação da Microsoft, é necessário ser aprovado em três exames:

  • 70-480: Programming in HTML5 with JavaScript and CSS3
  • 70-486: Developing ASP.NET MVC Web Applications
  • 70-487: Developing Microsoft Azure and Web Services

Decidi começar pela certificação Microsoft 70-487: Developing Microsoft Azure and Web Services, que é considerada a mais difícil das três. Passando nesse exame, acredito que eu fique mais motivado para fazer os outros dois e as coisas fluam melhor.

Como Vou Passar No Exame

A primeira coisa que eu fiz foi pesquisar rapidamente na internet sobre a prova para achar quais pontos serão abordados no exame, onde posso achar conteúdo para estudar e relatos de pessoas que passaram no teste. (mais…)

Leia Mais

Novo Desafio: Lambda3

Como contei no artigo sobre a minha trajetória profissional, estou no começo do meu caminho na Lambda3 e gostaria de deixar um pouco minha visão e expectativas dessa experiência.

A Lambda3 é uma empresa de desenvolvimento de software focada em conceitos e metodologias ágeis. Até aí, normal. O que torna a Lambda3 bem diferente do convencional é a chamada Democracia Organizacional. No blog da Lambda3 há vários posts que explicam o que é e como a Democracia Organizacional funciona na empresa, desde o processo de contratação até decisões financeiras e comerciais. Mas, para mim, um aspecto em especial foi bem difícil de acreditar que era possível. (mais…)

Leia Mais

Minha Trajetória Profissional

Agora que já escrevi um pouco sobre algumas coisas elementares do início da carreira de programador (escola, faculdade e estágio), vou começar a postar sobre a carreira de programador no mercado de trabalho, que é onde você vai se desenvolver para ter o sucesso que deseja. Acho legal iniciar esse assunto contando como foi a minha trajetória profissional. No post Como Conseguir um Estágio, contei rapidamente sobre como eu fiz para conquistar cada oportunidade como estagiário. Agora vou revelar um pouco do que aprendi em cada experiência e espero que você possa se beneficiar disso. (mais…)

Leia Mais