NoSQL with RavenDB and ASP.NET MVC - Part 1

Source Code - http://ravenmvc.codeplex.com

NoSQL with RavenDB and ASP.NET MVC - Part 2

A while back, I have blogged NoSQL with MongoDB, NoRM and ASP.NET MVC Part 1 and Part 2 on how to use MongoDB with an ASP.NET MVC application. The NoSQL movement is getting big attention and RavenDB is the latest addition to the NoSQL and document database world. RavenDB is an Open Source (with a commercial option) document database for the .NET/Windows platform developed  by Ayende Rahien.  Raven stores schema-less JSON documents, allow you to define indexes using Linq queries and focus on low latency and high performance. RavenDB is .NET focused document database which comes with a fully functional .NET client API  and supports LINQ. RavenDB comes with two components, a server and a client API. RavenDB is a REST based system, so you can write your own HTTP cleint API. As a .NET developer, RavenDB is becoming my favorite document database. Unlike other document databases, RavenDB is supports transactions using System.Transactions. Also it's supports both embedded and server mode of database. You can access RavenDB site at http://ravendb.net

A demo App with ASP.NET MVC

Let's create a simple demo app with RavenDB and ASP.NET MVC. To work with RavenDB, do the following steps.

 

  1. Go to http://ravendb.net/download and download the latest build.
  2. Unzip the downloaded file.
  3. Go to the /Server directory and run the RavenDB.exe. This will start the RavenDB server listening on localhost:8080


You can change the port of RavenDB  by modifying the "Raven/Port" appSetting value in the RavenDB.exe.config file.
When running the RavenDB, it will automatically create a database in the /Data directory. You can change the directory name data by modifying "Raven/DataDirt" appSetting value in the RavenDB.exe.config file.

RavenDB provides a browser based admin tool. When the Raven server is running, You can be access the browser based admin tool and view and edit documents and index using your browser admin tool. The web admin tool available at http://localhost:8080

The below is the some screen shots of web admin tool

 

 

 

 

Working with ASP.NET MVC 

To working with RavenDB in our demo ASP.NET MVC application, do the following steps

Step 1 - Add reference to Raven Cleint API

In our ASP.NET MVC application, Add a reference to the Raven.Client.Lightweight.dll from the Client directory.

Step 2 - Create DocumentStore

The document store would be created once per application. Let's create a DocumentStore on application start-up in the Global.asax.cs.

documentStore = new DocumentStore { Url = "http://localhost:8080/" };

documentStore.Initialise();


The above code will create a Raven DB document store and will be listening the server locahost at port 8080   

Step 3 - Create DocumentSession on BeginRequest  

Let's create a DocumentSession on BeginRequest event in the Global.asax.cs. We are using the document session for every unit of work. In our demo app, every HTTP request would be a single Unit of Work (UoW).

BeginRequest += (sender, args) =>

  HttpContext.Current.Items[RavenSessionKey] = documentStore.OpenSession();


Step 4 - Destroy the DocumentSession on EndRequest

 

EndRequest += (o, eventArgs) =>

{

    var disposable = HttpContext.Current.Items[RavenSessionKey] as IDisposable;

    if (disposable != null)

        disposable.Dispose();

};

 

At the end of HTTP request, we are destroying the DocumentSession  object.

The below  code block shown all the code in the Global.asax.cs

 

private const string RavenSessionKey = "RavenMVC.Session";

private static DocumentStore documentStore;

 

protected void Application_Start()

{

//Create a DocumentStore in Application_Start

//DocumentStore should be created once per application and stored as a singleton.

documentStore = new DocumentStore { Url = "http://localhost:8080/" };

documentStore.Initialise();

AreaRegistration.RegisterAllAreas();

RegisterRoutes(RouteTable.Routes);

//DI using Unity 2.0

ConfigureUnity();

}

 

public MvcApplication()

{

//Create a DocumentSession on BeginRequest  

//create a document session for every unit of work

BeginRequest += (sender, args) =>

    HttpContext.Current.Items[RavenSessionKey] = documentStore.OpenSession();

//Destroy the DocumentSession on EndRequest

EndRequest += (o, eventArgs) =>

{

var disposable = HttpContext.Current.Items[RavenSessionKey] as IDisposable;

if (disposable != null)

disposable.Dispose();

};

}

 

//Getting the current DocumentSession

public static IDocumentSession CurrentSession

{

  get { return (IDocumentSession)HttpContext.Current.Items[RavenSessionKey]; }

}

 

We have setup all necessary code in the Global.asax.cs for working with RavenDB. For our demo app,
Let’s write a domain class

 

public class Category

{

 

    public string Id { get; set; }

 

