Este artículo proviene de un motor de traducción automática.

Base de datos de documento NoSQL

Incrustar RavenDB en una aplicación ASP.NET MVC 3

Justin Schwartzenberger

Descargar el ejemplo de código

Atención a la circulación de NoSQL está creciendo dentro de Microsoft.NET Comunidad marco como seguimos oír hablar de las empresas compartir sus experiencias de implementación de la misma en las aplicaciones que conocemos y utilizar.Con esto generó mayor conciencia viene la curiosidad para profundizar e identificar cómo un almacén de datos NoSQL podría ofrecer beneficios o otras posibles soluciones para el software que los desarrolladores crean actualmente.Pero ¿dónde inicial, y cómo es de difícil la curva de aprendizaje?Quizás una preocupación aún más relevante: ¿cuánto tiempo y esfuerzo son necesarios para iniciar una nueva solución de almacenamiento de datos y empezar a escribir código con ella?¿Después de todo, tiene el proceso de instalación para una nueva aplicación a una ciencia, el derecha de SQL Server?

Word ha alcanzado el.NET Comunidad sobre las alas de un cuervo sobre una opción nueva para una implementación de la capa de datos de tipo NoSQL.RavenDB (ravendb.net) es una base de datos de documentos diseñado para el.Plataforma NET de Windows, empaquetado con todo lo que necesita para empezar a trabajar con un almacén de datos no relacionales.RavenDB almacena los documentos como JSON de menos de esquema.Existe una API RESTful para la interacción directa con el almacén de datos, pero la verdadera ventaja reside dentro de la.NET cliente API que viene incluido con la instalación.Implementa el modelo de unidad de trabajo y aprovecha la sintaxis LINQ para trabajar con documentos y las consultas.Si ya ha trabajado con un asignador de relacional de objeto (ORM) — como el marco de la entidad (EF) o NHibernate — o consume un servicio de datos de WCF, se sentirá derecho en el hogar con la arquitectura de API para trabajar con documentos en RavenDB.

La curva de aprendizaje para poner en ejecución con una instancia de RavenDB es breve y elegante.De hecho, la pieza que puede requerir más planeamiento es la estrategia de licencias (pero aún es mínima).RavenDB ofrece una licencia de open source para los proyectos que son también open source, pero hace falta una licencia comercial para proyectos comerciales de origen cerrada.Detalles de la licencia y la fijación de precios pueden encontrarse en ravendb.NET/licencias.El sitio afirma que licencia libre está disponible para las empresas de inicio o aquellos que buscan se utiliza en un proyecto de origen no comercial, cerrado.En cualquier caso, es conveniente repasar rápidamente las opciones para conocer la implementación a largo plazo potencial antes de cualquier desarrollo de prototipos o recinto de seguridad.

RavenDB incrustados y MVC

RavenDB se puede ejecutar en tres modos diferentes:

  1. Como un servicio de Windows
  2. Como una aplicación de IIS
  3. Incrustado en una.NET application

Los dos primeros tienen un proceso de instalación bastante sencillo, pero que vienen con alguna sobrecarga de la estrategia de implementación.La tercera opción, incrustada, es muy fácil de poner en funcionamiento.De hecho, hay un paquete de NuGet disponible para él.Una llamada para el siguiente comando en la consola de Administrador de paquetes en Visual Studio 2010 (o una búsqueda para el término "ravendb" en el cuadro de diálogo Administrar paquetes de NuGet) proporcionará todas las referencias necesarias para empezar a trabajar con la versión integrada en RavenDB:

Install-Package RavenDB-Embedded

Detalles del paquete pueden encontrarse en el sitio Galería de NuGet en bit.ly/ns64W1.

Agregar la versión incorporada de RavenDB a una aplicación ASP.NET MVC 3 aplicación es tan simple como agregar el paquete a través de NuGet y dando el almacén de datos de una ubicación del directorio de archivos. Debido a que ASP.NET tienen un directorio de datos conocidos en el marco de trabajo denominada App_Data y la mayoría de las empresas de alojamiento proporcionan acceso de lectura y escritura a ese directorio con poca o ninguna configuración necesaria, es un buen lugar para almacenar los archivos de datos. Cuando RavenDB crea su almacenamiento de información de archivo, genera un puñado de directorios y archivos en la ruta del directorio que le proporcionen. No creará un directorio de nivel superior para almacenar todo. Sabiendo que, merece la pena agregar la página de ASP.NET denominada App_Data mediante el menú contextual del proyecto en 2010 de Visual Studio de carpeta y, a continuación, cree un subdirectorio en el directorio App_Data de la RavenDB de datos (consulte figura 1).

