Chalalo Land

Tecnologías ASP.NET y un poco Más

Contacto


 Si quierer cooperar, yo feliz, muy agradecido :)

De donde me Visitan?

Locations of visitors to this page

Generic Content

Si te gustaron los articulos, y te animas te estaría muy agradecido!


Recent Posts

Tags

Community

Blogs de MVP

Amigos Geeks

Blogs Imperdibles

GODS

Archives

Email Notifications

Tutorial MongoDB con ASP.NET MVC - Ejemplo Práctico

Hola que tal, este tutorial lo estaba preparando hace un tiempo atrás, pero por cosas del destino, venia trazado con esto. Es esta oportunidad, vamos revisar un completo (quiero creerlo, aunque no es tan completo), caso de uso de MongoDB con ASP.NET MVC. Quiero aclarar primero que este articulo es parte de mi aprendizaje con MongoDB, no soy un experto, de hecho, espero que expertos critiquen mi trabajo de manera de aprender más, es decir, este articulo esta hecho con la excelente metodología “aprender haciendo”, y no enfurecerse por los errores.

Pero estoy seguro que nadie quiere leer mis penas, si no el artículo, veamos el contexto de la aplicación: “Un sencillo (paupérrimo) sistema de Blog, en donde se puedan ingresar post y se pueda además agregar comentarios a ese post.” Si lo vemos en más detalle:

Página Principal

image 1) Ingreso de el Post a MongoDB, por un tema de ahorrar código, no hice las validaciones para el ingreso. Los Tags puedes separarlos por espacios, puntos o punto y coma.

2) Listar todos los Post que se han ingresado, mostrando como link el título y luego la fecha de publicación. El link nos enviará a la página de detalle y allí se mostrará el texto del post.

3) Se muestran la cantidad de comentarios asociados y la lista de tags ingresados, separados por ;

4) Posibilidad de borrar el Post, si bien no tiene mucho sentido hacerlo acá, es por mostrar la funcionalidad.

Página Detalle

image 1) Mostrar el Titulo y el texto ingresado para el Post

2) Al igual que la página anterior mostramos la lista de tags separados por punto y coma.

3)Formulario para el ingreso de comentarios, esta vez usé solo input type=”text”, no quise usar textarea, solo por diseño. Al igual que la pantalla anterior, no tengo validada las entradas, por un tema de simplicidad.

4) Listado de Comentarios asociados al post, cada vez que ingresamos uno comentario, se refleja en esta lista.

Todo esto lo haremos almacenando los datos en MongoDB, así que vamos por parte

Parte 1 – MongoDB

¿Que es MongoDB?

