Il presente articolo è stato tradotto automaticamente.

Database di documenti NoSQL

Integrazione di RavenDB in un'applicazione ASP.NET MVC 3

Justin Schwartzenberger

Scaricare il codice di esempio

Particolare attenzione alla circolazione NoSQL sta crescendo all'interno di Microsoft.NET community Framework come continuiamo a sentire delle aziende di condividere le loro esperienze di implementazione di esso in applicazioni che abbiamo conoscere e utilizzare.Con questa maggiore consapevolezza proviene la curiosità per eseguire un'analisi e identificare come un archivio di dati NoSQL potrebbe offrire vantaggi o altre possibili soluzioni per il software che gli sviluppatori sono attualmente prerogativa.Ma, in cui si avvia e la curva di apprendimento è difficile?Forse un problema ancora più rilevante: quanto tempo e fatica sono necessari per avviare una nuova soluzione di archiviazione di dati e iniziare a scrivere codice nei confronti di tale?Dopo tutto, è necessario il processo di installazione di SQL Server per una nuova applicazione verso il basso per una scienza, a destra?

Word ha raggiunto il.NET community delle ali di un raven su una nuova opzione per un'implementazione del livello dati di tipo NoSQL.RavenDB (ravendb.net) è stato progettato un database di documento per il.Piattaforma di NET/Windows, incluso tutto il che necessario per iniziare a lavorare con un archivio dati non relazionali.RavenDB archivia i documenti in formato JSON meno dello schema.Un'API RESTful esiste per l'interazione diretta con l'archivio dati, ma il vero vantaggio si trova all'interno di.NET client API fornita in dotazione con l'installazione.Esso implementa il modello di unità di lavoro e si avvale di sintassi LINQ per lavorare con documenti e le query.Se non si è lavorato con un agente di mapping relazionale a oggetti (ORM), ad esempio l'Entity Framework (EF) o NHibernate, o consumate in modo che un servizio WCF di dati, sarà feel right at home con l'architettura dell'API per la gestione di documenti in RavenDB.

La curva di apprendimento per la creazione ed esecuzione di un'istanza di RavenDB è breve e semplice.In realtà, il pezzo che potrebbe richiedere più pianificazione è la strategia di gestione delle licenze (ma anche questa è minima).RavenDB offre una licenza open source per i progetti che sono anche aprire l'origine, ma una licenza commerciale è obbligatorio per i progetti commerciali di origine chiuso.Dettagli della licenza e il prezzo, visitare ravendb.NET/gestione delle licenze.Il sito si avverte che la licenza gratuita è disponibile per le società di avvio o di coloro che desiderano utilizzarlo in un progetto di origine non commerciali e chiuso.In entrambi i casi, è utile per rivedere velocemente le opzioni per comprendere il potenziale prima di qualsiasi sviluppo di prototipi o sandbox di implementazione a lungo termine.

RavenDB incorporato e MVC

RavenDB può essere eseguita in tre diverse modalità:

  1. Come un servizio di Windows
  2. Come un'applicazione IIS
  3. Incorporato in una.NET dell'applicazione

I primi due dispone di un processo di installazione piuttosto semplice, ma dotati di un certo overhead di strategia di implementazione.La terza opzione incorporata, è estremamente facile da installare ed eseguire.Infatti, è disponibile un pacchetto di NuGet per esso.Una chiamata per il comando riportato di seguito nella Console di Gestione pacchetti in Visual Studio 2010 (o una ricerca per il termine "ravendb" nella finestra di dialogo Gestisci pacchetti NuGet) fornirà tutti i riferimenti necessari per iniziare a lavorare con la versione incorporata di RavenDB:

Install-Package RavenDB-Embedded

Dettagli del pacchetto sono reperibili sul sito della raccolta di NuGet al bit.ly/ns64W1.

Aggiungere la versione incorporata di RavenDB a un'applicazione ASP.NET MVC 3 applicazione è semplice come l'aggiunta del pacchetto tramite NuGet e fornendo l'archivio dati file un percorso di directory. Poiché ASP.NET applicazioni dispongono di una directory di dati noti nel quadro denominato App_Data e la maggior parte delle società di hosting forniscono accesso in lettura/scrittura alla directory con poca o nessuna configurazione richiesta, è un ottimo strumento per memorizzare i file di dati. Quando RavenDB viene creata l'archiviazione di file, viene creata una serie di directory e file nel percorso di directory forniti ad esso. Non crea una directory di primo livello per memorizzare tutti i dati. Sapendo che, è utile per aggiungere le pagine ASP.NET cartella denominata App_Data tramite il menu di scelta rapida del progetto in Visual Studio 2010 e quindi creare una sottodirectory nella directory App_Data per i dati di RavenDB (vedere nella figura 1).