App_Data Directory Structure
Figura 1 estructura de directorio App_Data

Un almacén de datos de documentos es menor de esquema por naturaleza, por lo tanto, no hace falta para crear una instancia de una base de datos o configurar las tablas. Una vez realizada la primera llamada para inicializar el almacén de datos en el código, se crearán los archivos necesarios para mantener el estado de los datos.

Trabajar con la API del cliente RavenDB a la interfaz con el almacén de datos requiere una instancia de un objeto que implementa la interfaz Raven.Client.IDocumentStore que se crea e inicializa. La API tiene dos clases, DocumentStore y EmbeddedDocumentStore, que implementan la interfaz y se pueden utilizar en función del modo en que se ejecuta RavenDB. Sólo debe haber una instancia por cada almacén de datos durante el ciclo de vida de una aplicación. Puedo crear una clase para administrar una única conexión a mi tienda de documento que me permitirá tener acceso a la instancia del objeto IDocumentStore a través de una propiedad estática y tiene un método estático para inicializar la instancia (consulte figura 2).

Figura 2 clase para DocumentStore

public class DataDocumentStore
{
  private static IDocumentStore instance;
 
  public static IDocumentStore Instance
  {
    get
    {
      if(instance == null)
        throw new InvalidOperationException(
          "IDocumentStore has not been initialized.");
      return instance;
    }
  }
 
  public static IDocumentStore Initialize()
  {
    instance = new EmbeddableDocumentStore { ConnectionStringName = "RavenDB" };
    instance.Conventions.IdentityPartsSeparator = "-";
    instance.Initialize();
    return instance;
  }
}

El captador de propiedad estática comprueba un campo de respaldo estáticos privados de un objeto nulo y, si es null, inicia una excepción InvalidOperationException. Genero una excepción aquí, en lugar de llamar al método Initialize, para mantener el código seguro para subprocesos. Si se permitiera que la propiedad de instancia para hacer que la llamada y la aplicación confiaban en referencia a la propiedad para realizar la inicialización, habría una oportunidad de que más de un usuario podría escribir la aplicación al mismo tiempo, resultando en llamadas simultáneas para el método Initialize. Dentro de la lógica del método Initialize, creo una nueva instancia de la Raven.Client.Embedded.EmbeddableDocumentStore y establezca la propiedad de ConnectionStringName en el nombre de una cadena de conexión que se ha agregado al archivo web.config por la instalación del paquete de RavenDB NuGet. En el archivo web.config, establezco el valor de la cadena de conexión a una sintaxis que comprende RavenDB a fin de configurarlo para utilizar la versión local incrustada del almacén de datos. También puedo asignar el directorio del archivo en el directorio de base de datos creado en el directorio App_Data del proyecto MVC:

<connectionStrings>
  <add name="RavenDB " connectionString="DataDir = ~\App_Data\Database" />
</connectionStrings>

La interfaz IDocumentStore contiene todos los métodos para trabajar con el almacén de datos. Devolver y almacenar el objeto EmbeddableDocumentStore como una instancia del tipo de interfaz IDocumentStore para tener la posibilidad de cambiar la creación de instancias del objeto EmbeddedDocumentStore a la versión del servidor (DocumentStore) para alejarse de la versión incrustada. De este modo, todo el código de lógica que controle la administración de objetos de documento se se desacople de los conocimientos del modo en que se ejecuta RavenDB.

RavenDB creará las claves de ID de documento en un formato similar al resto de forma predeterminada. Un objeto de "Elemento" obtendría una clave en el formato "elementos/104". El nombre del modelo de objeto se convierte a minúsculas y es plural y un único número de identidad de seguimiento se anexa después de una barra diagonal con cada nueva creación de documentos. Puede ser problemático en una aplicación MVC, como la barra diagonal provocará un nuevo parámetro de ruta ser analizados. La API de cliente de RavenDB proporciona una manera de cambiar la barra diagonal estableciendo el valor de IdentityPartsSeparator. En mi método DataDocumentStore.Initialize, estoy ajustando el valor de IdentityPartsSeparator a un guión antes de que se llama al método de inicialización en el objeto EmbeddableDocumentStore, para evitar el problema de enrutamiento.

Adición de una llamada al método estático DataDocumentStore.Initialize desde el método Application_Start en el archivo Global.asax.cs de mi aplicación MVC establecerá la instancia de IDocumentStore en la primera ejecución de la aplicación, que tiene este aspecto:

protected void Application_Start()
{
  AreaRegistration.RegisterAllAreas();
  RegisterGlobalFilters(GlobalFilters.Filters);
  RegisterRoutes(RouteTable.Routes);
 
  DataDocumentStore.Initialize();
}

Desde aquí puedo hacer uso de la IDocumentStore objeto con una llamada estática a la propiedad DataDocumentStore.Instance para trabajar con objetos de documento de Mi almacén de datos incrustado dentro de mi aplicación MVC.

Objetos RavenDB

Para obtener una mejor comprensión de RavenDB en acción, crearé una aplicación de prototipo para almacenar y administrar los marcadores. RavenDB está diseñado para trabajar con objetos de CLR antiguos sin formato (POCOs), por lo que es necesario agregar atributos de propiedad para guiar la serialización. Crear una clase para representar un marcador es bastante sencillo. Figura 3 muestra la clase de marcador.

Figura 3 clase de marcador

public class Bookmark
{
  public string Id { get; set; }
  public string Title { get; set; }
  public string Url { get; set; }
  public string Description { get; set; }
  public List<string> Tags { get; set; }
  public DateTime DateCreated { get; set; }
 
  public Bookmark()
  {
    this.Tags = new List<string>();
  }
}

Cuando va a almacenar el documento RavenDB serializará los datos del objeto en una estructura JSON. El conocido "Id" propiedad con nombre se utilizará para controlar la clave de ID de documento. RavenDB creará ese valor — siempre la propiedad Id está vacío o nulo cuando se realiza la llamada para crear el nuevo documento y los almacenará en un elemento de @ metadatos para el documento (que se utiliza para controlar la clave del documento en el nivel de almacén de datos). Cuando se solicita un documento, el código de la API de cliente de RavenDB establecer la clave de identificación de documento a la propiedad Id cuando se carga el objeto document.

La serialización JSON de un documento de marcador de muestra se representa en la siguiente estructura:

{
  "Title": "The RavenDB site",
  "Url": "http://www.ravendb.
net",
  "Description": "A test bookmark",
  "Tags": ["mvc","ravendb"],
  "DateCreated": "2011-08-04T00:50:40.3207693Z"
}

La clase de marcador está preparada para trabajar bien con el almacén de documentos, pero la propiedad de las etiquetas se va a suponer un desafío en la capa de interfaz de usuario. Me gustaría dejar que el usuario introduzca una lista de etiquetas separadas por comas en un campo de entrada de cuadro de texto único y tiene el cuaderno de modelo MVC para asignar todos los campos de datos sin ningún código de lógica filtrándose en Mis vistas o las acciones del controlador. ¿Puedo abordar esto mediante el uso de un cuaderno modelo personalizado para la asignación de un campo de formulario denominado "TagsAsString" en el campo de Bookmark.Tags. En primer lugar, creo que la clase binder modelo personalizado (consulte figura 4).

Figura 4 BookmarkModelBinder.cs

public class BookmarkModelBinder : DefaultModelBinder
{
  protected override void OnModelUpdated(ControllerContext controllerContext,
    ModelBindingContext bindingContext)
  {
    var form = controllerContext.HttpContext.Request.Form;
    var tagsAsString = form["TagsAsString"];
    var bookmark = bindingContext.Model as Bookmark;
    bookmark.Tags = string.IsNullOrEmpty(tagsAsString)
      ?
new List<string>()
      : tagsAsString.Split(',').Select(i => i.Trim()).ToList();
  }
}

A continuación, actualizar el archivo Globals.asax.cs para agregar la BookmarkModelBinder a los cuadernos de modelo al iniciarse la aplicación:

protected void Application_Start()
{
  AreaRegistration.RegisterAllAreas();
  RegisterGlobalFilters(GlobalFilters.Filters);
  RegisterRoutes(RouteTable.Routes);
 
  ModelBinders.Binders.Add(typeof(Bookmark), new BookmarkModelBinder());
  DataDocumentStore.Initialize();
}

Para controlar el llenado de un cuadro de texto HTML con las etiquetas actuales en el modelo, vamos a agregar un método de extensión para convertir una lista de <string> objeto en una cadena delimitada por comas:

public static string ToCommaSeparatedString(this List<string> list)
{
  return list == null ?
string.Empty : string.Join(", ", list);
}

Unidad de trabajo

La API del cliente RavenDB se basa en el modelo de unidad de trabajo. Trabajar con documentos desde el almacén de documentos, una nueva sesión debe abrirse; Esta tarea necesita ser realizado y guardado; y debe cerrar la sesión. La sesión controla el seguimiento de cambios y opera de manera similar a un contexto de datos en el EF. Aquí es un ejemplo de cómo crear un nuevo documento:

using (var session = documentStore.OpenSession())
{
  session.Store(bookmark);
  session.SaveChanges();
}

Resulta óptimo para que la sesión en vivo a lo largo de la solicitud HTTP, lo que puede realizar un seguimiento de cambios, el primer nivel de uso de caché y así sucesivamente. Crearé un controlador de base que se utilizará el DocumentDataStore.Instance para abrir una nueva sesión en ejecutar la accióny ejecutar la acción se guardar los cambios y, a continuación, desechar el objeto de sesión (consulte figura 5). Esto me permite hacer todo el trabajo deseado durante la ejecución de mi código de acción con una instancia única sesión abierta.

Figura 5 BaseDocumentStoreController

public class BaseDocumentStoreController : Controller
{
  public IDocumentSession DocumentSession { get; set; }
 
  protected override void OnActionExecuting(ActionExecutingContext filterContext)
  {
    if (filterContext.IsChildAction)
      return;
    this.DocumentSession = DataDocumentStore.Instance.OpenSession();
    base.OnActionExecuting(filterContext);
  }
 
  protected override void OnActionExecuted(ActionExecutedContext filterContext)
  {
    if (filterContext.IsChildAction)
      return;
    if (this.DocumentSession != null && filterContext.Exception == null)
      this.DocumentSession.SaveChanges();
    this.DocumentSession.Dispose();
    base.OnActionExecuted(filterContext);
  }
}

Controlador MVC y la implementación de la vista

Trabajará directamente con el objeto IDocumentSession de la clase base y administrar todas las operaciones Create, Read, Update y Delete (CRUD) para los documentos de las acciones de BookmarksController. Figura 6 muestra el código para el controlador de marcadores.

Figura 6 BookmarksController clase

public class BookmarksController : BaseDocumentStoreController
{
  public ViewResult Index()
  {
    var model = this.DocumentSession.Query<Bookmark>()
      .OrderByDescending(i => i.DateCreated)
      .ToList();
    return View(model);
  }
 
  public ViewResult Details(string id)
  {
    var model = this.DocumentSession.Load<Bookmark>(id);
    return View(model);
  }
 
  public ActionResult Create()
  {
    var model = new Bookmark();
    return View(model);
  }
 
  [HttpPost]
  public ActionResult Create(Bookmark bookmark)
  {
    bookmark.DateCreated = DateTime.UtcNow;
    this.DocumentSession.Store(bookmark);
    return RedirectToAction("Index");
  }
   
  public ActionResult Edit(string id)
  {
    var model = this.DocumentSession.Load<Bookmark>(id);
    return View(model);
  }
 
  [HttpPost]
  public ActionResult Edit(Bookmark bookmark)
  {
    this.DocumentSession.Store(bookmark);
    return RedirectToAction("Index");
  }
 
  public ActionResult Delete(string id)
  {
    var model = this.DocumentSession.Load<Bookmark>(id);
    return View(model);
  }
 
  [HttpPost, ActionName("Delete")]
  public ActionResult DeleteConfirmed(string id)
  {
    this.DocumentSession.Advanced.DatabaseCommands.Delete(id, null);
    return RedirectToAction("Index");
  }
}

El IDocumentSession.Query <T> método en la acción Index devuelve un objeto de resultado que implementa la interfaz IEnumerable, por lo que puedo utilizar la expresión LINQ OrderByDescending para ordenar los elementos y llame al método ToList para capturar los datos al objeto de devolución. El método IDocumentSession.Load en la acción detalles adopta un valor de clave de ID de documento y deserializa el documento correspondiente a un objeto de tipo de marcador.

El método Create con el atributo de verbo HttpPost establece la propiedad CreateDate en el elemento de marcador y llama al método IDocumentSession.Store del objeto de sesión para agregar un nuevo registro de documento para el almacén de documentos. El método Update con el verbo HttpPost puede llamar al método de IDocumentSession.Store, debido a que el objeto Bookmark tiene el valor de Id ya establecido. RavenDB reconocerá que Id y actualizar el existente el documento con la clave coincidente en su lugar de crear uno nuevo. La acción DeleteConfirmed llama a un método Delete del objeto IDocumentSession.Advanced.DatabaseCommands, que proporciona un medio para eliminar un documento por clave sin tener que cargar el objeto en primer lugar. No tengo que llamar al método IDocumentSession.SaveChanges desde dentro de cualquiera de estas acciones, ya que tengo el controlador de base hacer esa llamada del ejecutar la acción.

