A first kick to RavenDB with main CRUD operations using ASP.NET MVC at the front end. (25 min)
Tools required: (minimum)
- Visual C# 2010 Express
(minimum)
This post is about implementing RavenDB as
the backend database of an ASP.NET MVC application.
1)
Download RavenDB.
The latest release used for this post:
I encourage you to review the source code, to download it click here.
2)
Unzip the file downloaded. (For this demo I have
downloaded and extracted the files at C:\Temp\RavenDB-Build-888 and copied the folder
and its files to C:\TEMP\RavenDB… The full package includes various folders
with tools that provide different capabilities as well tests and samples).
3)
Add a folder inside this location
C:\Temp\RavenDB and name it Data.
4) We are going to deploy/host RavenDB using
IIS. Open Internet Information Services (IIS) Manager
5) Select
Application Pools and create a new Application pool.
6)
Right click on Default Web Site (we are going to
host RavenDB as an Application)
Make
sure you select the right Application pool.
7)
Right click on the application you’ve just
created and go to Advanced Settings…
8)
We’ll set the Physical Path Credentials with a
user with read/write permissions to C:\TEMP\RavenDB\Data (for this demo I am
using my user account).
Click on the … ellipsis button to select the specific user.
Once the user is set, click OK.
9)
One more thing before you check your RavenDB
installation. Open the web config located at C:\TEMP\RavenDB and make sure the
markup looks like the following:
<configuration>
<appSettings>
<add key="Raven/DataDir" value="~\Data"/>
<add key="Raven/AnonymousAccess" value="All"/>
</appSettings>
<system.web>
<authentication mode="Windows"/>
<compilation debug="true"/>
</system.web>
<system.webServer>
<handlers>
<add name="All" path="*" verb="*" type="Raven.Web.ForwardToRavenRespondersFactory, Raven.Web"/>
</handlers>
<security>
<!-- allowing special characters in path to allow for '+' in document IDs -->
<requestFiltering allowDoubleEscaping="true"/>
</security>
</system.webServer>
<runtime>
<loadFromRemoteSources enabled="true"/>
</runtime>
</configuration>
10) Now
you could browse your RavenDB server and check out the options available. Type http://localhost/RavenDB in your browser or
select browse from the IIS.
11) Create
a new ASP.NET MVC3 project. Select an empty ASP.NET MVC project.
Help here
on how to create a project.
12) Copy
the folder Client from
(C:\Temp\RavenDB-Build-888) into your project’s folder.
13) Add
the following reference to your project:
Raven.Client.Lightweight.dll
14) Add
a class to the project and name it Contact.cs
15)
Remove the current mock for your Contact class
and paste the following code:
public class Contact
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string Province { get; set; }
public string Country { get; set; }
public string PostalCode { get; set; }
public string[] Phones { get; set; }
}
16) Add
another class to the project and name it RavenDbRepository.cs, then remove the
empty RavenDbRepository class and paste the following code:
public class RavenDbRepository<T> where T : class, new()
{
public static readonly string connectionString = "http://localhost/RavenDB/";
IDocumentStore store;
public RavenDbRepository()
{
store = new DocumentStore { Url = connectionString };
store.Initialize();
}
/*C*/
public bool Create(T entity)
{
try
{
using (var session = store.OpenSession())
{
session.Store(entity);
session.SaveChanges();
}
}
catch (Exception exception)
{
// log exception
return false;
}
return true;
}
/*R*/
public T Read(string key)
{
T response = default(T);
try
{
//using (var session = RavenDbManager.GetDocumentSession())
using (var session = store.OpenSession())
{
response = session.Load<T>(key);
}
}
catch (Exception exception)
{
// log exception
return response;
}
return response;
}
/*U*/
public bool Update(T entity)
{
try
{
using (var session = store.OpenSession())
{
session.Store(entity);
session.SaveChanges();
}
}
catch (Exception exception)
{
// log exception
return false;
}
return true;
}
/*D*/
public bool Delete(T entity)
{
try
{
using (var session = store.OpenSession())
{
session.Delete(entity);
session.SaveChanges();
}
}
catch (Exception exception)
{
// log exception
return false;
}
return true;
}
public bool Delete(string key)
{
try
{
using (var session = store.OpenSession())
{
var entity = session.Load<T>(key);
session.Delete<T>(entity);
session.SaveChanges();
}
}
catch (Exception exception)
{
// log exception
return false;
}
return true;
}
public List<T> Search(Func<T, bool> predicate, int page, int pageSize)
{
List<T> response = new List<T>();
try
{
using (var session = store.OpenSession())
{
if (predicate != null)
{
response = session.Query<T>()
.Where(predicate)
.Skip((page - 1) * pageSize)
.Take(pageSize).ToList();
}
else
{
response = session.Query<T>()
.Skip((page - 1) * pageSize)
.Take(pageSize).ToList();
}
}
}
catch (Exception exception)
{
// log exception
return response;
}
return response;
}
}
17)
Resolve errors
18)
Press F6.
19)
Add a new Controller, name it ContactController
20)
Review the default code generated for your
controller, then remove completely ContactController class and
paste the following code:
public class ContactController : Controller
{
RavenDbRepository<Contact> repository;
public ContactController()
{
repository = new RavenDbRepository<Contact>();
}
//
// GET: /Contact/
//
public ActionResult Index(int page=0)
{
var contactViewModel = new ContactsViewModel();
contactViewModel.Contacts = repository.Search(null, 1, 20);
return View(contactViewModel.Contacts);
}
//
// GET: /Contact/Details/5
//
public ActionResult Details(string key)
{
var contact = repository.Read(key);
return View(contact);
}
//
// GET: /Contact/Create
//
public ActionResult Create(Contact contact)
{
contact = new Contact();
return View(contact);
}
//
// POST: /Contact/Create
//
[HttpPost]
public ActionResult Create(string key, Contact contact)
{
try
{
var response = repository.Create(contact);
if (!response)
{
// show error
}
ActionResult redirectResult = RedirectToAction("Index");
return redirectResult;
}
catch
{
return View();
}
}
//
// POST: /Contact/Edit/5
//
[HttpPost]
public ActionResult Edit(Contact contact)
{
try
{
var response = repository.Update(contact);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
//
// POST: /Contact/Delete/5
//
//[HttpPost]
public ActionResult Delete(string key)
{
try
{
var response = repository.Delete(key);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
21)
Right click on the method Index and select add View… Select a strongly-typed view, Model class: Contact (RavenDBDemo), Scaffold template: List, select current layout: _layout.cshtml.
22)
review
the current code, then remove it and paste the following code:
@model IEnumerable<RavenDbDemo.Contact>
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Index</h2>
<p>
@Html.RouteLink("Create New", new { controller = "Contact", action = "Create" }, new { @class = "small button" })
</p>
<p>
@ViewBag.Message
</p>
<div>
<table width="85%" align="center">
<tableheader>
<tr>
<th align="left">First Name</th>
<th align="left">Last Name</th>
<th align="left">Address</th>
<th align="left">Province</th>
<th align="left">Country</th>
</tr>
</tableheader>
<tablebody>
@foreach (var contact in Model)
{
<tr>
<td align="left">@contact.FirstName</td>
<td align="left">@contact.LastName</td>
<td align="left">@contact.Address</td>
<td align="center">@contact.Province</td>
<td align="center">@contact.Country</td>
<td align="right">@Html.RouteLink("Details", new { controller = "Contact", action = "Details", key = contact.Id }, new { @class = "small button" })</td>
<td align="right">@Html.RouteLink("Delete", new { controller = "Contact", action = "Delete", key = contact.Id }, new { @class = "small button" })</td>
</tr>
}
</tablebody>
</table>
</div>
23)
Add a view for the Action Method Details,
Select a strongly-typed view, Model class: Contact (RavenDBDemo), Scaffold
template: Details, select current layout: _layout.cshtml.
Review the current code, then remove it and paste the following code:
@model RavenDbDemo.Contact
@{
ViewBag.Title = "Details";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Details</h2>
@using (Html.BeginForm("Edit", "Contact", FormMethod.Post))
{
<fieldset>
<legend>Contact</legend>
@Html.ValidationSummary(true)
@Html.HiddenFor(model => model.Id)
<div class="editor-label">
@Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FirstName)
@Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.LastName)
@Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.DateOfBirth)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.DateOfBirth)
@Html.ValidationMessageFor(model => model.DateOfBirth)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Address)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Address)
@Html.ValidationMessageFor(model => model.Address)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.City)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.City)
@Html.ValidationMessageFor(model => model.City)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Province)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Province)
@Html.ValidationMessageFor(model => model.Province)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Country)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Country)
@Html.ValidationMessageFor(model => model.Country)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.PostalCode)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.PostalCode)
@Html.ValidationMessageFor(model => model.PostalCode)
</div>
<p>
<input type="submit" value="Save Changes" />
</p>
</fieldset>
}
<p>
@Html.ActionLink("Back to List", "Index")
</p>
24)
Add a view for the Action Method Create,
Select a strongly-typed view, Model class: Contact (RavenDBDemo), Scaffold
template: Create, select current layout: _layout.cshtml.
Review
the current code, then remove it and paste the following code:
@model RavenDbDemo.Contact
@{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
<fieldset>
<legend>Contact</legend>
@Html.HiddenFor(model => model.Id)
<div class="editor-label">
@Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FirstName)
@Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.LastName)
@Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.DateOfBirth)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.DateOfBirth)
@Html.ValidationMessageFor(model => model.DateOfBirth)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Address)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Address)
@Html.ValidationMessageFor(model => model.Address)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.City)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.City)
@Html.ValidationMessageFor(model => model.City)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Province)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Province)
@Html.ValidationMessageFor(model => model.Province)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Country)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Country)
@Html.ValidationMessageFor(model => model.Country)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.PostalCode)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.PostalCode)
@Html.ValidationMessageFor(model => model.PostalCode)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
25)
At this point we could start playing with our implementation;
nevertheless we are going to make the UI a bit decent adding Foundation (front-end framework) to our
project. Download the package from here (Download Foundation
CSS)
26)
Extract the files and copy those three folders
into your project.
27)
Now include those folders to your project. First
click the icon “Show All Files” and then right click on the folders added and
select include in project.
28)
Open _layout.cshtml
29)
Replace the current code in _layout.cshtml with
the following:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>@ViewBag.Title</title>
<!-- Set the viewport width to device width for mobile -->
<meta content="width=device-width" name="viewport">
<!-- Included CSS Files (Compressed) -->
@* <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
*@
<link rel="stylesheet" href="stylesheets/foundation.min.css"/>
<link rel="stylesheet" href="stylesheets/app.css"/>
<script type="text/javascript" src="javascripts/modernizr.foundation.js"></script>
<!-- IE Fix for HTML5 Tags -->
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<div class="container top-bar home-border">
<div class="attached">
<div onclick="void(0);" class="name">
<span><a href="#">RavenDB Demo </a>
</span>
</div>
</div>
</div>
<div class="row">
<div class="twelve columns">
<h3>A first kick to RavenDB using ASP.NET MVC to handle the UI for the CRUD operations.</h3>
<hr>
</div>
</div>
<div class="row">
<div class="twelve columns">
@RenderBody()
</div>
</div>
<script type="text/javascript" src="~/javascripts/jquery.js"></script>
<script type="text/javascript" src="~/javascripts/foundation.min.js"></script>
<!-- Initialize JS Plugins -->
<script type="text/javascript" src="~/javascripts/app.js"></script>
</body>
</html>
30)
Press F5, and check how it works !
Considerations
Refactor code
Consider
using IoC Pattern.