Hoje estamos lançando outra versão de manutenção e correção de bugs do Couchbase .NET SDK: 2.0.2. Essa é uma versão posterior à 2.0 e à 2.0.1 e, principalmente, oferece suporte ao N1QL DP4, que agora foi mesclado com a ramificação principal.
O que há nesta versão?
No início deste mês, lançamos uma prévia para desenvolvedores do SDK para oferecer suporte ao N1QL DP4. O N1QL DP4 traz uma API REST totalmente nova e suporte para novos recursos, como instruções preparadas, parâmetros nomeados e posicionais e tempos limite. Nesta versão, estabilizamos as interfaces e o modelo de programação para trabalhar com o N1QL no SDK. Além disso, atualizamos do Common.Logging 2.0 para o 3.0 (eliminando assim a dependência dos pacotes NuGet obsoletos), adicionamos suporte a tempos limite de conexão configuráveis e aprimoramos as interfaces ClusterHelper e IBucket.
Suporte a N1QL DP4
O SDK agora é compatível com os seguintes recursos da API N1QL REST: parâmetros nomeados e posicionais, instruções e instruções preparadas, tempo limite, erros, métricas, somente leitura, assinatura e ID do contexto do cliente.
Parâmetros nomeados e parâmetros posicionais
Quando você cria uma declaração e deseja passar parâmetros junto com ela, por exemplo, como parte da cláusula WHERE, agora é possível fazer isso de duas maneiras: parâmetros nomeados e parâmetros opcionais. Os parâmetros nomeados assumem a forma de uma variável prefixada com o cifrão "$". Quando você cria a consulta e a executa, eles são enviados ao servidor e substituídos pelo valor que você forneceu para a variável. Aqui está um exemplo de como fazer isso com o SDK:
|
1 2 3 4 5 6 7 8 9 10 |
<span style="color: blue"> var</span> query = <span style="color: #a31515">"SELECT * FROM `beer-sample` WHERE type=$type LIMIT $limit"</span>; <span style="color: blue">var</span> queryRequest = <span style="color: blue">new</span> <span style="color: #2b91af">QueryRequest</span>(query) .AddNamedParameter(<span style="color: #a31515">"$type"</span>, <span style="color: #a31515">"beer"</span>) .AddNamedParameter(<span style="color: #a31515">"$limit"</span>, 10); <span style="color: blue">var</span> results = bucket.Query<<span style="color: blue">dynamic</span>>(queryRequest); <span style="color: blue">foreach</span> (<span style="color: blue">var</span> result <span style="color: blue">in</span> results.Rows) { <span style="color: #2b91af">Console</span>.WriteLine(result); } |
Em contraste com os parâmetros nomeados, os parâmetros posicionais associam um valor a uma variável ordinal na declaração. A ordem em que os parâmetros são adicionados define a ordem da substituição da variável/valor pelo servidor quando a consulta é executada. Por exemplo:
|
1 2 3 4 5 6 7 8 9 10 |
<span style="color: blue"> var</span> query = <span style="color: #a31515">"SELECT * FROM `beer-sample` WHERE type=$1 LIMIT $2"</span>; <span style="color: blue">var</span> queryRequest = <span style="color: blue">new</span> <span style="color: #2b91af">QueryRequest</span>(query) .AddPositionalParameter(<span style="color: #a31515">"beer"</span>) .AddPositionalParameter(10); <span style="color: blue">var</span> results = bucket.Query<<span style="color: blue">dynamic</span>>(queryRequest); <span style="color: blue">foreach</span> (<span style="color: blue">var</span> result <span style="color: blue">in</span> results.Rows) { <span style="color: #2b91af">Console</span>.WriteLine(result); } |
Observe que essa é exatamente a mesma consulta que a primeira, apenas usamos parâmetros nomeados em vez de parâmetros posicionais.
Declarações e declarações preparadas
O SDK divide o N1QL em duas coisas distintas: a linguagem em si e a API para execução de consultas N1QL. Uma "consulta" N1QL é chamada de instrução e é uma cadeia de código N1QL que será enviada ao servidor para execução e os resultados serão retornados ao cliente e mapeados no tipo dinâmico ou como um POCO. Aqui está um exemplo de execução de uma instrução N1QL usando um objeto RequestQuery:
|
1 2 3 4 5 6 7 8 |
<span style="color: blue"> var</span> queryRequest = <span style="color: blue">new</span> <span style="color: #2b91af">QueryRequest</span>() .Statement(<span style="color: #a31515">"SELECT * FROM `beer-sample`"</span>); <span style="color: blue">var</span> results = bucket.Query<<span style="color: blue">dynamic</span>>(queryRequest); <span style="color: blue">foreach</span> (<span style="color: blue">var</span> result <span style="color: blue">in</span> results.Rows) { <span style="color: #2b91af">Console</span>.WriteLine(result); } |
Quando o servidor receber uma instrução N1QL, ele compilará um plano de consulta para a instrução. Isso leva tempo e recursos, portanto, seria melhor se os resultados dessa etapa fossem armazenados em cache e reutilizados várias vezes. É exatamente para isso que servem os comandos preparados. Para usar instruções preparadas com o SDK, basta definir a propriedade prepared no objeto QueryRequest:
|
1 2 3 4 5 6 7 8 9 |
<span style="color: blue"> var</span> queryRequest = <span style="color: blue">new</span> <span style="color: #2b91af">QueryRequest</span>() .Statement(<span style="color: #a31515">"SELECT * FROM `beer-sample`"</span>) .Prepared(<span style="color: blue">true</span>); <span style="color: blue">var</span> results = bucket.Query<<span style="color: blue">dynamic</span>>(queryRequest); <span style="color: blue">foreach</span> (<span style="color: blue">var</span> result <span style="color: blue">in</span> results.Rows) { <span style="color: #2b91af">Console</span>.WriteLine(result); } |
Observe que o cliente executará o comando, obterá o comando preparado e as chamadas subsequentes reutilizarão o comando preparado armazenado em cache. Se você executar o código acima com um cronômetro, perceberá que a primeira chamada demora muito mais do que as chamadas subsequentes: esse é o efeito de criar o prepared statement e armazená-lo em cache.
Erros
A nova API também tem uma nova API de "erros" que é mapeada para o objeto QueryResult. Se ocorrer um erro durante o processamento de uma solicitação, o mecanismo N1QL retornará um objeto subdocumento chamada errors, que é então mapeada para uma classe no SDK chamada convenientemente de "Error":