App_Data Directory Structure
Figura 1 struttura della Directory App_Data

Un archivio di dati del documento è minore dello schema per natura, pertanto non è necessario creare un'istanza di un database o di impostare tutte le tabelle. Una volta effettuata la prima chiamata a inizializzare l'archivio di dati nel codice, verranno creati i file necessari per mantenere lo stato dei dati.

Per utilizzare con l'API del Client RavenDB interfaccia con l'archivio dati è necessaria un'istanza di un oggetto che implementa l'interfaccia Raven.Client.IDocumentStore per essere creato e inizializzato. L'API ha due classi, DocumentStore ed EmbeddedDocumentStore, che implementano l'interfaccia e possono essere utilizzate in base alla modalità di esecuzione di RavenDB. Dovrebbe essere presente solo un'istanza per ogni archivio di dati durante il ciclo di vita di un'applicazione. È possibile creare una classe per gestire una singola connessione per il negozio di documento che consenta di accedere all'istanza dell'oggetto IDocumentStore tramite una proprietà statica e disporre di un metodo statico per inizializzare l'istanza (vedere nella figura 2).

Figura 2 classe per 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;
  }
}

Il metodo di richiamo di proprietà statica controlla un campo di supporto statico privato per un oggetto null e, se è null, viene generata un'eccezione InvalidOperationException. Viene generata un'eccezione in questo caso, anziché chiamare il metodo Initialize, per mantenere il codice thread-safe. Se la proprietà di istanza fosse consentita per effettuare la chiamata e l'applicazione si basava su facendo riferimento alla proprietà per eseguire l'inizializzazione, non vi sarà possibilità che più di un utente potrebbe raggiunto l'applicazione allo stesso tempo, risultanti nelle chiamate simultanee per il metodo Initialize. All'interno della logica del metodo Initialize, creo una nuova istanza della Raven.Client.Embedded.EmbeddableDocumentStore e impostare la proprietà ConnectionStringName per il nome di una stringa di connessione è stata aggiunta al file Web. config per l'installazione del pacchetto RavenDB NuGet. Nel file Web. config, è necessario impostare il valore della stringa di connessione a una sintassi che RavenDB è consapevole del fatto per configurare in modo che utilizzi la versione locale incorporata dell'archivio dati. Inoltre eseguire il mapping della directory del file nella directory di Database che creato nella directory App_Data del progetto MVC:

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

L'interfaccia IDocumentStore contiene tutti i metodi per l'utilizzo con l'archivio dati. Tornare e memorizzare l'oggetto EmbeddableDocumentStore come un'istanza del tipo di interfaccia IDocumentStore in modo che dispone di flessibilità della modifica la creazione di un'istanza dell'oggetto EmbeddedDocumentStore per la versione server (DocumentStore), se si desidera abbandonare la versione incorporata. In questo modo, tutto il codice della logica che consente di gestire la gestione degli oggetti del documento verrà separata dalla conoscenza del modo in cui RavenDB è in esecuzione.

RavenDB creerà le chiavi di ID di documento in un formato simile a RIPOSO per impostazione predefinita. Un oggetto di "Item" otterrebbe una chiave nel formato "elementi/104." Il nome del modello oggetto viene convertito in lettere minuscole ed è pluralized e un unico numero di identità di registrazione viene aggiunto dopo una barra con ogni nuova creazione di un documento. Può essere problematico in un'applicazione di MVC, come la barra causerà un nuovo parametro di route da analizzare. L'API del Client RavenDB fornisce un modo per modificare la barra impostando il valore di IdentityPartsSeparator. Nel metodo DataDocumentStore.Initialize, sto impostando il valore di IdentityPartsSeparator un trattino prima di chiamare il metodo Initialize dell'oggetto EmbeddableDocumentStore, per evitare il problema di routing.

Aggiunta di una chiamata al metodo statico DataDocumentStore.Initialize dal metodo Application_Start nel file Global.asax.cs della mia applicazione MVC stabilirà l'istanza di IDocumentStore alla prima esecuzione dell'applicazione, che è simile al seguente:

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

Da qui si può fare ricorso al IDocumentStore oggetto con una chiamata statica per la proprietà DataDocumentStore.Instance per lavorare sugli oggetti del documento dal mio Negozio di dati incorporati all'interno di un'applicazione MVC.

Oggetti RavenDB

