home | articles | site map | contacts
about us
consulting
client login
support
contacts
  Generating Flying Creatures in NoSQL RavenDB with C# ASP .NET MVC Razor
  12/22/2010
Follow PrimaryObjects on Twitter Subscribe to Primary Objects via RSS More Software Articles
by Primary Objects
enter email address
 

Introduction

C# ASP .NET web applications have grown to encompass varying degrees of complexity and enterprise-level flexibility. With the latest release of the Microsoft ASP .NET MVC Model View Controller (Release Candidate) framework, C# web applications can be designed using the fluent view template language, Razor, to rapidly develop MVC Views and web application user interfaces. By combining the ASP .NET MVC Razor framework with a database layer, featuring the new NoSQL solution RavenDB, we can develop an enterprise-suitable solution for querying the NoSQL database and managing entities.

In this article, we'll create an example C# ASP .NET MVC Razor web application, using the RavenDB NoSQL database as the backend. Our web application will automatically generate random flying creatures and monsters, each with their own randomly generated magical weapon. The flying creatures will be created and stored in the NoSQL document database and displayed within an AJAX jQuery based datagrid. The backend will feature an enterprise solution for managing the RavenDB NoSQL database connection using the Repository design pattern combined with the Unit Of Work (UnitOfWork) pattern.

 

NoSQL RavenDB C# ASP .NET MVC Razor DataGrid

Did you Say NoSQL?

NoSQL is a term describing databases which differ from traditional relational database systems, in that they usually provide a different querying mechanism. They do not utilize the SQL query language nor do they traditionally deal with relational tables or foreign keys.

In this example, we'll be using the RavenDB NoSQL implementation, which offers a familiar .NET LINQ query language and programming interface framework. The RavenDB framework follows very similar to database solutions including the Microsoft Entity Framework, LINQ2SQL, and NHibernate.

It's important to note, developing a database schema for a NoSQL solution is quite different than developing for traditional relational databases. In relational databases, it is common to normalize and link tables together with foreign keys in order to reduce redundancy. In NoSQL document databases, reducing redundancy is less of a concern. Specifically, RavenDB provides the ability to distribute database entities (also called "Documents") across multiple repositories, databases, and servers, via the technique of "sharding". Due to this ability, storage space and relational structure is less of a concern. Similarly, query speed, data retrieval, and data design is improved due to the ability to store documents in their entirety as a single entity. For additional details, see Document Structure Design Considerations with RavenDB NoSQL.

Double Negatives are Fun: No Database Schema Required with NoSQL

One of the most interesting aspects of NoSQL solutions is the ability to get started with a C# ASP .NET web application design without creating a database schema. Tables, columns, and foreign key relations are developed implicitly within the C# .NET code, rather than directly inside a database client. The RavenDB NoSQL server takes care of storing the Document entities and generating indexes for search queries. Due to this flexibility, we can jump directly into developing our solution.

Yes, You Have to Download Some Stuff

Requires an install of ASP .NET MVC 3 RC
Requires an install of RavenDB
Requires the StructureMap DLL

Getting Started with RavenDB NoSQL

The first step to setting up RavenDB NoSQL is to download and install the RavenDB database server. After downloading, extract the contents of the zip file to C:\RavenDB or similar directory. Next, install the database service by opening a command-line prompt and typing: raven.server /install

Once the database service is installed, you can optionally configure the IIS web client, which allows querying the database using LINQ and performing administrative tasks in the NoSQL database. To install the IIS web client, simply open IIS Manager and create a new virtual directory, pointing to the RavenDB install folder at C:\RavenDB\Web. Enable running of ASP scripts and set the target .NET Framework to 4.0 (either on the Application Pool or in the virtual folder options, depending on your version of IIS Manager). You'll also want to verify the Web folder has Read and Write access for the ASP Internet user. You can access the RavenDB client at http://localhost:8080.

Creating our Repository and Interfaces

