Browsing:

Category: ASP.NET MVC

ASP.NET MVC and MongoDb Part 2

In the former blogpost we've explored MongoDb and we've created a very basic MVC application with the norm.dll. We created a User and a Bug class, but we've only implemented some CR (and not UD) for the User class.
In this post I will explore how to deal with a one-to-many relationship in a document based database, i.e. a non relational database.
classdiagram

One user can submit more bugs, so there is a one to many relationship between the user and the bugs. But how to implement that in an app where there is no relational database and where there are no constraints?

This were the ViewModel pattern comes in.

Below I created a new class, the BugViewModel class, which is a combination of attributes or fields from the Bug and the User class:

namespace BugBase.ViewModels
{
    public class BugViewModel
    {
        public ObjectId Id { get; set; }
        public ObjectId UserId { get; set; }
        public string ShortDescription { get; set; }
        public string LongDescription { get; set; }
        public DateTime? Date { get; set; }
        public bool Status { get; set; }
        public string Email { get; set; }
        public string UserName { get; set; }
    }
}

So, that's great. Thank you Framework! But however do I map those attributes to the User and Bug classes?
There is AutoMapper, an object to object mapper by Jimmy Bogard, available here. But I think Automapper is a bit complex to use. For simple scenario's, like BugBase, there is a solution in the ASP.NET MVC futures assembly. This assembly provides a static class, the ModelCopier, that can also use for copying values between View Model objects and Domain model objects. It's available here at Codeplex. The dll is called Microsoft.Web.Mvc.dll. Add a reference to it in BugBase and you're good to go.

The controller create method, would look like this:

[HttpPost]
 public ActionResult Create(BugViewModel bugviewmodel)
   {
       var bug = new Bug();
       ModelCopier.CopyModel(bugviewmodel, bug);

       using (var session = new MongoSession())
        {
         string username = bugviewmodel.UserName;
            var user = session.users
              .Where(u => u.UserName == username)
                  .FirstOrDefault();

                bug.User = user;
                session.Save(bug);                
         }
      return RedirectToAction("Index");
      }
 }

You see how easy it is to add a user to a bug using the ViewModel Pattern and copy the viewmodel to the original domain class.

Next time we'll spice up the userinterface and we'll add some jQuery and Ajax.


(Another) ASP.NET MVC 2.0 app with MongoDb Part 1

I have been fiddling around with Db4o and well.. I did not finish my app. Which probably means that it is time to try out yet another document based database. So. Let's cover MongoDb this time.

I will now create a webapplication where users can submit bugs for a certain software project. Let's call it BugBase.

The first step is to create the folders c:\data and c:\data\db. These are the default folders that you can override if you wish. The next step is to download the Mongo binaries and extract them in c:\data\db.

As with many nosql databases they're obscenely trivial to install. Click on mongod.exe in the bin folder and the Mongo server is running:

mongodb

Let's see if this works. Let's create a bug record for "unable to save" in a database.