Os campos são definidos da seguinte forma:
| Campo | Significado | Opcional |
| Código | Um número exclusivo para o erro ou aviso. | Falso |
| Mensagem | Uma descrição detalhada do erro ou aviso. | Falso |
| Nome | Um nome exclusivo que tem uma correspondência 1:1 com o código e identifica a condição que causou o erro ou o aviso. | Verdadeiro |
| Gravidade | Um dos seguintes níveis de gravidade do N1QL: Severo, Aviso, Informação ou Erro | Verdadeiro |
| Temp | Indica que o erro ou aviso é temporário e, se for falso, tentar novamente a solicitação resultará no mesmo resultado. | Verdadeiro |
Métricas
As métricas fornecem, bem, métricas referentes à declaração que foi executada pelo servidor. Observe que há um parâmetro de métricas do lado do servidor que é verdadeiro por padrão, portanto, se a solicitação não incluir um parâmetro de métricas, elas ainda serão enviadas. Para desativar as métricas, defina o seguinte campo em seu objeto QueryRequest antes de executá-lo:
|
1 2 3 4 5 6 7 8 9 |
<span style="color: blue"> var</span> queryRequest = <span style="color: blue">new</span> <span style="color: #2b91af">QueryRequest</span>() .Statement(<span style="color: #a31515">"SELECT * FROM `beer-sample`"</span>) .Metrics(<span style="color: blue">false</span>); <span style="color: blue">var</span> results = bucket.Query<<span style="color: blue">dynamic</span>>(queryRequest); <span style="color: blue">foreach</span> (<span style="color: blue">var</span> result <span style="color: blue">in</span> results.Rows) { <span style="color: #2b91af">Console</span>.WriteLine(result); } |
Em geral, para a maioria dos códigos de produção, você não deseja que as métricas sejam retornadas, mas o inverso é verdadeiro para ambientes de desenvolvimento.
Readonly, assinaturas e ID do contexto do cliente
Vou descrevê-las brevemente. Readonly é uma configuração em todo o servidor que também pode ser substituída pelo cliente. Em geral, isso será verdadeiro para todas as solicitações GET, mas há uma configuração em todo o servidor que substituirá essa. Signature (Assinatura) é um cabeçalho opcional para o esquema de resultados. A ID do contexto do cliente é uma cadeia de 64 caracteres opcional fornecida pelo cliente e retornada pelo servidor. Sua principal finalidade é rastrear e depurar solicitações.
O que mudou?
Registro comum 3.0
As versões 2.0.0 e 2.0.1 dependem da versão 2.0 da biblioteca Common.Logging. Na versão 2.0.2, atualizamos essa dependência para a Common.logging 3.0. Você pode ler sobre o motivo da atualização aqui. A boa notícia é que você não verá mais essa mensagem confusa quando tentar adicionar uma dependência ao Log4Net ou ao NLog no Nuget:

A má notícia é que você provavelmente precisará fazer alterações no App.Config ou no Web.Config para dar suporte à alteração na nomenclatura do Assembly que o projeto Common.Logging está usando agora. Por exemplo, para adicionar uma dependência do Log4Net, você precisará usar a "nova" convenção de nomenclatura em seu App.Config ou Web.Config a partir deste ponto:

Para algo parecido com isso:

É claro que o nome do Assembly que você forneceria ali seria o da versão da qual você se tornou dependente.
Alterações no ICluster e no ClusterHelper
Um novo método foi adicionado à interface ICluster e, portanto, ao próprio objeto Cluster: IsOpen(string bucketName). O objetivo desse método é, você adivinhou, determinar se um objeto do Cluster está mantendo uma referência a uma implementação do IBucket. Isso é útil para testes e, talvez, dentro do seu aplicativo, quando você quiser gerenciar as referências do IBucket.
O ClusterHelper também recebeu um pouco de atenção: agora ele é um "multi-ton" para buckets e também um singleton para instâncias do Cluster! Por que fizemos isso? Simplesmente para facilitar o gerenciamento de referências de cluster e bucket em ambientes multithread, como aplicativos ASP.NET. A API agora tem a seguinte aparência:

As grandes adições são GetBucket(bucketName) e RemoveBucket(bucketName). Se você abrir um bucket usando GetBucket(bucketName), a referência será armazenada em cache e as solicitações subsequentes para o bucket com o mesmo nome resultarão no retorno do objeto de bucket armazenado em cache.
RemoveBucket(bucketName) removerá a instância do bucket do objeto Cluster. Observe que uma e somente uma referência será armazenada por nome de bucket, mas que você pode abrir quantos diferente (ou seja, padrão, amostra de cerveja, etc.) como você desejar. Além de GetBucket(bucketName) e RemoveBucket(bucketName), foi adicionado outro método, Count, que fornece uma contagem dos buckets abertos que estão sendo mantidos pelo ClusterHelper. Por fim, ao chamar Close() no ClusterHelper, todos das caçambas serão fechadas.
Alterações no ViewRow
A propriedade ViewRow.Key foi alterada de um tipo System.object para um tipo dinâmico. Isso foi feito para que a propriedade reflita a estrutura JSON da chave e não vaze os tipos da API de serialização JSON subjacente.
Correções de bugs
A lista de tíquetes do Jira que estão incluídos nessa versão pode ser encontrada nas notas da versão aqui.