The first step of developing our C# ASP .NET MVC (RC) Razor web application is to develop our data layer for handling the NoSQL database connection. We can begin by declaring several interfaces to define our Repository and UnitOfWork design patterns. These two combined design patterns will allow us to easily utilize the RavenDB database in a multiple-threaded environment, such as a web application. The design will also provide an easy TDD (Test Driven Development) and unit-testable solution, by allowing us to swap in or out additional database backends or unit test classes.

We'll be using the same design as described in the previous article, Using the Entity Framework Repository and UnitOfWork Pattern in C# ASP .NET.

The Repository Interface

namespace FlyingCreatures.Repository.Interface
{
    public interface IRepository<T> where T : class
    {
        IEnumerable<T> GetAll();
        IEnumerable<T> Find(Func<T, bool> where);
        T Single(Func<T, bool> where);
        T First(Func<T, bool> where);

        void Delete(T entity);
        void Add(T entity);
        void SaveChanges();
    }
}

Our Repository interface will allow us to query and manage the RavenDB NoSQL database with basic database functionality. We've defined the interface using .NET generics, which allows us to easily convert the solution to utilize other concrete classes for unit testing, TDD, or alternative database backend solutions.

The UnitOfWork Interface

namespace FlyingCreatures.Repository.Interface
{
    public interface IUnitOfWork : IDisposable
    {
        void Commit();
    }
}

The UnitOfWork design pattern will allow us to maintain a single NoSQL database session per web request. The connection will be opened upon beginning the web request and closed upon ending it. The UnitOfWork pattern requires a single method for committing the NoSQL database transaction.

The UnitOfWork Factory Interface

namespace FlyingCreatures.Repository.Interface
{
    public interface IUnitOfWorkFactory
    {
        IUnitOfWork Create();
    }
}

The UnitOfWork Factory interface will allow us to implement multiple types of database managers, including alternative database solutions (Entity Framework, SQL Server, MySQL, Oracle, etc).

Implementing the UnitOfWork Pattern

We can define the concrete class for handling the UnitOfWork pattern in the NoSQL RavenDB backend solution, by defining the class as shown below. Note, the usage of the HttpContext to store and retrieve the concrete unit of work (ie., database session) within the same web request, which optimizes performance and provides thread-safe management of the RavenDB database connection. This class utilizes StructureMap to provide an IoC container for instanting the required classes upon demand.

using StructureMap;

namespace FlyingCreatures.Repository.Concrete
{
    public class UnitOfWork
    {
        private const string HTTPCONTEXTKEY =
"FlyingCreatures.Repository.Base.HttpContext.Key"; private static IUnitOfWorkFactory _unitOfWorkFactory; private static readonly Hashtable _threads = new Hashtable(); public static void Commit() { IUnitOfWork unitOfWork = GetUnitOfWork(); if (unitOfWork != null) { unitOfWork.Commit(); } } public static IUnitOfWork Current { get { IUnitOfWork unitOfWork = GetUnitOfWork(); if (unitOfWork == null) { _unitOfWorkFactory = ObjectFactory.GetInstance<IUnitOfWorkFactory>(); unitOfWork = _unitOfWorkFactory.Create(); SaveUnitOfWork(unitOfWork); } return unitOfWork; } } private static IUnitOfWork GetUnitOfWork() { if (HttpContext.Current != null) { if (HttpContext.Current.Items.Contains(HTTPCONTEXTKEY)) { return (IUnitOfWork)HttpContext.Current.Items[HTTPCONTEXTKEY]; } return null; } else { Thread thread = Thread.CurrentThread; if (string.IsNullOrEmpty(thread.Name)) { thread.Name = Guid.NewGuid().ToString(); return null; } else { lock (_threads.SyncRoot) { return (IUnitOfWork)_threads[Thread.CurrentThread.Name]; } } } } private static void SaveUnitOfWork(IUnitOfWork unitOfWork) { if (HttpContext.Current != null) { HttpContext.Current.Items[HTTPCONTEXTKEY] = unitOfWork; } else { lock(_threads.SyncRoot) { _threads[Thread.CurrentThread.Name] = unitOfWork; } } } } }

