Hoy lanzamos otra versión de mantenimiento y corrección de errores del SDK .NET de Couchbase: 2.0.2. Esta es la continuación de las versiones 2.0 y 2.0.1 y proporciona soporte para N1QL DP4, que ya se ha fusionado con la rama maestra.
¿Qué contiene esta versión?
A principios de este mes, publicamos una versión preliminar para desarrolladores del SDK compatible con N1QL DP4. N1QL DP4 trae una API REST completamente nueva y soporte para nuevas características como sentencias preparadas, parámetros con nombre y posicionales y tiempos de espera. En esta versión hemos estabilizado las interfaces y el modelo de programación para trabajar con N1QL en el SDK. Además, actualizamos de Common.Logging 2.0 a 3.0 (deshaciéndonos así de la dependencia de los paquetes NuGet obsoletos), añadimos soporte para tiempos de espera de conexión configurables y mejoramos las interfaces ClusterHelper e IBucket.
Soporte N1QL DP4
El SDK es ahora compatible con las siguientes funciones de la API REST de N1QL: parámetros con nombre y posicionales, sentencias y sentencias preparadas, tiempo de espera, errores, métricas, sólo lectura, firma e id de contexto de cliente.
Parámetros nominales y parámetros posicionales
Cuando se crea una sentencia y se le quieren pasar parámetros, por ejemplo como parte de la cláusula WHERE, se puede hacer de dos formas: parámetros con nombre y parámetros opcionales. Los parámetros con nombre adoptan la forma de una variable con el prefijo "$". Al crear la consulta y ejecutarla, se envían al servidor y se sustituyen por el valor que proporcione para la variable. Aquí tiene un ejemplo de cómo hacer esto con el 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); } |
A diferencia de los parámetros Nominal, los parámetros Posicional asocian un valor a una variable ordinal de la sentencia. El orden en que se añaden los parámetros define el orden de sustitución variable/valor por el servidor cuando se ejecuta la consulta. Por ejemplo
|
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 se trata exactamente de la misma consulta que la primera, sólo que hemos utilizado Parámetros Nombrados en lugar de Parámetros Posicionales.
Declaraciones y declaraciones preparadas
El SDK divide el N1QL en dos cosas distintas: el lenguaje en sí y la API para ejecutar consultas N1QL. Una "consulta" N1QL se denomina sentencia y es una cadena de código N1QL que será enviada al servidor para su ejecución y los resultados devueltos al cliente y mapeados en el Tipo dinámico o como POCO. A continuación se muestra un ejemplo de ejecución de una sentencia N1QL utilizando un 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); } |
Cuando el servidor recibe una sentencia N1QL, compila un plan de consulta para la sentencia. Esto toma tiempo y recursos, por lo que sería mejor si los resultados de este paso fueran almacenados en caché y reutilizados una y otra vez. Para eso están las sentencias preparadas. Para utilizar sentencias preparadas con el SDK, simplemente hay que establecer la propiedad prepared en el 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 el cliente ejecutará la sentencia, obtendrá la sentencia preparada y las llamadas posteriores reutilizarán la sentencia preparada almacenada en caché. Si ejecutas el código anterior con un temporizador, observarás que la primera llamada tarda bastante más que las siguientes: ese es el efecto de crear la sentencia preparada y almacenarla en caché.
Errores
La nueva API también tiene una nueva API de "errores" que se asigna al objeto QueryResult. Si se produce un error al procesar una solicitud, el motor N1QL devolverá un objeto subdocumento llamada errores que luego se asigna a una clase en el SDK llamada convenientemente, "Error":