    [Required(ErrorMessage = "Name Required")]

    [StringLength(25, ErrorMessage = "Must be less than 25 characters")]

    public string Name { get; set;}

    public string Description { get; set; }  


}


We have created simple domain entity Category. Let's create repository class for performing CRUD operations against our domain entity Category.

 

public interface ICategoryRepository

{

    Category Load(string id);

    IEnumerable<Category> GetCategories();

    void Save(Category category);

    void Delete(string id);      

}

 

 

public class CategoryRepository : ICategoryRepository

{

    private IDocumentSession session;

   

    public CategoryRepository(IDocumentSession session)

    {

     this.session = session;

    }

    //Load category based on Id

    public Category Load(string id)

    {

        return session.Load<Category>(id);

    }

    //Get all categories

    public IEnumerable<Category> GetCategories()

    {

        var categories= session.LuceneQuery<Category>()

            .ToArray();

        return categories;

 

    }

    //Insert/Update category

    public void Save(Category category)

    {

       

     //store category object into session

     session.Store(category);   

    //save changes

     session.SaveChanges();

    }

    //delete a category

    public void Delete(string id)

    {

        var category = Load(id);

        session.Delete<Category>(category);

        session.SaveChanges();

    }       

}


For every CRUD operations, we are taking the current document session object from HttpContext object.

session = MvcApplication.CurrentSession;

We are calling the static method CurrentSession from the Global.asax.cs

public static IDocumentSession CurrentSession

{

    get { return (IDocumentSession)HttpContext.Current.Items[RavenSessionKey]; }

}

 

Retrieve Entities 

The Load method get the single Category object based on the Id. RavenDB is working based on the REST principles and the Id would be like categories/1. The Id would be created by automatically when a new object is inserted to the document store. The REST uri categories/1 represents a single category object with Id representation of 1.  

public Category Load(string id)

{

   return session.Load<Category>(id);

}

The GetCategories method returns all the categories calling the session.LuceneQuery method. RavenDB is using a lucen query syntax for querying. I will explain more details about querying and indexing in my future posts.  

public IEnumerable<Category> GetCategories()

{

    var categories= session.LuceneQuery<Category>()    

        .WaitForNonStaleResults()

        .ToArray();

    return categories;

 

}

Insert/Update entity

For insert/Update a Category entity, we have created Save method in repository class. The Save mothod used for both insert and update category. We just need to store category object on the document session. The session.SaveChanges() will save the changes to document store. if the category is a new one, it will insert a new record and update the category object, if it is an existing category object.

public void Save(Category category)

{

//store category object into session

session.Store(category); 

//save changes           

session.SaveChanges();

}

 

Delete Entity 

In the Delete method, we call the document session's delete method and call the SaveChanges method to reflect changes in the document store. 

public void Delete(string id)

{

    var category = Load(id);

    session.Delete<Category>(category);

    session.SaveChanges();

}

 

Let’s create ASP.NET MVC controller and controller actions for handling CRUD operations for the domain class Category

 

public class CategoryController : Controller

{

private ICategoryRepository categoyRepository;

//DI enabled constructor

public CategoryController(ICategoryRepository categoyRepository)

{

    this.categoyRepository = categoyRepository;

}

public ActionResult Index()

{  

 

    var categories = categoyRepository.GetCategories();

    if (categories == null)

        return RedirectToAction("Create");

    return View(categories);

}

 

[HttpGet]

public ActionResult Edit(string id)

{

    var category = categoyRepository.Load(id);    

    return View("Save",category);

}

// GET: /Category/Create

[HttpGet]

public ActionResult Create()

{

    var category = new Category();

    return View("Save", category);

}

[HttpPost]

public ActionResult Save(Category category)

{

    if (!ModelState.IsValid)

    {

        return View("Save", category);

    }

 

        categoyRepository.Save(category);

        return RedirectToAction("Index");

 

 

}       

[HttpPost]

public ActionResult Delete(string id)

{

    categoyRepository.Delete(id);

    var categories = categoyRepository.GetCategories();

    return PartialView("CategoryList", categories);   

 

}       

}

 

RavenDB is an awesome document database and I hope that it will be the winner in .NET space of document database world.  The source code of demo application available at http://ravenmvc.codeplex.com/


Published Wednesday, May 26, 2010 9:40 PM by shiju

Comments

# re: NoSQL with RavenDB and ASP.NET MVC - Part 1

Friday, June 4, 2010 6:49 AM by Andy

Is the session.SaveChanges() command effectively a transactional commit?

If so, then maybe it should be micro managed in the repository, instead put in the EndRequest function?

Leave a Comment

(required) 
(required) 
(optional)
(required)