Implementing the RavenDB UnitOfWork Concrete Class

While we have defined the generic UnitOfWork class manager above, we now need to define the concrete implementation for the RavenDB NoSQL database backend. We can do this by implementing the IUnitOfWork interface and implementing the methods for creating the database session, committing, and cleanup. Note, we utilize the RavenDB IDocumentStore to open a new session (NoSQL database connection), and call the SaveChanges() method to commit (which is similar in design to the Entity Framework and NHibernate framework). Due to the concrete implementation, we'll need to reference the RavenDB database client.

using StructureMap;
using Raven.Client;

namespace FlyingCreatures.Repository.Concrete
{
    public class RavenUnitOfWork : IUnitOfWork
    {
        public IDocumentSession Context { get; private set; }

        public RavenUnitOfWork(IDocumentStore session)
        {
            Context = session.OpenSession();
        }

        #region IUnitOfWork Members

        public void Commit()
        {
            Context.SaveChanges();
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            if (Context != null)
            {
                Context.Dispose();
                Context = null;
            }

            GC.SuppressFinalize(this);
        }

        #endregion
    }
}

Implementing the RavenDB UnitOfWork Factory Concrete Class

So far, we've defined the UnitOfWork manager and the RavenDB UnitOfWork class. Our enterprise design requires a UnitOfWork Factory class in order to instantiate the proper concrete class for handling the NoSQL database connections. We can do this by implementing the IUnitOfWorkFactory interface, as shown below. Note how we reference the RavenDB IDocumentStore object to obtain the database context. This context is then passed on to the RavenUnitOfWork concrete class for actually opening the database session, saving, and disposing.

using Raven.Client;

namespace FlyingCreatures.Repository.Concrete
{
    public class RavenUnitOfWorkFactory : IUnitOfWorkFactory
    {
        private static Func<IDocumentStore> _objectContextDelegate;
        private static readonly Object _lockObject = new Object();

        public static void SetObjectContext(Func<IDocumentStore> objectContextDelegate)
        {
            _objectContextDelegate = objectContextDelegate;
        }

        #region IUnitOfWorkFactory Members

        public IUnitOfWork Create()
        {
            IDocumentStore context;

            lock (_lockObject)
            {
                context = _objectContextDelegate();
            }

            return new RavenUnitOfWork(context);
        }

        #endregion
    }
}

The Chief RavenDB Repository Class

One final class remains in order to complete our Repository and Unit Of Work design pattern for the Raven DB NoSQL database manager, and that is the concrete repository class. We can implement the NoSQL repository class by implementing the IRepository interface as a .NET generic class and implementing the methods for actually querying the RavenDB NoSQL database documents, loading, saving, and deleting. We utilize .NET generics in the repository class in order to provide individual concrete repository classes for each entity type (Document) that we wish to perform database operations on. This allows for defining custom operations or business rules within each repository, prior to executing the actual NoSQL database operation (such as, preventing a delete on an entity if a certain condition exists, or saving an additional entity if a condition exists, etc). Note, all database query operations follow a similar framework pattern as the Entity Framework and NHibernate, in utilizing the Context or Session (IDocumentSession) object to perform operations.

using Raven.Client;

namespace FlyingCreatures.Repository.Concrete
{
    public class RavenRepository<T> : IRepository<T> where T : class
    {
        private IDocumentSession _context;

        protected IDocumentSession Context
        {
            get
            {
                if (_context == null)
                {
                    _context = GetCurrentUnitOfWork<RavenUnitOfWork>().Context;
                }

                return _context;
            }
        }

        public TUnitOfWork GetCurrentUnitOfWork<TUnitOfWork>() where TUnitOfWork : IUnitOfWork
        {
            return (TUnitOfWork)UnitOfWork.Current;
        }

        public IEnumerable<T> GetAll()
        {
            return Context.Query<T>();
        }

        public IEnumerable<T> Find(Func<T, bool> where)
        {
            return this.Context.Query<T>().Where<T>(where);
        }

