Browsing:

Category: ASP.NET MVC

Back to basics: db4o and ASP.NET MVC 3

Let's write an app with Db4o and ASP.NET MVC again! Check out this post first.

We've kept it simple & stupid: a model with two entities and a one-to-many relationship. It should set a base for most apps though:

A little background: in a data centre there are serverracks, with blade enclosures or blade chassis. In these enclosures, you can put servers. So, bladeservers go into one blade chassis. Here is the typical one to many relationship!

This is how I set up the solution in Visual Studio:

  • Core contains the business entities
  • Tasks is a console app to get things done quick and dirty
  • Test is for the Unit tests
  • Web is the ASP.NET MVC3 application (Empty) with Razor

So, just create a new class in Core and make it look like this:

Now go to the Db4o website quickly to grab db4o for .NET 4.0, version 8.0.160.14822 MSI. You may have to register first. Install the software.

Now open (add to the solution if you did not yet do so) the Web application and add some references:

I created the following folder structure:

Let's first create the interface for the Unit of Work:

And since we'll be using Db4o for our objectpersistence, let's create a Db4oSession, that derives from ISession.

Next, we need to set up the mechanism that handles the connection to the database for us. This is the SessionFactory:

Now, we are ready to write our first tests:

I can run them successfully in Nunit, which is awesome.

And yes, this all is based on the weblog of my hero Rob Conery. I am so sorry.

Well, we're almost there. We can actually write our controller methods now. Let's add a BladeChassisController and add the Index method:

public ActionResult Index()
        {
            var chassis = SessionFactory.Current.All<BladeChassis>();
            return View(chassis);
            
        }

And this would be our Edit method (that retrieves an instance of the bladechassis to edit):

public ActionResult Edit(int id)
        {
            string cid = id.ToString();
            var chassis = SessionFactory.Current.Single<BladeChassis>(x => x.ChassisID == cid);
            return View(chassis);
        }

And this is the actual altering the 'record' in the 'database':

[HttpPost]
        public ActionResult Edit(int id, FormCollection values)
        {
            try
            {
                // TODO: Add update logic here
                string cid = id.ToString();
                var c = SessionFactory.Current.Single<BladeChassis>(x => x.ChassisID == cid);
                
                UpdateModel(c);
                SessionFactory.Current.Save(c);
                SessionFactory.Current.CommitChanges();
                return RedirectToAction("Index");
            }
            catch
            {
                return View();
            }
        }

And here is the delete method:

public ActionResult Delete(int id)
        {
            try
            {
                string cid = id.ToString();
                var chassis = SessionFactory.Current.Single<BladeChassis>(x => x.ChassisID == cid);
                SessionFactory.Current.Delete(chassis);
                return RedirectToAction("Index");
            }
            catch
            {
                return View("Index");
            }
        }

It's insanely simple. (Until you need to fill a DropDownList, but that's a separate article).

You can download the example here.
Don't forget to run the Console App in Tasks first, because it adds some data to the app.


NoSQL: Eloquera and ASP.NET MVC 3 (and Ninject)

I thought today I'd give Eloquera a swing, just to do something fun and new before starting the other obligations of the day. Eloquera is another objected oriented database, like Db4o or MongoDb. The difference is that Eloquera is natively .NET and it's free whereas db4o is not. But I honestly do not prefer the one above the other. I'm just trying things out.

I wrote a quick web application as a POC.

First, download Eloquera from here. I took the x64 .NET 4.0 version and installed it..
I edited the C:\Program Files\Eloquera\Eloquera Server 3.0\Eloquera.config file so that the Eloquera files are in c:\temp\db by adding DatabasePath="c:\temp\db":

  <Server ServerPort="43962" 
            DatabasePath="c:\temp\db"
            Trace="true" 
            InMemoryAllowed="true"
            Secure="true" 
            AllowUsers="" 
            AllowGroups="Everyone" />
    <SmartRuntime Smart="true" />

Oh and don't forget to take a look in the Samples directory in the program folder.

Then I created an new ASP.NET MVC 3 application and edited the Global.asax.cs. Here is the relevant portion:

Here are the gotcha's:

  • The OpenSession() method opens the database and if it doesn't exist, a new one is created with some dummy data.
  • I use the session-per-web request pattern (the lifetime of a session is the same as a web request). You can see that here in the complete Global.asax.cs.
  • I do not have to deal with Transactions with Eloquera.
  • I have to register types explicitly, else the Views won't recognize them: db.RegisterType(typeof(iSay.Story));

And then I only have to get the current session in my Repositories with (private DB db = MvcApplication.db;) and I'm good to go just like any other app!

using Eloquera.Client;

