- En la Parte 1 se explica cómo instalar y configurar Couchbase en Windows.
- En la Parte 2 se trató algo de la jerga de Couchbase que necesitarás conocer
- La parte 3 mostraba el ejemplo más simple de uso de Couchbase en ASP.NET
En esta entrada del blog, voy a desarrollar la parte 3 presentando Linq2Base. También voy a mover Couchbase fuera del controlador y ponerlo en un muy básico repositorio clase. Mi objetivo de esta entrada de blog es que te sientas cómodo con los fundamentos de Couchbase y Linq2Couchbase, y seas capaz de empezar a aplicarlo en tu aplicación web.
Mover Couchbase fuera del Controlador
El trabajo del Controlador es dirigir el tráfico: tomar las peticiones entrantes, entregarlas a un modelo, y luego dar los resultados a la vista. Para seguir el Principios SÓLIDOS (concretamente el Principio de Responsabilidad Única), el acceso a los datos debe estar en algún lugar de un "modelo" y no en el controlador.
El primer paso es refactorizar el código existente. Podemos mantener el 'ejemplo realmente simple' de la Parte 3, pero debería ser movido a un método en otra clase. Aquí está el HomeController refactorizado y el nuevo PersonRepository:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public class HomeController : Controller { private readonly PersonRepository _personRepo; public HomeController(PersonRepository personRepo) { _personRepo = personRepo; } public ActionResult Index() { var person = _personRepo.GetPersonByKey("foo::123"); return Content("Name: " + person.name + ", Address: " + person.address); } } public class PersonRepository { private readonly IBucket _bucket; public PersonRepository(IBucket bucket) { _bucket = bucket; } public dynamic GetPersonByKey(string key) { return _bucket.Get(key).Value; } } |
Algunas cosas a tener en cuenta:
- HomeController ya no depende directamente de Couchbase. Si la API de Couchbase cambiara, por ejemplo, sólo tendríamos que hacer cambios en PersonRepository, no en HomeController.
- No tuve que decirle explícitamente a StructureMap cómo instanciar PersonRepository. Se imagina que PersonRepository es "auto-vinculante". Si tuviera que utilizar una interfaz en su lugar (como IPersonRepository), tendría que hacer un cambio en DefaultRegistry para indicárselo a StructureMap. Si estás utilizando un contenedor IoC diferente, tu situación puede ser diferente.
Refactorización para utilizar una clase Persona
En el ejemplo anterior, estoy utilizando un dinámico objeto. dinámico es genial para algunas situaciones, pero en este caso, sería una buena idea llegar a una definición más concreta de lo que es una "Persona". Puedo hacerlo con una clase C#.
|
1 2 3 4 5 6 |
public class Person { public string Name { get; set; } public string Address { get; set; } } |
También actualizaré el PersonRepository para que utilice esta clase.
|
1 2 3 4 5 |
public Person GetPersonByKey(string key) { return _bucket.Get(key).Value; } |
Mientras estamos en ello, voy a tomar algunas medidas para hacer esto más de una aplicación MVC adecuada. En lugar de devolver Content(), voy a hacer que la acción Index devuelva una View, y voy a pasarle un método lista de objetos Persona. Crearé un archivo Index.cshtml, que delegará en un parcial de _person.cshtml. También voy a dejar caer en un diseño que utiliza Bootstrap. Esta última parte es completamente gratuita, pero hará que las capturas de pantalla se vean un poco más bonitas.
Nueva acción del Índice:
|
1 2 3 4 5 6 7 |
public ActionResult Index() { var person = _personRepo.GetPersonByKey("foo::123"); var list = new List {person}; return View(list); } |
Index.cshtml:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@model List<CouchbaseAspNetExample2.Models.Person> @{ ViewBag.Title = "Home : Couchbase & ASP.NET Example"; } @if (!Model.Any()) { <p>There are no people yet.</p> } @foreach (var item in Model) { @Html.Partial("_person", item) } |
persona.cshtml:
|
1 2 3 4 5 6 7 8 9 |
@model CouchbaseAspNetExample2.Models.Person <div <span class="hljs-keyword">class</span>=<span class="hljs-string">"panel panel-default"</span>> <div <span class="hljs-keyword">class</span>=<span class="hljs-string">"panel-heading"</span>> <h2 <span class="hljs-keyword">class</span>=<span class="hljs-string">"panel-title"</span>>@Model.Name</h2> </div> <div <span class="hljs-keyword">class</span>=<span class="hljs-string">"panel-body"</span>> @Html.Raw(Model.Address) </div> </div><code> |
Ahora se ve un poco mejor. Además, podremos mostrar toda una lista de documentos Persona más adelante en la demo.

Presentación de Linq2Couchbase
Couchbase Server soporta un lenguaje de consulta conocido como N1QL. Es un superconjunto de SQL, y le permite aprovechar su conocimiento existente de SQL para construir consultas muy potentes sobre documentos JSON en Couchbase. Linq2Couchbase va un paso más allá y convierte las consultas Linq en consultas N1QL (al igual que Entity Framework convierte las consultas Linq en consultas SQL).
Linq2Couchbase forma parte de Laboratorios Couchbasey aún no forma parte de la librería principal y soportada del SDK .NET de Couchbase. Sin embargo, si estás acostumbrado a Entity Framework, NHibernate.Linq, o cualquier otro proveedor de Linq, es una gran manera de introducirse a Couchbase. Para algunas operaciones, todavía necesitarás usar el núcleo de Couchbase .NET SDK, pero hay mucho que podemos hacer con Linq2Couchbase.
Empieza por añadir Linq2Couchbase con NuGet (si aún no lo has hecho).

N1QL (y, por tanto, Linq2Couchbase) depende de la aplicación cubo indexado. Entra en la consola de Couchbase, haz clic en la pestaña 'Query' y crea un índice primario en el archivo hello-couchbase cubo.