Per ottenere una migliore comprensione di RavenDB in azione, creerà un'applicazione di prototipo per memorizzare e gestire i segnalibri. RavenDB è progettato per funzionare con normale vecchi oggetti CLR (POCOs), in modo che non è necessario aggiungere gli attributi di proprietà per condurre la serializzazione. Creazione di una classe per rappresentare un segnalibro è piuttosto semplice. Figura 3 viene illustrata la classe di segnalibro.

Figura 3 classe segnalibro

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>();
  }
}

RavenDB serializza i dati dell'oggetto in una struttura JSON quando esce per archiviare il documento. Verrà utilizzato il noto "Id" proprietà denominata per gestire la chiave di ID di documento. RavenDB creerà tale valore, purché la proprietà Id è vuoto o null quando si effettua la chiamata per creare il nuovo documento e memorizzare in un elemento di metadati @ per il documento (che viene utilizzato per gestire la chiave del documento a livello dell'archivio dati). Quando si richiede un documento, il codice di RavenDB Client API impostare la chiave dell'ID documento la proprietà Id durante il caricamento dell'oggetto documento.

La serializzazione JSON di un documento di segnalibro di esempio è rappresentata nella struttura seguente:

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

La classe del segnalibro viene inizializzata per funzionare correttamente con l'archivio di documenti, ma la proprietà tag è destinata a sono difficili da gestire nel livello dell'interfaccia utente. Vorrei che consente all'utente di immettere un elenco di tag separati da virgole in un campo di input casella di testo e il raccoglitore modello MVC mappare tutti i campi di dati senza alcun codice logica sfumato in my visualizzazioni o azioni del controller. È possibile affrontare questo utilizzando un raccoglitore modello personalizzato per il mapping di un campo modulo denominato "TagsAsString" per il campo Bookmark.Tags. Innanzitutto, creare la classe di Raccoglitore modello personalizzato (vedere nella 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();
  }
}

Quindi aggiornare il file Globals.asax.cs per aggiungere il BookmarkModelBinder per i raccoglitori di modello all'avvio dell'applicazione:

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

Per gestire la compilazione di una casella di testo HTML con il tag corrente nel modello, verrà aggiunto un metodo di estensione per convertire un elenco <string> oggetto da una stringa separata da virgole:

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

Unità di lavoro

L'API del Client RavenDB si basa sul modello di unità di lavoro. Per lavorare su documenti dall'archivio del documento, una nuova sessione deve essere aperto; lavoro deve essere eseguito e salvato; ed è necessario chiudere la sessione. La sessione gestisce il rilevamento delle modifiche e funziona in modo simile a un contesto di dati di EF. Di seguito è riportato un esempio di creazione di un nuovo documento:

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

È ottimale per la sessione live in tutta la richiesta HTTP può tenere traccia delle modifiche, la cache di primo livello di utilizzo e così via. Verrà creata in un controller di base che verrà utilizzato il DocumentDataStore.Instance per aprire una nuova sessione su l'esecuzione di azionie via eseguita l'azione verrà salvare le modifiche e quindi eliminare l'oggetto di sessione (vedere nella figura 5). Questo mi permettono di eseguire tutte le operazioni desiderate durante l'esecuzione del mio codice di azione con un'istanza singola sessione aperta.

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);
  }
}

Controller MVC e implementazione di visualizzazione

Le azioni BookmarksController verranno lavorare direttamente con l'oggetto IDocumentSession dalla classe base e gestire tutte le operazioni di creazione, lettura, aggiornamento ed eliminazione (CRUD) per i documenti. Figura 6 viene visualizzato il codice per il controller di segnalibri.

Figura 6 classe BookmarksController

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");
  }
}

Il IDocumentSession.Query <T> metodo nell'azione indice restituisce un oggetto di risultato che implementa l'interfaccia IEnumerable in modo che è possibile utilizzare l'espressione LINQ OrderByDescending per ordinare gli elementi e chiamare il metodo ToList per catturare i dati per l'oggetto restituito. Il metodo IDocumentSession.Load nell'azione dettagli accetta un valore di chiave ID di documento e deserializza il documento corrispondente a un oggetto di tipo segnalibro.