namespace iSay.Infrastructure
{
    public class StoryRepository : IStoryRepository
    {
      private DB db = MvcApplication.db;
        public IEnumerable<Story> getStories()
        {
           return db.ExecuteQuery("SELECT Story").OfType<Story>();
        }

        public void Add(Story s)
        {
            db.Store(s);
        }

        public int getNumberOfComments(Story s)
        {
            var comments = s.Comments.Count();
            return comments;
        }
    }
}

The controller (I used Ninject again, as you can see I injected the controller with IStoryRepository):

using iSay.Infrastructure;

namespace iSay.Controllers
{
    public class HomeController : Controller
    {
        private IStoryRepository _repo;

        public HomeController(IStoryRepository repo)
        {
            _repo = repo;
        }

        public ActionResult Index()
        {
            var stories = _repo.getStories();
            return View(stories);
        }

    }
}

Hmm, that's almost embarrassing, as easy as this is.

You can view the source code here. Maybe I'll spice it up later but I have to go now.


Nhibernate, ASP.NET MVC 3 and Ninject

In this post I said something about dependency injection and that we all should, well, inject dependencies. Why? To make our applications loosely coupled. In an ASP.NET MVC 3 application, I don't want my Views to know what is in my persistence layer. Hell, I don't even want my Controllers to have a notion. And I want to be able to change the Entity Framework with Nhibernate or DB4o.

And since I have this obsession with Nhibernate lately, this post is again about Nhibernate. And Ninject.

Wire up Nhibernate in ASP.NET MVC 3

A Nhibernate Session is very cheap and does not require too many resources. So, we're setting up a session per web request, in Global.asax. The session opens when the request begins and closes when the request ends. I already blogged about that here. This also how Ayende demonstrates this in the Tekpub series.
Global.asax is also the place where we wire up Ninject. Download Ninject.Web.Mvc and Ninject from here rather than from Github, because strange things happen if you don't.

Now, take a look at my very bloated Global.asax:

Sorry for that.

So, when we issue a webrequest, a Nhibernate Session is established. A Session in Nhibernate is the same as a Unit of Work. It's also comparable with the Linq to SQL's datacontext.
Now let's create a PersonRepository interface:

using System.Web;
using Concepts.Core;

namespace Concepts.Web
{
    public interface IPersonRepository
    {
        IQueryable<Person> GetPersons();
    }
}

And implement the interface with Nhibernate:

using System.Web;
using NHibernate;
using Concepts.Core;

namespace Concepts.Web
{
    public class PersonRepository :IPersonRepository
    {
        private ISession _session;
        public PersonRepository(ISession session)
        {
            _session = session;
        }
        public IQueryable<Person> GetPersons()
        {
            return _session.CreateCriteria<Person>().List<Person>().AsQueryable();
        }
    }
}

Now the Dependency Injection trick. We'll use Ninject to bind the IPersonRepository to our PersonRepository, and to bind the ISession to the method GetCurrentSession from Global.asax:

using System.Web;
using Ninject.Modules;
using NHibernate;

namespace Concepts.Web
{
    public class ConceptsNinjectModule : NinjectModule
    {
        public override void Load()
        {
            this.Bind<IPersonRepository>().To<PersonRepository>();
            this.Bind<ISession>().ToMethod(x => MvcApplication.SessionFactory.GetCurrentSession());
        }
    }
}

And now we can inject our HomeController with the IPersonRepository.. ! When we fire up our controller methods, Ninject sees that we implemented the PersonRepository, which uses Nhibernate. And this, ladies and gentlemen, is so cool, it hurts.

You can get the complete source code here.


Fluent Nhibernate and ASP.NET MVC

For those of you who follow our personal tweets (yeah we have to tweet more with our @itsletnl account), you probably noticed how we're currently involved (or obsessed) by Fluent Nhibernate. Fluent Nhibernate FTW!

Itslet Fluent Nhibernate

Fluent Nhibernate uses poco mappings instead of XML mapping, like its big brother Nhibernate. There is no strong argument to use Fluent Nhibernate instead of Nhibernate, it is just a matter of taste to me. Although, Fluent Nhibernate can automap and autopersist your entities, based on convention, and Nhibernate cannot.

Getting started with Fluent Nhibernate is very simple. Just read their excellent getting started guide. It doesn't make much sense to repeat that on this website with different entities.

But how do we get started with ASP.NET MVC? How do I connect with the Fluent Nhibernate session gracefully?

Handling sessions

I used the book of Jason Dentler to find out more. The most common pattern in web application is the session-per-request. We open the session when the request begins and close it when it ends.

Nhibernate has a contextual session feature which allows a session to be associated with some application specific scope (unit of work). You need to set the current_session_context_class property in the hibernate-configuration section of web.config. But as we use Fluent Nhibernate, we have to use the 'ExposeConfiguration' method.