Es un sistema de Bases de datos OpenSource, multiplataforma basado en Documentos JSON, de esquemas libres, lo que significa que cada registro puede tener un esquema de datos distinto, es decir, que  una colección puede contener registros con distintas estructuras. Las características más relevantes, (además de que es OpenSource), son la velocidad (es muuuy rápido) , su capacidad para soportar ambientes de alto rendimiento y su motor de consultas, simple y poderoso. Te recomiendo que veas los siguientes links para profundizar:

  • http://www.genbetadev.com/bases-de-datos/una-introduccion-a-mongodb
  • http://www.4tic.com/blog/2011/06/29/mongodb-la-mysql-del-nosql/

    Instalación de MongoDB en Windows 7

    Primero que nada, tenemos que descargar MongoDB desde el WebSite del proyecto: http://www.mongodb.org/downloads , como puedes ver tienes la posibilidad de descargarlo para bastantes plataformas.

  • Descarga  y  descomprime  el Zip 32-bit. El compilado "Production" es el recomendado.
  • Descarga y descomprime el Zip 64-bit
    Nota, existen limitaciones de tamaño de almacenamiento en la versión de 32 Bits, que la puedes revisar en:http://blog.mongodb.org/post/137788967/32-bit-limitations

    Te recomiendo descomprimir el archivo y cambiar el nombre de la carpeta a algo más corto, por ejemplo, simplemente mongo.

    Crear el directorio de Datos

    Por defecto, MongoDB almacenará la data en la carpeta \db\data, pero esta no es creada automáticamente, por lo que debemos hacerlo nosotros.

    C:\> mkdir \data
    C:\> mkdir \data\db

    Y listo!, ya tenemos instalado MongoDB. Sonrisa

    Levantar el server y conectarnos a él.

    Los binarios importantes que están en la carpeta \mongo\bin son:

  • mongod.exe – que corresponde al servidor, prueba con mongod –help para las opciones
  • mongo.exe – El Shell para administración

    En mi caso , entro a c:\mongo\bin\mongod.exe para correr el server:

    image

    Quizás esta opción de levantar manualmente el servidor no nos acomode, pero descuida, el manual para instalarlo como servicio lo puedes encontrar acá :http://www.mongodb.org/display/DOCS/Windows+Service

    La consola de administración por defecto nos conecta al el servidor de MongoDB corriendo en localhost, y usa una base de datos llamada test, podemos ver más opciones para el arranque de el shell con mongo --help.

    image

    Luego descargue el Driver para C#, ya que vamos a programar la aplicación, yo descargué el MSI desde: https://github.com/mongodb/mongo-csharp-driver/downloads en mi caso la versión 1.3

    Para ver una descripción de los comandos de administración del Shell puedes visitar http://www.mongodb.org/display/DOCS/Developer+Zone 

    image

    Se instala en Archivos de Programa\MongoDB\CsharpDriver 1.3

    image

    Y estamos listos con la instalación y el driver. Luego obviamente tenemos que hacer la referencia en el proyecto MVC 3.

    image

     

    Parte 2 - Creación del Blog “La Preparación”

    Vamos a utilizar 3 clases, Artículo, Comentario y Tag, el modelo de clases el siguiente:

    image

    Y el código:

    using System;

    using System.Collections.Generic;

    using System.Web;

    using MongoDB.Bson.Serialization.Attributes;

    using MongoDB.Bson.Serialization.IdGenerators;

    using MongoDB.Bson;

     

    namespace Blog.Models

    {

        public class Tag

        {

            public string tag { get; set; }

        }

     

     

        public class Comentario

        {

            [BsonId]

            public ObjectId id { get; set; }

            public string fecha { get; set; }

            public string usuario { get; set; }

            public string texto { get; set; }

     

        }

     

        public class Articulo

        {

            public Articulo()

            {

             Tags =new List<Tag>();

             Comentarios = new List<Comentario>();

            }

            [BsonId]

            public ObjectId id { get; set; }

            public string titulo {get;set;}

            public string texto { get; set; }

            public string fecha { get; set; }

            public List<Tag> Tags { get; set; }

            public List<Comentario> Comentarios { set; get; }

        }

     

     

    }

    Como puedes ver el Objeto Articulo contiene una lista de Tags y una lista de comentarios, quise a propósito tener objetos “más complejos” que una colección Key-Value simple, para colocarme en un escenario un poco más real y que sirve al amigo lector Sonrisa

    MondoDB automáticamente asigna un id a cada objeto, sin tener que nosotros agregar una propiedad,  sin embargo por facilidad en el manejo y a manera de demo, voy a agregar un id propio, sin embargo, decoro el atributo con BsonId indicando así que es un id de tipo BSON (Propiedades de MongoDB). La fecha la dejé como string, no voy a hacer búsquedas sobre ella, hay un tema de performance de cuidado sobre los datetime y su conversión.

    La Estructura del proyecto entonces, es la siguiente:

    image

    Parte 3 – Pagina de Ingreso de POST (Index.cshtml)

    Veamos el Index.cshtml y revisaremos la implementación de las siguientes funcionalidades:

  • Ingreso de datos (Post  y Tags)
  • Listado de Post
  • Contador de Comentarios
  • Borrar Post

    Primero que nada, vamos a ver el listado de datos, para eso veremos la acción del controlador y el método del modelo asociado:

    Controlador

      public ActionResult Index()

      {

         ArticulosModel model = new ArticulosModel();

         ViewBag.Articulos = model.ObtArticulos();

         return View();

       }

    Modelo

    public IEnumerable<Articulo> ObtArticulos()

    {

    MongoDatabase db = Db();

    MongoCollection<Articulo> Articulos=db.GetCollection<Articulo>("articulos");

    return Articulos.FindAll().ToList<Articulo>();

    }

    La función de conexión devuelve  un objeto MongoDatabase  referenciando a la base de datos en la que estoy trabajando, en mi caso mydb, no es necesario que la crees antes, si no que mongoDB la crea onfly si es que no existe (lo mismo que las colecciones).

    private MongoDatabase Db()

      {

        string connectionString = "mongodb://localhost";

        MongoServer server = MongoServer.Create(connectionString);

        MongoDatabase db = server.GetDatabase("mydb");

        return db;

    }

     

    Ahora si analizamos el código del Modelo, podemos ver que primero creamos el objeto conexión a partir del método Db (El cual veremos a continuación). Luego obtenemos toda la colección con GetColletion, nota que la colección es tipada (Articulo), una MongoCollection de objeto articulo. Luego retornamos todos los registros (findAll), revisa la documentación de la api de CSharp http://www.mongodb.org/display/DOCS/CSharp+Driver+Tutorial para obtener mas información.

    En resumen, obtenemos toda la colección de “articulos”, con GetCollection<Articulo>(“articulos”), este último parámetro indica la colección dentro de MongoDB.

    En el controlador retorna hacia la vista,en el diccionario dinámico ViewBag, la lista de artículos. Como puedes ver esta vista no es tipada, de todos modos podemos recorrer la lista fácilmente con un foreach. Dentro de este ciclo, vamos a tener otro foreach en donde vamos a recorrer la lista de tags (articuloItem.Tags).

    Puedes notar que existen dos links, en los cuales se utiliza el helper @Url, el primero hace referencia a la Acción Detalle del Controlador Artículos, el cual nos envia a la vista de detalle, y el siguiente hace referencia a la acción Borrar del controlador Articulos. en estos Links ocupo el Id de Articulo como parámetro en la url, en las vistas y controlador lo vamos a tratar como un String y luego, al momento de acceder a la colección de MongoDB vamos a convertirlo a un ObjectID

    <section id="listado">
      <ul>
       @foreach (Blog.Models.Articulo articuloItem in ViewBag.Articulos)
        {
         <li>

           <p>

            <a class="Titulo"

                href="@Url.Action("Detalle", "Articulos"
                new { Guid = articuloItem.id })">
    @articuloItem.texto</a>
            <
    br
    />

            (@articuloItem.fecha)</p>

                    @articuloItem.Comentarios.Count() Comentarios, Tags:

                    @foreach (Blog.Models.Tag tagItem in articuloItem.Tags)

               {

                            <text> @tagItem.tag ;</text>

               }

          <p>

          <a class="Borrar" href="@Url.Action("Borrar", "Articulos",
                        new { Guid = articuloItem.id })">

                        Borrar</a>
           </
    p
    >

         </li> }

        </ul>

    </section>

    Lo anterior nos dibuja lo siguiente en pantalla:

    image

    Como vez, bastante fácil el recorrer la colección. Veamos ahora como se implementa la acción Borrar.

    Acción Borrar

    Controlador

    public ActionResult Borrar(string Guid)

    {

       ArticulosModel model = new ArticulosModel();

       model.Borrar(Guid);

       return RedirectToAction("Index");

    }

    Modelo

    public void Borrar(string Guid)

    {

       MongoDatabase db = Db();

       db.GetCollection<Articulo>("articulos").FindAndRemove(Query.EQ("_id",

           ObjectId.Parse(Guid)), SortBy.Ascending("id"));

    }

    Al igual que el ejemplo anterior, en el controlador, obtenemos la referencia a mongoDB, y luego obtenemos la colección, y de inmediato utilizamos el método FindAndRemove y le pasamos la query que compara equivalencia (EQ) del campo “_id”, y le pasamos el campo Guid parseado a ObjectId. No podemos pasar directamente sin parsear el Id, ya que la equivalencia no se cumpliría. Puedes ver la manera de almacer el id en la colección escribiendo db.articulos.find() en el shell, fijate que el campo Id es _id:

    image

    En resumen, esta sola línea de código permite obtener la colección filtrada mediante la query EQ, y a la vez, remover los elementos de ese filtro, es realmente fantástico que con una sola línea podamos hacer tanto sobre mongo.

    Acción Detalle

    Controlador

    public ActionResult Detalle(string Guid)

    {

       if (Guid == null)

       {

          Guid = TempData["ObjId"].ToString();

       }

       ArticulosModel model = new ArticulosModel();

       ViewBag.Id = Guid;

       return View(model.Detalle(Guid));

    }

    Modelo 

    public Articulo Detalle(string Guid)

    {

    MongoDatabase db = Db();

    return db.GetCollection<Articulo>("articulos").FindOneById(ObjectId.Parse(Guid));

    }

     

    De igual manera que para borrar un registro, se obtiene la colección y obtenemos mediante el método FindOneById (parseando el Id) un único registro el cual retornaremos al controlador Detalla y este a su vez a la vista tipada. El if que vez en el controlador lo utilizo para determinar el id si es que es la primera carga de la vista detalle o si es que se creó un nuevo comentario en la vista  detalle, de manera de mantener del id.

    Formulario de Inserción

    Veamos el formulario, comentarios de CSS aparte (no soy diseñador, hago todo absolutamente horrible).

     

    image

    El código asociado a este formulario es el siguiente:

    <section id="contenido">

        @using (Html.BeginForm("Create", "Articulos",

                FormMethod.Post, new { id = "formulario" }))

        {

            <label for="titulo">Título:</label>

            <input type="text" id="titulo" name="titulo" />

            <label for="titulo">Texto:</label>

            <textarea id="texto" name="texto" rows="3" cols="40"></textarea>

            <label for="tags">Tags</label>

            <input type="text" id="tags" name="tags" />

            <input type="submit" value="Grabar!" id="grabar" />

        }

    </section>


    Muy simple la verdad, ahora veamos el Controlador y el Modelo

    Controlador

    [HttpPost]
    public ActionResult Create(FormCollection form)

    {

      Articulo articulo = new Articulo();

      articulo.titulo = form["titulo"];

      articulo.texto = form["texto"];

      articulo.fecha = Convert.ToString(DateTime.Now);

      string tags = form["tags"];

      char[] delimitadores = { ' ', ',', '.', ';' };

      string[] TagsString = tags.Split(delimitadores);

      foreach (String tagtexto in TagsString)

      {

        articulo.Tags.Add(new Tag() { tag = tagtexto });

      }

      ArticulosModel model = new ArticulosModel();

      model.Insertar(articulo);

      return RedirectToAction("Index");

    }

    Modelo

    public void Insertar(Articulo articulo)
    {
    MongoDatabase db = Db();
    MongoCollection<Articulo> articulos = db.GetCollection<Articulo>("articulos");
    articulos.Insert(articulo.ToBsonDocument());

    }

     

    Nota que en el controlador, recibimos los objetos del formulario y los vamos asignando a un nuevo objeto Articulo, luego vamos separando los tags mediante delimitadores y los vamos ingresando a la lista de objetos tag de Articulo. Una vez que este listo el objeto, se lo pasamos al método del modelo para que lo inserte. Y nuevamente, crear la conexión, obtener la colección para luego insertar el articulo, parseado a una clase BsonDocument, que es con lo que trabaja MongoDB. El controlador nos devuelve a la acción Index, que nos va a recargar la vista.

    Página detalle (Detalle.cshtml)

    La página de detalle es una vista tipada, básicamente tiene el mismo funcionamiento que la página anterior, por lo que solo me voy a enfocar a las funcionalidades que varían.

    Un tema importante para la funcionalidad del detalle, es agregar nuevos comentarios, esto implica obtener el objeto Articulo desde la colección y luego agregarle un nuevo objeto comentario, para posteriormente almacenarlo.

    Veamos la inserción de un nuevo comentario:

    Controlador

    [HttpPost]

    public ActionResult CrearComentario(FormCollection form)

    {

      ArticulosModel model = new ArticulosModel();

      model.CrearComentario(form["id"].ToString(),

      new Comentario()

      {

        usuario = form["usuario"].ToString(),

        texto = form["comentario"].ToString(),

        fecha = Convert.ToString(DateTime.Now)

      });

      TempData["ObjId"] = form["id"].ToString();

      return RedirectToAction("Detalle");

    }

    Modelo

    public void CrearComentario(string Guid, Comentario comentario)

    {

        MongoDatabase db = Db();

        var articulos = db.GetCollection<Articulo>("articulos");

        Articulo a = articulos.FindOneById(ObjectId.Parse(Guid));

        a.Comentarios.Add(comentario);

        articulos.Save<Articulo>(a);

    }

    En el controlador creamos el comentario, a partir de los datos obtenidos desde el formulario, recuerda que debemos almacenar en algún elemento de formulario el id del articulo, para obtener el elemento desde la colección, llamamos al método del modelo llamado CrearComentario y pasamos como argumento el nuevo objeto, luego retornamos a la acción detalle, lo que produce evidentemente una recarga de página y podremos ver la lista de comentarios actualizada. Es en esta acción donde utilizamos TempData para enviar el ObjId hacia la acción Detalle).

    En el modelo, como ya habíamos dicho, obtenemos el articulo (objeto a), y agregamos el comentario a la lista de comentarios (a.Comentarios.Add(comentario)). Luego ( y con una facilidad fascinante) , simplemente guardamos el articulo con el método de MongoDB Save, indicando el tipo y el objeto que vamos a guardar.

    En la vista, el formulario tiene el siguiente código, en el que puedes ver que tenemos un campo oculto con el Id, el cual es asignado al ViewBag en el controlador Articulo, en la acción Detalle. Veamos el código:

    <p>

    @using (Html.BeginForm("CrearComentario", "Articulos", FormMethod.Post, new { id = "formularioPost" }))

    {

      <label for="usuario">Usuario:</label>

      <input type="text" id="usuario" name="usuario" required="true"
             placeholder
    ="Ingrese su Nombre:"
    />

      <label for="comentario">Comentario:</label>

      <input type="text" id="comentario" name="comentario" 
             required="true" placeholder="Ingrese Comentario:"
    />

        

      <input type="submit" value="Comentar!" id="comentar" />

      <input  type="hidden" value="@ViewBag.Id" id="" name="Id" />

    }

    </p>

     

    image

    Luego, todos los comentarios ingresados son mostrador en la tabla siguiente:

    image

    Cuyo código es muy simple, recordemos que esta vista es tipada, por lo que podemos recorrer la colección de Comentarios fácilmente:

    <ul>
    @foreach (Blog.Models.Comentario comentarioItem in @Model.Comentarios)

    {

       <li>
         <p>@comentarioItem.texto</p>

         <p>Posteado el @comentarioItem.fecha By @comentarioItem.usuario</p>

       </li>

    }

    </ul>

    Puedes utilizar MongoVUE para visualizar los datos , en el caso que no te guste utilizar la Shell, descárgalo en  http://www.mongovue.com/, yo descargué la versión gratis, aunque el producto no es caro, de hecho estamos evaluando el comprarlo, ya que ayuda bastante

    image

     

    Nos conectamos y podemos ver la colección y su estructura.

    image

     

    Nota que los comentarios tienen el _id 00000000000, debido a que no los estoy asignando en la creación del comentario,  ya que no voy a hacer nada con esto, sin embargo, si vas a hacer filtros mediante los comentarios, es necesario que si tengan un Id asignado.

     

    Descarga el ejemplo!! Sonrisa

    image

    Sumario

    No soy para nada un experto en MongoDB, simplemente dije “voy a investigar, hacer una aplicación de prueba, y escribir en el blog lo que aprendí”, y dicho sea de paso, proponerlo para la búsqueda entre miles de registros para un sistema en producción, para esto tenía que convencer a mi jefe para que diera el OK a utilizar MongoDB, y bueno, ese es tema aparte, pero lo aprobó Sonrisa

     

    Personalmente me sorprendió la rapidez, y su nueva filosofía, distinta a mis ojos que solo conocían bases de datos relacionales. La API de C# provee de prácticamente  de todo lo necesario. Quizás puedo criticar la falta e ejemplos en C# o que yo soy malo para buscar información, justamente fue esto lo que me inspiró a escribir este pequeño tutorial, de manera de poder ayudar a los que andan tan perdidos como yo.

    Seguramente hay gente que puede aportar mejorando este código, sea bienvenido, de esa manera yo también aprendo Sonrisa

    Espero que te sirva!! y ya tengo otro articulo sobre búsquedas de texto, en VBNET y Mongo Risa

    Saludos!

    brown

    Posted: 2/12/2011 14:40 por Gonzalo Perez | con 2 comment(s)
    Archivado en: ,,
    Comparte este post:

    Comentarios

    MVP Factor ha opinado:

    Lunes, 19 de diciembre Optimización de infrastructura SQL Server 1pm -6GMT Webcast

    # December 19, 2011 7:37 PM

    Chalalo Land ha opinado:

    Hola, en esta primera parte del un tutorial en el cual vamos a estar trabajando con ASP.NET Web API junto

    # March 11, 2012 8:26 PM