- 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 |
público clase InicioControlador : Controlador { privado sólo lectura Depósito de personas personaRepo; público InicioControlador(Depósito de personas personaRepo) { personaRepo = personaRepo; } público AcciónResultado Índice() { var persona = personaRepo.GetPersonByKey("foo::123"); devolver Contenido("Nombre: " + persona.nombre + ", Dirección: " + persona.dirección); } } público clase Depósito de personas { privado sólo lectura IBucket _bucket; público Depósito de personas(IBucket cubo) { _bucket = cubo; } público dinámico GetPersonByKey(cadena clave) { devolver _bucket.Visite(clave).Valor; } } |
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 |
público clase Persona { público cadena Nombre { consiga; configure; } público cadena Dirección { consiga; configure; } } |
También actualizaré el PersonRepository para que utilice esta clase.
1 2 3 4 5 |
público Persona GetPersonByKey(cadena clave) { devolver _bucket.Visite(clave).Valor; } |
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 |
público AcciónResultado Índice() { var persona = personaRepo.GetPersonByKey("foo::123"); var lista = nuevo Lista {persona}; devolver Ver(lista); } |
Index.cshtml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@modelo Lista<CouchbaseAspNetEjemplo2.Modelos.Persona> @{ ViewBag.Título = "Inicio : Ejemplo de Couchbase y ASP.NET"; } @si (!Modelo.Cualquier()) { <p>Allí son no gente pero.</p> } @foreach (var artículo en Modelo) { @Html.Parcial("_persona", artículo) } |
persona.cshtml:
1 2 3 4 5 6 7 8 9 |
@modelo CouchbaseAspNetEjemplo2.Modelos.Persona <div <span clase="hljs-palabra clave">clase</span>=<span clase="hljs-string">"panel panel-default"</span>> <div <span clase="hljs-palabra clave">clase</span>=<span clase="hljs-string">"panel-encabezado"</span>> <h2 <span clase="hljs-palabra clave">clase</span>=<span clase="hljs-string">"panel-title"</span>>@Modelo.Nombre</h2> </div> <div <span clase="hljs-palabra clave">clase</span>=<span clase="hljs-string">"panel-cuerpo"</span>> @Html.En bruto(Modelo.Dirección) </div> </div><código> |
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 |
utilizando Couchbase.Linq.Filtros; [Filtro de tipo de documento("Persona")] público clase Persona { público Persona() { Tipo = "Persona"; } público cadena Tipo { consiga; configure; } público cadena Nombre { consiga; configure; } público cadena Dirección { consiga; configure; } } |
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 |
público AcciónResultado Índice() { var lista = personaRepo.GetAll(); devolver Ver(lista); } |
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 |
utilizando Sistema.Colecciones.Genérico; utilizando Sistema.Linq; utilizando Couchbase.Núcleo; utilizando Couchbase.Linq; utilizando Couchbase.Linq.Extensiones; utilizando Couchbase.N1QL; público clase Depósito de personas { privado sólo lectura IBucket _bucket; privado sólo lectura IBucketContext _contexto; público Depósito de personas(IBucket cubo, IBucketContext contexto) { _bucket = cubo; _contexto = contexto; } público Lista GetAll() { devolver _contexto.Consulta() .Consistencia de escaneado(Consistencia de escaneado.SolicitarPlus) .OrderBy(p => p.Nombre) .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 |
Para().Singleton().Utilice("Consigue un Couchbase Bucket", x => ClusterHelper.GetBucket("hello-couchbase", "¡Contraseña!")); Para().HttpContextScoped().Utilice("Obtener un contexto de cubo Couchbase", x => nuevo 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é.