Here is my Global.asax.cs. Check out line 45: .ExposeConfiguration(c => c.SetProperty("current_session_context_class", "thread_static"))'.

Now we can refer to that session in our controller, like this:

In the example the person is fetched from the database. But to make the app more production worthy, we would use dependency injection rather than directly access the current session. Also, we would not have exposed our connectionstring like that.
But that is a completely new blogpost.


ASP.NET MVC 2 and MongoDb Part 3 – Custom membershipprovider

Out of the box you can easily implement a user management system in a ASP.NET MVC (2) application with very little effort. You can use the SqlMembershipProvider which will create a database for you with tables for maintaining users and their roles. But what if you don't use SQL as a backend but Mongo instead?
Then you need to implement your own custom membership provider. Here are the steps to do just that.

NB: This is just to get you started! It's not a very tightly secured authentication system, so don't use this in production. In production you'll probably want to use SSL and encrypted passwords.

In ASP.NET MVC it is possible to secure controller actions, like this:

[RequiresAuthentication]
public ActionResult Index()
   {
      var session = new MongoSession();
         {
           var userlist = session.users.AsEnumerable();
           return View(userlist);
         }
  }

By adding the RequiresAuthentication attribute above the controller method 'Index', the /User/Index page is secured.

To accomplish this, we need to take the following steps:

  • Add the authentication attribute class
  • Add a membership class which implements the MembershipProvider interface
  • Add a Controller and a View
  • Modify web.config

Let's start with the RequiresAuthenticationAttribute class, which inherits from the ActionFilterAttribute:

public class RequiresAuthenticationAttribute : ActionFilterAttribute
 {
   public override void OnActionExecuting(ActionExecutingContext filterContext)
   {
      //redirect if not authenticated
      if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
          {
           //use the current url for the redirect
          string redirectOnSuccess = filterContext.HttpContext.Request.Url.AbsolutePath;

           //send them off to the login page
          string redirectUrl = string.Format("?ReturnUrl={0}", redirectOnSuccess);
          string loginUrl = FormsAuthentication.LoginUrl + redirectUrl;
          filterContext.HttpContext.Response.Redirect(loginUrl, true);
     }
   }
}

Now that we have this class defined, let's create our custom membershipprovider class.
Firstly, create a class with the name 'BugBaseMembership' and let it implement the MembershipProvider interface:

implement membershipprovider interface

Now scroll down to the last method:

implement3

Here you can implement your own validate user method. And our backend is of course MongoDb so here we go:

public override bool ValidateUser(string email, string password)
 {
   using (var session = new MongoSession())
    {
      User user = session.users
       .Where(u => u.Email == email &amp;&amp; u.Password == password).FirstOrDefault();
       if (user == null) return false;
          else  return true;
     }
 }

Next, create an AccountController and implement the Login method:

namespace BugBase.Controllers
{
    public class AccountController : Controller
    {
        public ActionResult Logon()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Logon(string email, string password, string returnUrl)
        {
            // Basic parameter validation
            if (String.IsNullOrEmpty(email))
            {
                ModelState.AddModelError("email", "You must specify a mailaddress.");
            }
            if (String.IsNullOrEmpty(password))
            {
                ModelState.AddModelError("password", "You must specify a password.");
            }

            if (ViewData.ModelState.IsValid)
            {

                BugBaseMembership provider = new BugBaseMembership();
                bool loginSuccessful = provider.ValidateUser(email, password);

                if (loginSuccessful)
                {
                    SetAuthenticationCookie(email);
                    return RedirectToAction("Index", "Bug");
                }

                else
                {
                    ModelState.AddModelError("_FORM", "The username or password provided is incorrect.");
                }
            }

            // If we got this far, something failed, redisplay form

            return View();

        }

        public virtual void SetAuthenticationCookie(string username)
        {
            FormsAuthentication.SetAuthCookie(username, false);
        }

        public void Logoff()
        {
            FormsAuthentication.SignOut();
            Response.Redirect("/");
        }
    }
}

When the logon is successfull, you are directed to /Bugs/Index and the authentication is set. Which means that you can retrieve the logonname from the user during the session (User.Identity.Name returns the current logon name).

We're not there yet. We have create a View for the user to enter Username and Password.
It should be strongly typed View and derive from the BugBase.Models.User class.

<label for="email">email</label>
<label for="password">password</label>
<input type="submit" value="Log in" />

There is one thing left to do. We need to plug our custom membership system into the website.
Open web.config and add the following in the System.Web section:

implement4

There you go. You just created your own custom membership provider with a MongoDb backend. Pretty easy, no?

You can download the codesample here.