        public T Single(Func<T, bool> where)
        {
            return this.Context.Query<T>().SingleOrDefault<T>(where);
        }

        public T First(Func<T, bool> where)
        {
            return this.Context.Query<T>().First<T>(where);
        }

        public virtual void Delete(T entity)
        {
            this.Context.Delete(entity);
        }

        public virtual void Add(T entity)
        {
            this.Context.Store(entity);
        }

        public void SaveChanges()
        {
            this.Context.SaveChanges();
        }
    }
}

A Global Addition to Enable the Repository and UnitOfWork Pattern

We require one more item in our C# ASP .NET MVC Razor web application in order to bring the repository and unit of work pattern to life. Our design operates per web request, and as such, we'll need to instantiate the RavenDB repository class and provide it to our factory so that it knows which concrete type to instantiate. We'll also need to close and dispose of the database connection when the web request ends. We can solve both of these issues within the Global.asax.cs file, as shown below:

using StructureMap;
using StructureMap.Configuration;

...

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);

            // Initialize the Repository factory.
            ObjectFactory.Initialize(
                x =>
                {
                    x.For<IUnitOfWorkFactory>().Use<RavenUnitOfWorkFactory>();
                    x.For(typeof(IRepository<>)).Use(typeof(RavenRepository<>));
                }
            );

            // RavenDB initialization method.
            RavenUnitOfWorkFactory.SetObjectContext(
                () =>
                {
                    var documentStore = 
new Raven.Client.Document.DocumentStore { Url = "http://localhost:8080" }; documentStore.Conventions.IdentityPartsSeparator = "-"; documentStore.Initialize(); return documentStore; } ); } protected void Application_EndRequest(object sender, EventArgs e) { // Close the UnitOfWork (database connection)
// if this is the final request. IIS will call this method for
// any file request (*.css, *.jpg, *.gif, etc). string path = Request.Url.AbsolutePath.ToLower(); if (path.IndexOf(".css") == -1 && path.IndexOf(".js") == -1 &&
path.IndexOf(".gif") == -1 && path.IndexOf (".png") == -1 && path.IndexOf(".jpg") == -1) { UnitOfWork.Current.Dispose(); } }

Note in the above code, we've provided the concrete class to StructureMap (our IoC container framework) so that it knows which repository class to use when an IUnitOfWorkFactory type is requested. We've also provided an initialization method to StructureMap, which gets executed when the RavenUnitOfWorkFactory class is instantiated. Specifically, the initialization method opens the DocumentStore (database connection) at the server URL and returns the session connection.

Convincing RavenDB to be Friends with ASP .NET MVC Routes

Note, during initialization of the request in Application_Start(), we change the RavenDB IdentityPartsSeparator to a dash, rather than the default slash character. This property is used by RavenDB when auto-generating Identity keys for the database entities (Documents). A typical identifier might look like "people/123". The slash would create problems in the C# ASP .NET MVC framework when attempting to parse the URL on an entity operation, as the "people" and "123" parts would appear as separate controllers/actions. We can resolve this by converting the slash to a dash, which properly displays in the MVC URL route as "people-123", and is passed as a parameter to our controller methods.

Who's the Real EndRequest?

In Global.asax.cs, we've also overridden the Application_EndRequest() method in order to close and dispose of the database session and connection. While this can be optional if you prefer to allow garbage collection to dispose of the connections, it is good practice to explicitly dispose of database connections upon ending the web request. Since we're using the C# ASP .NET MVC framework, which routes all incoming URL requests to the application handler, we would receive multiple EndRequest calls for each URL, image, CSS style link, javascript file, etc. In this case, we would potentially be attempting to dispose of the UnitOfWork for each request to non-executable files in our application. If you dig deep enough into the repository pattern we've designed, you can see that a call to UnitOfWork.Current will create a new RavenDB UnitOfWork object (and a new database connection) if the UnitOfWork.Current is null (which would be the case if we've already closed the connection from the request ending). In this case, the first call to EndRequest would close the database connection as we expect, and the subsequent calls to EndRequest (from images, etc) would open a new connection (although very light-weight in RavenDB NoSQL) and then dispose of it.