Il metodo Create con l'attributo del verbo HttpPost imposta la proprietà CreateDate in corrispondenza della voce segnalibro e chiama il metodo IDocumentSession.Store dell'oggetto di sessione per aggiungere un nuovo record di documento nell'archivio del documento. Il metodo di aggiornamento con il verbo HttpPost può chiamare il metodo di IDocumentSession.Store, anche perché l'oggetto Bookmark avrà il valore Id già impostato. RavenDB riconoscerà che Id e aggiornamento esistenti documento con la chiave corrispondente invece di crearne uno nuovo. L'azione DeleteConfirmed chiama un metodo di eliminazione dell'oggetto IDocumentSession.Advanced.DatabaseCommands, che fornisce un modo per eliminare un documento dalla chiave senza dover caricare innanzitutto l'oggetto. Non è necessario chiamare il metodo IDocumentSession.SaveChanges all'interno di una di queste operazioni, poiché dispone di controller di base chiamata su eseguita l'azione.

Tutte le visualizzazioni sono molto semplici. Può essere fortemente tipizzati per la classe di segnalibro nel markup Create, Edit e Delete e per un elenco dei segnalibri nel codice indice. Ogni visualizzazione è possibile fare direttamente riferimento le proprietà del modello per la visualizzazione e i campi di input. Un luogo in cui è necessario variare in riferimento alla proprietà object è con il campo di input per i tag. Si utilizzerà il metodo di estensione ToCommaSeparatedString nelle visualizzazioni di creazione e modifica con il codice riportato di seguito:

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

Ciò consentirà all'utente di immettere e modificare i tag associati al segnalibro in un formato delimitato da virgole all'interno di una casella di testo.

La ricerca di oggetti

Tutte le operazioni CRUD nel luogo, è possibile attivare mia attenzione per l'aggiunta di un ultimo bit di funzionalità: la possibilità di filtrare l'elenco di segnalibro da tag. Oltre a implementare l'interfaccia IEnumerable, l'oggetto restituito dal metodo IDocumentSession.Query implementa inoltre la IOrderedQueryable e IQueryable interfacce dal.NET Framework. Questo consente di utilizzare LINQ per filtrare e ordinare le query. Ad esempio, ecco una query dei segnalibri creati nel corso degli ultimi cinque giorni:

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

Ecco uno per scorrere l'elenco completo dei segnalibri pagine:

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

RavenDB crea indici dinamici basati sull'esecuzione di queste query che verranno mantenute per "alcune quantità di tempo" prima di essere eliminato. Quando una query simile viene eseguita nuovamente con la stessa struttura di parametro, verrà utilizzato l'indice dinamico temporaneo. Se l'indice viene utilizzato sufficiente all'interno di un determinato periodo, l'indice verrà reso permanente. Queste verranno mantenute oltre il ciclo di vita dell'applicazione.

È possibile aggiungere il seguente metodo di azione per la classe BookmarksController per gestire i segnalibri durante la lettura dal tag:

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);
}

Prevede che questa azione da sottoporre a hit regolarmente dagli utenti dell'applicazione.Se effettivamente è così, questa query dinamica verrà ottenere trasformata in un indice permanente da RavenDB senza ulteriore lavoro necessari da parte.

Un Raven inviati a viene usato Us

Con l'introduzione dei RavenDB, i.NET comunitaria sembra di avere infine una soluzione di tipo di archivio del documento NoSQL desiderino verso di esso, consentendo di negozi incentrato sugli elementi di Microsoft e agli sviluppatori di glide attraverso tutto il mondo non relazionali esplorazione di così tante altre lingue e discipline, per alcuni anni.Nevermore termine è ascoltare le richieste di una mancanza di amore non relazionali per lo stack di Microsoft.RavenDB sta effettuando con facilità.NET di avviare la riproduzione e creazione di prototipi con un archivio dati non relazionali raggruppando l'installazione con un client privo di API che simula le tecniche di gestione dei dati che gli sviluppatori stanno già utilizzando.Mentre l'argomento perenne tra relazionali e relazionali sicuramente non specifica out, la facilità di provato qualcosa di "nuovo" dovrebbe aiutare a portare a una migliore comprensione di come e dove una soluzione non relazionali può adattarsi all'interno di un'architettura di applicazione.

Justin SchwartzenbergerÈ stata entrenched CTO a DealerHosts, nello sviluppo di applicazioni Web per un po', attraversando altipiani sintattiche di PHP, classico ASP, Visual Basic, VBNET e ASP.NET Web Form.Come dei primi utenti di ASP.NET MVC nel 2007, egli ha deciso di eseguire il refactoring del suo attivo di stack Web per tutte le cose MVC.Egli contribuisce agli articoli, si parla a gruppi di utenti, gestisce un blog all'indirizzo iwantmymvc.com e possono essere seguiti in movimenti in twitter.com/schwarty.

Grazie all'esperto tecnico riportato di seguito per la revisione di questo articolo: Ayende Rahien