Wednesday 31 October 2012

A first kick to RavenDB with main CRUD operations using ASP.NET MVC at the front end. (25 min)

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)
- RavenDB (A NoSQL Document-Oriented Database)

This post is about implementing RavenDB as the backend database of an ASP.NET MVC application.


1)      Download RavenDB.
To download RavenDB go here (to know more about the instructions of how to install RavenDB go here)
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 { getset; }
        public string FirstName { getset; }
        public string LastName { getset; }
        public DateTime DateOfBirth { getset; }
        public string Address { getset; }
        public string City { getset; }
        public string Province { getset; }
        public string Country { getset; }
        public string PostalCode { getset; }
        public string[] Phones { getset; }
    }

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 : classnew()
    {
        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
Don’t use repository pattern with RavenDb, advice hereJ
Refactor code
Consider using IoC Pattern.


No comments:

Post a Comment