C:\data\db\bin>mongo
MongoDB shell version: 1.4.2
url: test
connecting to: test
type "exit" to exit
type "help" for help
> b = { description : "unable to save" };
{ "description" : "unable to save" }
> b = { bugtext : "unable to save the records"};
{ "bugtext" : "unable to save the records" }
> db.bugbase.save(b);
> db.bugbase.find();
{ "_id" : ObjectId("4c03a7fa0e6c000000004226"), "bugtext" : "unable to save the
records" }
>

mongocli

Apparently it does work 😉
I inserted a record in a document (ie table) and Mongo created a unique identifier for me. Cool.

Put Mongo in an ASP.NET MVC 2 app

First step is to create an MVC2 app in Visual Studio 2010 and get rid of all the standard stuff from the template.

Did somebody actually write a MongoDb to Linq provider? But yes of course! My hero Rob Conery did. And Andrew Theken took it further and hosts the project on Github.

What a surprise, only yesterday Andrew uploaded a new version. Wherever would I be without the efforts of these brilliant people? Download the latest Norm project (should I use Github) and compile it in Visual Studio. Grab the norm.dll from the bin folder and add a reference to it in the BugBase project.

Next I need an example as of how to use this dll. At Codeplex Shiju Varghese wrote an example application, which I will use as a reference. But you don't need to check out all these examples. It's just to give the credits to the people that made all this possible.

For BugBase we need at least 2 classes. Bug and User. Create them in the Models folder.

 public class User
    {
        [MongoIdentifier]
        public ObjectId Id { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }
        public List Bugs { get; set; }

        public User() {
            Bugs = new List();
        }
    }
public class Bug
    {
        [MongoIdentifier]
        public ObjectId Id { get; set; }
        public string ShortDescription { get; set; }
        public string LongDescription { get; set; }
        public DateTime? Date { get; set; }
        public bool Status { get; set; }
        public User User { get; set; } 
    }

Now, let's add a folder called Infrastructure to put all the data layer stuff in. Create a new class called MongoSession.cs.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Norm;
using Norm.Collections;
using Norm.Linq;
using Norm.Responses;
using BugBase.Models;

namespace BugBase.Infrastructure
{
    internal class MongoSession : IDisposable
    {
        private readonly MongoQueryProvider provider;

        public MongoSession()
        {
            var mongo = new Mongo("BugBase", "localhost", "27017", "");
            this.provider = new MongoQueryProvider(mongo);
        }

        public IQueryable Bugs
        {
            get { return new MongoQuery(this.provider); }
        }
        public IQueryable users
        {
            get { return new MongoQuery(this.provider); }
        }
        public MongoQueryProvider Provider
        {
            get { return this.provider; }
        }

        public void Add(T item) where T : class, new()
        {
            this.provider.DB.GetCollection().Insert(item);
        }

        public void Dispose()
        {
            this.provider.Server.Dispose(); 
        }
        public void Delete(T item) where T : class, new()
        {
            this.provider.DB.GetCollection().Delete(item);
        }

        public void Drop()
        {
            this.provider.DB.DropCollection(typeof(T).Name);
        }

        public void Save(T item) where T : class,new()
        {
            this.provider.DB.GetCollection().Save(item);            
        }
    }    
  }

We've got the models in place and the datalayer, so let's create a UserController.

usercontroller

Let's create a view.

usercreateview

Create a strongly typed view with the BugBase.Models.User View Data Class and Create as View Content.

Now let's add the controllermethods to create a user and list them:

 public ActionResult Index()
        {
            using (var session = new MongoSession())
            {
                var userlist = session.users.AsEnumerable();
                return View(userlist);
            }
        }
 [HttpPost]
        public ActionResult Create(User user)
        {
            using (var session = new MongoSession())
            {
                session.Save(user);
            }
            return new RedirectResult("/User");
        }

Compile the project and behold the result:

usercreate

And the list: (OK, never mind the poor security of this app)

userlist

Next time let's implement the bugs. You can download the very beta beta beta BugBase App here.


Db4o and ASP.NET MVC 2 part 2

In the last article I explained how to set up an ASP.NET MVC 2 project with a Db4o backend to create a weblog application. In the spirit of Domain Driven Design we created a single entity 'Story'. We managed to create a separate class for database access. We did not however, implement the Unit of Work or the Repository pattern. We'll get to that later. Let's have a working application first and we'll build in the abstractions later.

So, we created a StoryController in the Controllers folder and we implemented the Create method. After clicking Submit the method takes us to Index:

datienaarindex

So we need to go to line 19 and right click on Index and click Add View, just like before:

datienaarindexview

And then we can scaffold the Index View like this:

index

Here is the code to list all the Story entries in the database:

public ActionResult Index()
        {
            //instantiate the db4o objectcontainer:
            IObjectContainer db = MySingleInstance.Instance().
                OpenDataBase();

            try
            {
                //this is the tricky part.
                //We select all the Story objects from the db:
                var query = from Story s in db select s;
                //And we'll add that to a list:
                List allStories = new List();
                foreach (Story story in query)
                {
                    allStories.Add(story);
                }
                //We'll present the List to the View
                return View(allStories);
            }
            finally
            {
                //Close the database.
                db.Close();
            }
        }

Why am I copying the query result to a List? The answer is, if I don't, the database closes before the page is displayed, so the page remains empty and we'll get a DataBaseClosed Exception.

Now run the application (hit F5) and browse to http://localhost:portnr/Story.

List

And we see all our entries. If you click Create New, you'll be directed to the form we made in part 1 and you can add another blog article. So basically the weblog application is ready, right?

The only thing is, it looks like shit. And I am no designer. So let's grab a nice minimalist CSS template from the web. I googled 'minimalist elegant css' and came across Inkcover. The css of this site is a bit cluttered and it's derived from a templating engine from Coldfusion I believe. But with a little hacking it came out quite nice.

Ik Zeg..

I'll think I'll have to notify the designers.

Next time I will cover validation at last. And perhaps upload the code.


Db4o and ASP.NET MVC 2 part 1

Today Scott Guthrie anounced the final release of ASP.NET MVC 2. So it's a good time to give this a spin and check out the enhancements, like the new model validation and asynchronous controllers.

The best way to learn is to actually build an application. That's what I would like to do here. I am going to build a blog application. For persistence I will use db4o, an object oriented database. Reasons for using Db40 is convenience. With Db4o, you don't need to run SQL Server, which makes your code very portable and extremely easy to adjust to changing businessneeds.

The way I code this application is probably not from the books. It's just how I think I should code. If you can think of any improvements please do not hesitate and tell us.

IkZeg
I'm going to call this application 'IkZeg'. It's a blog application, so we need a class for stories and for writers. We also like to enable comments. The classdiagram would be something like this:
ClassDiagram2

Now install ASP.NET MVC 2 and download Db40 for .NET 3.5

Start Visual Studio (2008, I won't use 2010 until it's RTM-ed) and create a new ASP.NET MVC 2 Web Application: IkZeg.

start

I'm not adding a Test project because I just want to get going as fast as possible.

Then, rightclick the folder Models and add a new class and name it: Story.

public partial class Story
    {
        public Story()
        {
            storyId = System.Guid.NewGuid();
            commentsCount = 0;
            storyDate = DateTime.Now;
        }
        public Guid storyId { get; set; }
        public string storyTitle { get; set; }
        public string storyText { get; set; }
        public DateTime storyDate { get; set; }
        public Writer writer { get; set; }
        public int commentsCount { get; set; }
        public List

Also add a Writer and a Comment class:

 public class Writer
    {
       public Writer()
       {
           registerDate = DateTime.Now;
       }
        public string username { get; set; }
        public string password { get; set; }
        public string email { get; set; }
        public DateTime registerDate { get; set; }
        //the birthday is nullable:
        public DateTime? birthday { get; set; }
    }

public class Comment
    {
        public Comment()
        {
            postedDate = DateTime.Now;
        }
        public string username { get; set; }
        public string commentOn { get; set; }
        public DateTime postedDate { get; set; }
        public string commentText { get; set; }
    }

Now that we've created the model it's time to dive into persistence with Db4o.

Data Access with Db4o

An object-oriented database stores objects directly in the database without having to map it to a relational structure. This is a major timesaver and it has lots of other advantages (and disadvantages) but that discussion is a bit out of scope. Db4o is just awesome to use.

Add references to dll's from Db4o: Db40objects.Db4o, Db40objects.Db4o.CS and Db40objects.Db4o.Linq:

ref

To implement persistence I followed Rob Conery because yes, he's my hero. I learn an awful lot from him and from the Tekpub vids.

Data Access

To get started with Db4o is really simple. After adding the references you can start doing CRUD operations immediately, like so:

using(IObjectContainer db = Db4oEmbedded.OpenFile(Db4oEmbedded.NewConfiguration(),@"c:\temp\thebigblahdatabase.dat"))
{
    Story story = new Story();
    story.storyTitle="Wat doen we met Pasen?!"
    story.storyText="Codekloppen, obviously!"
    db.store(story);
    db.Close();
}

But that would mean we would be repeating ourselves continuously. With every CRUD operation, we would have to write the same piece of code and worry about closing the database. It would be nice to abstract these operations away. Also, maybe we would like to migrate to MySQL or Oracle later. So let's take another approach.

Unit of Work

We'll set up a Unit of Work. This takes care of CRUD methods. The value of using a Unit of Work pattern is to free the rest of your code from these concerns so that you can concentrate on business logic.

DataAccess

using Db4objects.Db4o;
using Db4objects.Db4o.Linq;
using Db4objects.Db4o.CS;

namespace IkZeg.Models
{
    public interface ISession:IDisposable 
    {
        void CommitChanges();
        Db4objects.Db4o.IObjectContainer Container { get; }
        void Delete(System.Linq.Expressions.Expression> expression);
        void Delete(T item);
        void DeleteAll();
        void Dispose();
        T Single(System.Linq.Expressions.Expression> expression);
        System.Linq.IQueryable All();
        void Save(T item);
    }
}

There's a lot of funky generics in there. That's good, because we need to implement this interface for all our ikZeg classes (Story, Writer and Comment).

Now let's create a class that implements the ISession interface we just created:

using Db4objects.Db4o;
using Db4objects.Db4o.Linq;
using Db4objects.Db4o.CS;

namespace IkZeg
{
    public class Db4oSession : IDisposable,ISession
    {
        private IObjectContainer db;
        public IObjectContainer Container {
            get {
                return db;
            }
        }

        public Db4oSession(IObjectServer server) {
            db=server.OpenClient();
        }

       /// 
        /// Returns all T records in the repository
        /// 
        public IQueryable All() {
            return (from T items in db
                    select items).AsQueryable();
        }
/// 
        /// Finds a subset of items using a passed-in expression lambda
        /// 
        public IQueryable Where(System.Linq.Expressions.Expression> expression)
        {
            return All().Where(expression);
        }
 
        
        /// 
        /// Finds an item using a passed-in expression lambda
        /// 
        public T Single(System.Linq.Expressions.Expression> expression) {
            return All().SingleOrDefault(expression);
        }
 
        /// 
        /// Saves an item to the database
        /// 
        /// 
        public void Save(T item) {
            db.Store(item);
        }
 
        /// 
        /// Deletes an item from the database
        /// 
        /// 
        public void Delete(T item) {
            db.Delete(item);
        }
 
        /// 
        /// Deletes subset of objects
        /// 
        public void Delete(System.Linq.Expressions.Expression> expression) {
            var items = All().Where(expression).ToList();
            items.ForEach(x => db.Delete(x));
        }
 
        /// 
        /// Deletes all T objects
        /// 
        public void DeleteAll() {
            var items = All().ToList();
            items.ForEach(x => db.Delete(x));
        }
 
 
        /// 
        /// Commits changes to disk
        /// 
        public void CommitChanges() {
            //commit the changes
            db.Commit();
 
        }


        public void Dispose() {
            //explicitly close
            db.Close();
            //dispose 'em
            db.Dispose();
        }
    }
}

       

Now create a class that inherits from Db4oSession and it all starts making sense:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Db4objects.Db4o;
using Db4objects.Db4o.Linq;
using Db4objects.Db4o.CS;

namespace IkZeg.Models
{
    public class IkZegSession : Db4oSession
    {
        internal IkZegSession(IObjectServer server) : base(server) { }

        public IQueryable stories
        {
            get
            {
                return All();
            }
        }


        public IQueryable storiesByWriter(string writer)
        {
            return Where(s => s.writer.ToString() == writer);
        }


        public Story getStory(string id)
        {
            return Single(s => s.storyId.ToString() == id);
        }

        public int getComments(string storyGuid)
        {
            return Where(c => c.commentOn == storyGuid).Count();
        }

       }
}

And here is the class that holds the Db40 file- and connectioninfo:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Db4objects.Db4o;
using Db4objects.Db4o.CS;
using Db4objects.Db4o.Linq;

namespace IkZeg.Models
{
    public class SessionFactory
    {
        static ISession _current;
       
        static IObjectServer _server;

        public static ISession CreateSession()
        {
            if (_server == null)
            {
                _server = Db4oFactory.OpenServer( @"c:\temp\debigblahdatabase", 0);
            }

            return new IkZegSession(_server);
        }

        public static IkZegSession Current
        {
            get
            {
                if (_current == null)
                    _current = CreateSession();
                return (IkZegSession)_current;
            }
        }

    }
}

Let's use this

So, let's create a story and show it on the website. To do that, I'll need a controller and a view. Let's start with the controller.

Right click the folder Controllers and click Add Controller.
Name the Controller StoryController and add action methods. Like this:

contr2

Add using statements for Db4o and the Models folder:

using IkZeg.Models;
using Db4objects.Db4o;
using Db4objects.Db4o.Linq;
using Db4objects.Db4o.CS;

Just below the StoryController declare an IkZegSession:

  public class StoryController : Controller
    {
        IkZegSession db = SessionFactory.Current;

Scroll down to line 63 or so to the Create method and make it look like this:

   [HttpPost]
        public ActionResult Create(Story story)
        {
            if (ModelState.IsValid)
            {
                    story.writer = new Writer { username = Request.Form["writer.username"] };
                    story.storyDate = DateTime.Now;
                    db.Save(story);
                    return RedirectToAction("Index");
                }

                        else
            {
                return View();
            }
        }

Now add the view. Right click the Create Method (along line 65) and click Add View.

addView

Create a strongly-typed view. The data class is IkZeg.Models.Story. That's the story class we made earlier.

view2

Almost done now. Let's take a look a this View though. In the folder Views, you have now a Story folder, with a file named Create.aspx. Open the file and remove the HTML fieldset (because I think it's ugly). Also remove the Guid labels and textboxes and the Postdate labels and textboxes. We don't need them as they are generated in the Create method in the controller.

Hit debug (or F5). The webbrowser starts with the standard webpage. Browse to: http://localhost:51305(or whatever portnumber)/Story/Create. And there we are. It's a bit ugly but we will work at the layout later.
cr

After clicking Create you'll get a Yellow Screen of Death, which is okay, as we did not implement the Index method yet. I will cover that (and validation) in part 2.