Todas las vistas son bastante sencillos. Ellos pueden ser inflexible a la clase de marcador en las marcas de crear, editar y eliminar y a una lista de marcadores en el marcado de índice. Cada vista puede hacer referencia directamente a las propiedades de modelo de presentación y los campos de entrada. Es el lugar donde tendré que varían en referencia de la propiedad de objeto con el campo de entrada para las etiquetas. Usaré el método de extensión ToCommaSeparatedString en las creación y edición de vistas con el siguiente código:

@Html.TextBox("TagsAsString", Model.Tags.ToCommaSeparatedString())

Esto permitirá al usuario introducir y editar las etiquetas asociadas con el marcador en un formato delimitado por comas dentro de un solo cuadro de texto.

Buscar objetos

Con todas mis operaciones CRUD en su lugar, puedo enciendo mi atención a la adición de encargarme de funcionalidad: la capacidad de filtrar la lista marcador por etiquetas. Además de implementar la interfaz IEnumerable, el objeto devuelto desde el método IDocumentSession.Query implementa también la IOrderedQueryable y IQueryable interfaces desde el.NET Framework. Esto me permite utilizar LINQ para filtrar y ordenar las consultas. Por ejemplo, aquí es una consulta de los marcadores creados en los últimos cinco días:

var bookmarks = session.Query<Bookmark>()
  .Where( i=> i.DateCreated >= DateTime.UtcNow.AddDays(-5))
  .OrderByDescending(i => i.DateCreated)
  .ToList();

Aquí es una página a través de la lista completa de marcadores:

var bookmarks = session.Query<Bookmark>()
  .OrderByDescending(i => i.DateCreated)
  .Skip(pageCount * (pageNumber – 1))
  .Take(pageCount)
  .ToList();

RavenDB a crear índices dinámicos basados en la ejecución de estas consultas que se mantendrá para "cierta cantidad de tiempo" antes de ser eliminados de. Cuando se vuelve a ejecutar una consulta similar con la misma estructura de parámetro, se utilizará el índice dinámico temporal. Si se utiliza el índice suficiente dentro de un período determinado, el índice se hará permanente. Éstas se conservarán más allá del ciclo de vida de la aplicación.

El siguiente método de acción puedo agregar a mi clase de BookmarksController para controlar la obtención de marcadores por etiqueta:

public ViewResult Tag(string tag)
{
  var model = new BookmarksByTagViewModel { Tag = tag };
  model.Bookmarks = this.DocumentSession.Query<Bookmark>()
    .Where(i => i.Tags.Any(t => t == tag))
    .OrderByDescending(i => i.DateCreated)
    .ToList();
  return View(model);
}

Espero que esta acción se va a alcanzar de forma regular los usuarios de mi aplicación.Si de hecho, es el caso, esta consulta dinámica obtener convertirá en un índice permanente por RavenDB con ningún trabajo adicional necesario por mi parte.

Envía un cuervo despertar nos

Con la aparición de RavenDB, el.NET Comunidad parece tener finalmente una solución de tipo de almacén de documentos NoSQL atendida hacia dentro, permitiendo tiendas centradas en Microsoft y los desarrolladores deslizarse a través del mundo no relacional que han sido desplazarse por otros tantos marcos e idiomas para los últimos años.Veremos nunca más escuchamos los gritos de falta de amor no relacional para la pila de Microsoft.RavenDB es lo que facilita.NET que para comenzar la reproducción y prototipos con un almacén de datos no relacionales, es preciso de la instalación con un API que imita las técnicas de administración de datos que ya se están empleando los desarrolladores del cliente limpio.Mientras que el argumento perenne entre relacionales y no relacionales seguramente no finalizan, la facilidad de probando algo "nuevo" ayudarán a dar lugar a una mejor comprensión de cómo y dónde una solución no relacional cabe dentro de una arquitectura de aplicaciones.

Justin SchwartzenbergerHa sido afianzado CTO en DealerHosts, desarrollo de aplicaciones Web durante bastante tiempo, recorrer las selvas sintácticas de PHP, clásico ASP, Visual Basic, de Visual Basic.NET y ASP.NET Web Forms.Como los primeros de ASP.NET MVC en 2007, decidió refactorizar su enfoque de la pila de Web para todas las cosas MVC.Él contribuye a artículos, habla en grupos de usuarios, mantiene un blog en iwantmymvc.com y se pueden seguir en Twitter en twitter.com/schwarty.

Gracias al siguiente experto técnico para la revisión de este artículo: Ayende Rahien