We can resolve this issue by filtering out the request to our actual application EndRequest. We can do this by examining the Request.Url object and checking for specific file types to ignore. The above method should be expanded to your particular solution and file-types, but in general, the opening and close of the connection is a light-weight process.

Now the Fun Starts

We've completed the repository and unitofwork pattern for RavenDB. We can now move on to defining our actual entities and user interface. We'll begin by creating a Creature and Weapon entity, and then provide a concrete repository manager for each entity (utilizing the generic repository that we've created above).

One of the benefits of NoSQL is that there is no need to create a database schema. We can simply create the entity classes and persist them to the database as a complete document. The NoSQL database takes care of generating indexes, distributed sharding, and handling storage. This allows us to rapidly develop of business logic layer and avoid heavy database schema design work up front.

The Creature Entity

Our Creature entity will be a basic entity containing a Name, Age, and a child entity of type Weapon. We also provide an Id property, which will be populated automatically by the RavenDB database server. We could also provide our own identifier, such as a Guid, if we prefer.

namespace FlyingCreatures.Models
{
    public class Creature
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        public Weapon Weapon { get; set; }
    }
}

The Weapon Entity

The Weapon entity is another basic entity, including basic data types and an enum.

namespace FlyingCreatures.Models
{
    public class Weapon
    {
        public enum WeaponType
        {
            Melee,
            Ranged,
            Magic
        };

        public string Id { get; set; }
        public string Name { get; set; }
        public WeaponType Type { get; set; }
        public int DamageMinimum { get; set; }
        public int DamageMaximum { get; set; }
    }
}

The NoSQL RavenDB is Not Relational

The NoSQL RavenDB is not relational. That wasn't a typo, but an intentional repeated statement. The RavenDB server stores entities as complete documents. This allows high-speed querying and fetch rates by returning all required data about a specific entity, needed for processing by the C# ASP .NET web application. For example, when we query for a Creature entity, we'll also obtain the Weapon entity embedded inside the Creature document. A second query to pull the child entity, Weapon, is not required. Thus, we perform a single database read operation as opposed to two. This is called denormalization, which may seem alien to traditional relational database design. However, RavenDB NoSQL is not a relational database server. As such, it is able to automatically distribute documents across servers via sharding and optimize query execution. There is no foreign key relationship between our two entities, as each Creature will exist as an independent document, and always contain a complete Weapon entity. For more details, see RavenDB Denormalized References.

Creating a Creature Repository Manager

We can now implement a concrete repository for the Creature entity by using the generic repository we designed above. Note, each concrete repository will contain a private reference to the IRepository interface, binding it to our specific entity. This allows us to customize each repository with any needed business rules prior to executing database operations.

namespace FlyingCreatures.Managers
{
    public class CreatureManager
    {
        #region Names

        private static string[] creatureFirstName = new string[10]
        {
            "White",
            "Black",
            "Light",
            "Dark",
            "Evil",
            "Cunning",
            "Magic",
            "Silver",
            "Golden",
            "Slimy"
        };

        private static string[] creatureLastName = new string[10]
        {
            "Gargoyle",
            "Dragon",
            "Wraith",
            "Harpie",
            "Nymph",
            "Serpent",
            "Bat",
            "Chimaera",
            "Hippogryph",
            "Spirit"
        };

        #endregion

        private static IRepository<Creature> _repository
        {
            get
            {
                return ObjectFactory.GetInstance<IRepository<Creature>>();
            }
        }

        public static Creature Load(string id)
        {
            return _repository.Single(u => u.Id == id);
        }

        public static List<Creature> GetAll()
        {
            List<Creature> list = new List<Creature>();

            // Fetch all entities from the repository.
            list = _repository.GetAll().ToList();

            return list;
        }

        public static void Insert(Creature entity)
        {
            // Add the new entity to the repository.
            _repository.Add(entity);
        }

        public static void Delete(Creature entity)
        {
            // Add any custom business rules.
            _repository.Delete(entity);
        }