Si no tienes un índice, Linq2Couchbase te dará un útil mensaje de error como "No primary index on keyspace hello-couchbase. Use CREATE PRIMARY INDEX para crear uno".
Para utilizar Linq2Couchbase de la forma más eficaz, tenemos que empezar a dar a los documentos de Couchbase un campo de "tipo". De esta manera, podemos diferenciar entre un documento de "persona" y un documento de "ubicación", por ejemplo. En este ejemplo, sólo voy a tener documentos de "persona", pero es una buena idea hacer esto desde el principio. Voy a crear un campo Tipo, y establecerlo en "Persona". También pondré un atributo en la clase C# para que Linq2Couchbase entienda que esta clase es para un cierto tipo de documento.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
using Couchbase.Linq.Filters; [DocumentTypeFilter("Person")] public class Person { public Person() { Type = "Person"; } public string Type { get; set; } public string Name { get; set; } public string Address { get; set; } } |
Si realiza estos cambios, su aplicación seguirá funcionando. Esto se debe a que todavía estamos recuperando el documento por su clave. Pero ahora cambiemos la acción Índice para intentar obtener TODOS los documentos de Persona.
|
1 2 3 4 5 6 |
public ActionResult Index() { var list = _personRepo.GetAll(); return View(list); } |
Necesitaremos implementar ese nuevo método de repositorio GetAll:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
using System.Collections.Generic; using System.Linq; using Couchbase.Core; using Couchbase.Linq; using Couchbase.Linq.Extensions; using Couchbase.N1QL; public class PersonRepository { private readonly IBucket _bucket; private readonly IBucketContext _context; public PersonRepository(IBucket bucket, IBucketContext context) { _bucket = bucket; _context = context; } public List GetAll() { return _context.Query() .ScanConsistency(ScanConsistency.RequestPlus) .OrderBy(p => p.Name) .ToList(); } } |
En este ejemplo, le estoy diciendo a Couchbase que ordene todos los resultados por Nombre. Si quieres, puedes experimentar con los métodos Linq normales a los que estás acostumbrado: Where, Select, Take, Skip, etc.
Ignora la Consistencia de Escaneo por ahora: Hablaré de ello más adelante. ¿Pero qué pasa con ese IBucketContext? El IBucketContext es similar al DbContext de Entity Framework, o al ISession de NHibernate. Para obtener ese IBucketContext, necesitaremos actualizar el DefaultRegistry.
|
1 2 3 4 5 |
For().Singleton().Use("Get a Couchbase Bucket", x => ClusterHelper.GetBucket("hello-couchbase", "password!")); For().HttpContextScoped().Use("Get a Couchbase Bucket Context", x => new BucketContext(x.GetInstance())); |
Esto le dice a StructureMap que quiero crear un nuevo BucketContext y que quiero que se aplique a cada solicitud HTTP. Si utiliza HttpContextScoped en StructureMap, también tendrá que utilizar HttpContextLifecycle.DisposeAndClearAll() en el Application_EndRequest. Si estás utilizando un contenedor IoC diferente, tendrás que gestionarlo de forma diferente.
Ahora, si vuelves a compilar y ejecutar la aplicación web, aparecerá el mensaje "There are no people yet". Eh, ¡¿dónde me he metido?! No aparecí porque el documento "foo::123" aún no tiene un campo "type". Ve a la consola de Couchbase y añádelo.

Una vez hecho esto, actualiza tu página web y la persona volverá a aparecer.
Nota rápida sobre ScanConsistency
Linq2Couchbase se basa en un índice para generar y ejecutar consultas. Cuando añades nuevos documentos, el índice debe ser actualizado. Hasta que el índice no se actualice, los documentos aún no indexados no serán devueltos por Linq2Couchbase (por defecto). Añadiendo en ScanConsistency de RequestPlus (Consulte la documentación de Couchbase para obtener más información sobre la coherencia del análisis.), Linq2Couchbase esperará hasta que el índice se actualice antes de ejecutar una consulta y devolver una respuesta. Se trata de un compromiso que deberás tener en cuenta a la hora de diseñar tu aplicación. ¿Qué es más importante: la velocidad bruta o la precisión total?
Como ejemplo sencillo, supongamos que está creando un sistema de gestión de contenidos:
- Si está creando herramientas de administración, probablemente valore más la precisión total que el rendimiento.
- Los administradores necesitan saber exactamente qué contienen los datos para poder gestionarlos con eficacia.
- Las funciones de administración se utilizan con poca frecuencia en comparación con las funciones públicas, por lo que es aceptable cierta latencia.
- Si estás creando una página pública que enumera todo el contenido, la velocidad bruta es probablemente más importante.
- Si una nueva página de contenido tarda uno o dos segundos más en aparecer al público, no pasa nada.
- La parte pública de un sitio se visitará con mucha frecuencia, por lo que el rendimiento es un factor importante.
- Esto es sólo un ejemplo: el tipo de Consistencia de Exploración que debe utilizar depende de usted y de sus casos de uso.
Conclusión
Linq2Couchbase es una potente herramienta para trabajar con Couchbase de forma familiar. Es de código abierto, pero aún no está soportada oficialmente por Couchbase. He puesto todo el código de esta entrada de blog a disposición en Github.
En el próximo post, te mostraré cómo usar Linq2Couchbase para crear, actualizar y borrar documentos. También veremos la diferencia en flexibilidad que Couchbase puede darte comparado con un RDBMS tradicional como SQL Server.
¿Tiene alguna pregunta? ¿Algo no funciona como esperabas? Déjanos un comentario, ping me on Twittero envíame un correo electrónico (matthew.groves AT couchbase DOT com) y te ayudaré.