Los campos se definen del siguiente modo:
| Campo | Significado | Opcional |
| Código | Un número único para el error o advertencia. | Falso |
| Mensaje | Una descripción detallada del error o advertencia. | Falso |
| Nombre | Un nombre único que tiene una correspondencia 1:1 con el código e identifica la condición que causó el error o la advertencia. | Verdadero |
| Gravedad | Uno de los siguientes niveles de gravedad N1QL: Grave, Advertencia, Información o Error | Verdadero |
| Temp | Indica que el error o advertencia es temporal y si es falso, reintentar la petición tendrá el mismo resultado. | Verdadero |
Métricas
Las métricas proporcionan, bueno métricas, con respecto a la sentencia que fue ejecutada por el servidor. Tenga en cuenta que hay un parámetro de métricas del lado del servidor que es verdadero por defecto, por lo que si la solicitud no incluye un parámetro de métricas, éstas seguirán siendo enviadas. Para deshabilitar las métricas, establece el siguiente campo en tu objeto QueryRequest antes de ejecutarlo:
|
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); } |
En general, para la mayoría del código de producción no se desea que se devuelvan métricas, sin embargo, lo contrario es cierto para entornos de desarrollo.
Sólo lectura, firmas e Id. de contexto de cliente
Las describiré brevemente. Readonly es una configuración del servidor que también puede ser anulada por el cliente. En general, esto será cierto para todas las peticiones GET, sin embargo hay una configuración a nivel de servidor que reemplazará esto. Firma es una cabecera opcional para el esquema de resultados. Client Context ID es una cadena opcional de 64 caracteres que es proporcionada por el cliente y devuelta por el servidor. Su propósito principal es rastrear y depurar peticiones.
¿Qué ha cambiado?
Registro común 3.0
Las versiones 2.0.0 y 2.0.1 dependían de la versión 2.0 de Common.Logging Library. En la versión 2.0.2 actualizamos esta dependencia a Common.logging 3.0. Puedes leer sobre el motivo de la actualización aquí. La buena noticia es que ya no verá este mensaje confuso cuando intente añadir una dependencia a Log4Net o NLog en Nuget:

La mala noticia es que es probable que tenga que hacer cambios en su App.Config o Web.Config para apoyar el cambio en la Asamblea de nomenclatura que el proyecto Common.Logging está utilizando ahora. Por ejemplo, para añadir una dependencia de Log4Net tendrá que utilizar la "nueva" convención de nomenclatura en su App.Config o Web.Config a partir de ahora:

A algo como esto:

Por supuesto, el nombre del ensamblado que suministre será el de la versión de la que haya tomado una dependencia.
Cambios en ICluster y ClusterHelper
Se ha añadido un nuevo método a la interfaz ICluster, por tanto al propio objeto Cluster: IsOpen(string bucketName). El propósito de este método es, lo has adivinado, determinar si un objeto Cluster contiene una referencia a una implementación de IBucket. Esto es útil para pruebas y quizás dentro de tu aplicación cuando quieras gestionar tus referencias IBucket.
El ClusterHelper también ha recibido un poco de amor: ¡ahora es un "multi-ton" para cubos y también un singleton para instancias de Cluster! ¿Por qué lo hemos hecho? Para facilitar la gestión de referencias a clústeres y cubos en entornos multihilo, como las aplicaciones ASP.NET. La API tiene ahora este aspecto:

Las grandes novedades son GetBucket(bucketName) y RemoveBucket(bucketName) Si abre un cubo utilizando GetBucket(bucketName), la referencia se almacenará en caché y las siguientes solicitudes del cubo con el mismo nombre devolverán el objeto de cubo almacenado en caché.
RemoveBucket(bucketName) eliminará la instancia del bucket del objeto Cluster. Tenga en cuenta que se almacenará una y sólo una referencia por nombre de cubo, pero que puede abrir tantos diferente (por ejemplo, default, beer-sample, etc.) como desee. Además de GetBucket(bucketName) y RemoveBucket(bucketName), se ha añadido otro método, Count, que proporciona un recuento de los buckets abiertos que mantiene el ClusterHelper. Por último, cuando se llama a Close() en el ClusterHelper, todos de los cubos se cerrará.
Cambios en ViewRow
La propiedad ViewRow.Key ha pasado de ser un tipo System.object a ser un tipo dinámico. Esto se ha hecho para que la propiedad refleje la estructura JSON de la clave y no se filtren los tipos de la API de serialización JSON subyacente.
Corrección de errores
La lista de tickets de Jira incluidos en esta versión puede consultarse en las notas de la versión aquí.