In a previous article, I had demonstrated how we could introduce RavenDB as an auto-save cache for information that resides in a traditional RDBMS. Today we are going to look at how we can use it as the only backing store of an ASP.NET MVC application.
Note: If you are new to NoSql or RavenDB or Document Databases, make sure you read Hello RavenDB! Introducing RavenDB for .NET Developers
Using RavenDB as a backing store is actually very easy. In a development environment, all you have to do is install using Nuget package manager and start the Raven.Server.exe from the packages folder. However in a production environment, you may or may not be able to run an additional HTTP server for yourself. If you have a hosting provider where your site is shared with multiple other sites, you may not have the access to run a separate HTTP server. Beauty of RavenDB is that even in such a constrained environment, you can easily run RavenDB and not require SQL Server backend. RavenDB has special embedded variant that allows you to run RavenDB in medium trust.
In this article, we will see how to build an ASP.NET MVC app using RavenDB Embedded and then publish it to a hosted source.
Again for simplicity purposes we will take our tried and tested Blog example. The blog data model is simple enough to do quickly but deep enough to explain basic relationships.
1: Start with a usual ASP.NET MVC, Internet project template
2. Add RavenDB (Embedded) reference using the Package Manager (Tools > Extension Manager).
3. Review the License Agreement. RavenDB is NOT free for commercial use. Accept the license to complete installation.
4. Add a class library project to the solution. Call it RaBlog.Service.
5. Modeling the Blog Document database. To start of with we will consider a very simple model
- Add all the classes in a Model folder.
Note: The ‘Comment’ object is typically not a part of the Blog Post update. We will see how we have to use a workaround because of this. In a future post, we will see how to use a ‘denormalized’ document structure. For now, let’s focus on using the embedded version to publish a live site.
6. With the Model out of the way, let's look a different way of implementing persistence layer, one without a repository pattern.
RavenDB backing store is represented by the DocumentStore object. It implements the IDocumentStore interface. Every 'connection' to the DocumentStore is a RavenDB DocumentSession. This implements the IDocumentSession interface. To build our RavenDB access layer we will do the following
- Create a RavenController class that inherits from the MVC's Controller class.
- We will inject the instance of IDocumentStore and instead of using the MVC provided HTTPServerUtility.Session object we will hide it by using the new keyword and add a Session of type IDocumentSession.
- We override the OnActionExecuting method. This is called just before the action begins execution.
We will use OpenSession in this method. OpenSession is akin to document connection open in ADO.NET. So just before the Action Execution starts we open a connection to our RavenDB store
- We override the OnActionExecuted method. This is called once the action is executed. In this method we will check if there is an error, if not, the document session is saved. This saves whatever entities we put in the RavenDB's document Session.
- The simplicity of this pattern is we don't have to explicitly call the RavenDB's document Save method in any of our controllers. Any object we put in the session will get persisted once the action completes. On the flip side, this couples data persistence to the View layer. So use it with caution.
7. With our base controller ready we move on to the HomeContoller and change it to inherit from RavenController instead of Controller and add a constructor.
8. We load a single 'row' of data from RavenDB by making the following call in our controller.
Example here is of the Details action that pulls up a particular Blog Post. The Session property is here the same as the one we defined in RavenController earlier. It is of type IDocumentSession. Hear the Load<T> method loads the required Document type from RavenDB. In our case it's the Blog with the id as provided in the id parameter.
9. Similarly, entire list of Blogs can be loaded as follows
10. Updates are recorded as follows. We load the existing Blog from the session again, using the current blog’s id. Update the Title and Content and store it into the Session. Remember the ActionExecuted override from earlier? That gets called once this method finishes and thus Save is called which persists changes to the database.
Note: RavenDB’s document session does not do partial updates to the document. If we directly put the blog object passed to the action method in session store, since it does not have the related Comments, RavenDB will assume the comments have been deleted and will do the same in the database.
In future we will see how to ‘denormalize’ objects and resolve this issue.
11. I am not detailing the UI changes for Index/Add/Edit/Delete. They are standard MVC strongly typed views. I have made some HTML/CSS changes to make them look a little pretty. However we will look at the Details page where we will show the Comments that were made for the blog.
12. Implementing the Details view.
- The Details view will be actually the entire post and hence will present users with the opportunity to view existing comments and add their own comments if required.
- We update the Details.cshtml to show the user comments that have already been posted
- Next we add a Partial View called CreateComments.cshtml. We can use the Add New View wizard and Select the ‘Comments’ Entity and the ‘Create’ template
- We make some cosmetic changes to make it look nicer. The final markup for CreateComments.cshtml is as follows:
- Now update the Details.cshtml to show the Partial View in the Details Page. We pass a new instance of the Comment object with the current Blog Id to the child partial view.
- In the Controller we need to handle the POST from the CreateComments view.
- We add the following code to handle the POST
We load the current blog instance from the Session.
Check if the blog previously contained comments or not. If not initialize it.
Add the comment object and store the entire blog into the Session.
When this method completes the overloaded ActionExecuted method is called which saves the changes to the Store.
13. Finish off with the composition root and controller factory to inject the DocumentStore object. You can review the theory behind this pattern in my DI Introduction post.
14. Point of interest for us in this is the CreateControllerFactory() method in CompositionRoot.
15. As seen above we are creating an instance of the EmbeddableDocumentStore() and giving it the ConnectionStringName. The connection string is defined in web.config. It is added when you install RavenDB from the package manager. The value looks as follows:
16. Notice the single statement inside a compiler directive allowing it to be applicable only for DEBUG builds.
This enables an embedded HttpServer that allows you to run the RavenDB console. Setting this to true requires your account to have netsh access to the port on which it tries to run. This may not be possible in production so we are disabling the admin console for now. In my case, my host does not allow netsh to port 8080 and if the pre-processor directives are not there you will get an Access Denied exception at run-time.
Even in Dev environment if Visual Studio is not running in Administrator mode you will get the above error. Solution for it is to run the following command in an Administrator mode, console:
netsh http add urlacl url=http://+:8080/ user="<your domain>\<your user id>"
17. With the Application built and ready to roll, switch to a Release build and build the project. Publish it to your production server through your established processes. I used Solution Explorer > RaBlog > Right Click > Publish to bring up my publish profile and push the changes using my publishing profile.
18. Hit publish and wait for the upload to complete. Navigate to the URL location and you should see the Home Page. There you go, you have successfully created and published an application using embedded RavenDB. This sample application is running at http://blog.sumitmaitra.com/
Note: If you are getting the yellow screen of death, turn off custom errors and checkout the error details.
We saw how we can use Embedded RavenDB to build and publish our own ASP.NET MVC application with very little effort.
The entire source code of this article can be downloaded over here
RavenDB on the Cloud
Hibernating Rhinos (the authors of RavenDB) have just launched a public beta of RavenDB on the cloud called RavenHQ. Currently the beta is available only for apps deployed to AppHarbor. Check it out if you want to.
Disclaimer: www.dotnetcurry.com is not associated with Hibernating Rhinos or AppHarbor.