        public static Creature CreateRandom()
        {
            Creature creature = new Creature();

            Random random = new Random((int)DateTime.Now.Ticks);

            creature.Name =
HelperManager.CreateRandomName(creatureFirstName, creatureLastName); creature.Age = random.Next(1, 500); creature.Weapon = WeaponManager.CreateRandom(); return creature; } } }

The Weapon entity manager follows a similar implementation, so we'll skip the details for brevity.

Our MVC Razor MasterPage Layout

To create the user interface, we'll take advantage of the .NET MVC Razor template engine. We'll start by creating a master page in Razor, which includes placeholders for the Head, Body, and Code (javascript) sections. This allows us to organize our MVC Razor views and resulting HTML output.

<!DOCTYPE html>
<html>
<head>
    <title>@View.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet"
type="text/css" /> <script src="@Url.Content("~/Scripts/jquery-1.4.1.min.js")"
type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")"
type="text/javascript"></script> <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> @RenderSection("Head", false) </head> <body> @RenderBody() <script language="javascript"> @RenderSection("Code", false) </script> </body> </html>

The Main MVC Razor View

We can now create our main MVC Razor View, based upon the master page created above. Our view will be strongly bound to a list of Creature entities, which will be populated in a datagrid (using the jQuery flexigrid control combined with AJAX).

@model IList<FlyingCreatures.Models.Creature>

@{
    View.Title = "Flying Creatures!";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

@section Head
{
    <script src="@Url.Content("~/Content/flexigrid/flexigrid.js")"
type="text/javascript"></script> <link href="@Url.Content("~/Content/flexigrid/css/flexigrid/flexigrid.css")"
rel="stylesheet" type="text/css" /> } @section Code { initializeGrid(); function initializeGrid() { $('.creatureGrid').flexigrid ({ buttons: [{ name: 'Add', bclass: 'add', onpress: addCreature}, { name: 'Delete', bclass: 'delete', onpress : deleteCreature}], singleSelect: true }); } function addCreature(com, grid) { // Submit the add form. $('#addLink').submit(); } function deleteCreature(com, grid) { // Set the id in the form to be the entity to delete. var rowId = $('.trSelected', grid)[0].id; document.getElementById('id').value = rowId; // Submit the delete form. $('#deleteLink').submit(); } function successLoad() { // The ajax call updates the partial view, so rebuild the grid. initializeGrid(); } } <h2>Flying Creatures!</h2> @using (@Ajax.BeginForm("GenerateCreature", "Index",
new AjaxOptions { LoadingElementId = "ctrlProgress", InsertionMode = InsertionMode.Replace, UpdateTargetId = "creatureGridDiv",
OnSuccess = "successLoad();" }, new { id = "addLink" })) { } @using (@Ajax.BeginForm("Delete", "Index",
new AjaxOptions { LoadingElementId = "ctrlProgress", InsertionMode = InsertionMode.Replace, UpdateTargetId = "creatureGridDiv",
OnSuccess = "successLoad();" }, new { id = "deleteLink" })) { <input type="hidden" id="id" name="id" /> } <div id="creatureGridDiv"> @Html.Partial("_CreatureGrid") </div> <div id="ctrlProgress" style="display:none; position:absolute; top:30%; left:50%;">
<img src="@Url.Content("~/Content/ajax-loader.gif")" alt="ajax progress" /></div>

In the above view Razor code, we include javascript to initialize the flexigrid datagrid control. We also include code to handle the Add and Delete button clicks on the datagrid. You'll also notice two MVC Razor style AJAX forms included within the view. One form handles the Add button event while the other form handles the Delete button event. Both forms submit to the MVC view controller via AJAX, providing a completely seamless user interface experience.

The most important part to the above C# MVC Razor view code is the partial control for the CreatureGrid. This is where we actually output the available Creature entities and display them within a table. The jQuery flexigrid takes care of formatting the table as a datagrid.

The MVC Razor Creature Grid Partial View

@model IList<FlyingCreatures.Models.Creature>

<table class="creatureGrid">
    <thead>
        <tr>
            <th width="150">Creature Name</th>
            <th width="100">Age</th>
            <th width="200">Weapon</th>
        </tr>
    </thead>
    <tbody id="creatureGridBody">
    @foreach (var creature in Model)
    {
        <tr id="@creature.Id">
            <td>@creature.Name</td>
            <td>@creature.Age</td>
            <td>@creature.Weapon.Name (@creature.Weapon.DamageMinimum -
@creature.Weapon.DamageMaximum dmg)</td> </tr> } </tbody> </table>
The Razor PartialView for our CreatureGrid simply contains HTML markup to display a table. We use inline Razor code to include a for-loop, which allows us to iterate of the Creature entities, and display the details in the table. The table will later be formatted as a datagrid via jQuery with the flexigrid control.

Say This 5 Times Fast: Creature Controller

Last, but not least, is the final piece of the puzzle - the CreatureController. This implements our main .NET MVC view controller class. Within the controller, we define the code to initially populate the datagrid and utilize our repository and unitofwork design pattern framework to load and save NoSQL entities.
using FlyingCreatures.Repository.Concrete;
using FlyingCreatures.Managers;
using FlyingCreatures.Models;

    public class CreatureController : Controller
    {
        public ActionResult Index()
        {
            List<Creature> creatureList = CreatureManager.GetAll();

            // If this is the first time running, create a fresh creature.
            if (creatureList.Count == 0)
            {
                Creature creature = CreatureManager.CreateRandom();
                CreatureManager.Insert(creature);
                UnitOfWork.Commit();

                creatureList.Add(creature);
            }

            return View(creatureList);
        }

        [HttpPost]
        public ActionResult GenerateCreature()
        {
            Creature creature = CreatureManager.CreateRandom();
            CreatureManager.Insert(creature);
            UnitOfWork.Commit();

            return PartialView("_CreatureGrid", CreatureManager.GetAll());
        }

        [HttpPost]
        public ActionResult Delete(string id)
        {
            Creature creature = CreatureManager.Load(id);
            CreatureManager.Delete(creature);

            UnitOfWork.Commit();

            return PartialView("_CreatureGrid", CreatureManager.GetAll());
        }
    }

Note, in the above code we follow a basic pattern for using the repository and unitofwork pattern with RavenDB NoSQL. We execute any database operations via our entity manager classes, which all operate within the same database session context (within the same web request). Once we're finished, we commit the UnitOfWork by calling UnitOfWork.Commit(). The UnitOfWork then takes care of completing the session context and flushing to the NoSQL database. The UnitOfWork does not need to be contained within only 1 method. We could have spread the database operations throughout multiple methods or classes and all database operations would continue to be maintained within the same unit of work, committing once we've completed the transaction.

Download @ GitHub and Wiki

You can download the project source code on GitHub by visiting the project home page.
Also view the Flying Creatures GitHub Wiki

Conclusion

The C# ASP .NET MVC framework, combined with the Razor view template engine, provides a rapid development environment for creating model view controller based web applications. By taking advantage of alternate database platform technologies, such as NoSQL, we further optimize .NET MVC web application solutions in a multitude of ways. The RavenDB NoSQL database server allows storage of entities as complete documents, without the requirement for up-front design of a database schema. While the choice between utilizing traditional relational database platforms versus NoSQL platforms will differ depending on the project at hand, technology advances in general, help to create more powerful and maintainable C# ASP .NET web application solutions.

About the Author

This article was written by , founder and chief developer of Primary Objects, a software and web application development company. You can contact Primary Objects regarding your software development needs at http://www.primaryobjects.com
   
comments powered by Disqus
Profile
Learn more about Primary Objects and our goals ..  More
09/09/2013
Primary Objects releases SentimentView Twitter sentiment analysis engine .. More
05/31/2013
Primary Objects releases ColorBot interactive machine learning, AI .. More
Home | About Us | Services | Client Login | Job Opportunities | Contact Us
Copyright © Primary Objects 2013
Privacy Policy
Follow us on Twitter