RavenDB - Image Gallery Project (VII) - User Registration

Published on 2010-10-5

The code for this and all other entries can be found here: http://github.com/robashton/RavenGallery/ 

Now we’re here, let’s talk about registration using the system outlined on the previous pages when combined with RavenDB.

Registration Validation

We’re going to assume we have a form which can be represented by the following view model:

   1:      public class UserRegisterViewModel
   2:      {
   3:          [DisplayName("Username")]
   4:          public string Username
   5:          {
   6:              get;
   7:              set;
   8:          }
   9:   
  10:           [DisplayName("Password")]
  11:          public string Password
  12:          {
  13:              get;
  14:              set;
  15:          }
  16:   
  17:          [DisplayName("Stay logged in")]
  18:          public Boolean StayLoggedIn
  19:          {
  20:              get;
  21:              set;
  22:          }
  23:      }

There, that was easy – but remember on the previous page where I said we only needed views for views and entities for behaviour?  There are always exceptions, and for validation purposes I’m going to define an interface called IUserService which we can ask if users exist or not.

   1:   public class UserRegisterViewModelValidator : AbstractValidator<UserRegisterViewModel>
   2:      {
   3:          public UserRegisterViewModelValidator(IUserService userService)
   4:          {
   5:              this.RuleFor(x => x.Username).NotEmpty().Must(x => !userService.DoesUserExistWithUsername(x));
   6:              this.RuleFor(x => x.Password).NotEmpty();
   7:              this.RuleFor(x => x.StayLoggedIn).NotEmpty();
   8:          }
   9:      }
 
So, we have
 
   1:      public class UserService : IUserService
   2:      {
   3:          public bool DoesUserExistWithUsername(string username)
   4:          {
   5:              throw new NotImplementedException();
   6:          }
   7:      }

I am just going to hard code the query into this service, and I really need an integration test for this, so that’s what I’m going to do, I’m going to fire up a local instance of RavenDB, populate it with some data and run some tests against it. I always write integration tests against RavenDB, even for trivial things like this – just in case RavenDB changes or I make a whoopsee somewhere down the line.

   1:      [TestFixture]
   2:      public class UserServiceTests : LocalRavenTest
   3:      {
   4:          [Test]
   5:          public void WhenUserExists_DoesUserExistWithUsername_ReturnsTrue()
   6:          {
   7:              using (var session = Store.OpenSession())
   8:              {
   9:                  session.Store(new UserDocument()
  10:                  {
  11:                       PasswordHash = "pass",
  12:                       Username = "testUser"
  13:                  });
  14:                  session.SaveChanges();
  15:   
  16:                  UserService service = new UserService(session);
  17:                  bool result = service.DoesUserExistWithUsername("testUser");
  18:                  Assert.True(result);
  19:              }
  20:          }
  21:   
  22:          [Test]
  23:          public void WhenUserDoesNotExist_DoesUserExistWithUsername_ReturnsFalse()
  24:          {
  25:              using (var session = Store.OpenSession())
  26:              {
  27:                  session.Store(new UserDocument()
  28:                  {
  29:                      PasswordHash = "pass",
  30:                      Username = "testUser"
  31:                  });
  32:                  session.SaveChanges();
  33:   
  34:                  UserService service = new UserService(session);
  35:                  bool result = service.DoesUserExistWithUsername("testOtherUser");
  36:                  Assert.False(result);
  37:              }
  38:          }
  39:      }

LocalRavenTest just creates a document store in the local directory and takes care of clearing it up again. As you can see, we have just come across the functionality of saving documents to RavenDB for the first time, and it couldn’t be any simpler.

We pass the IDocumentSession into the UserService and then test the functionality of UserService. Predictably our tests fail because we haven’t written the code yet!

Oh wait, here we go

   1:      public class UserService : IUserService
   2:      {
   3:          public IDocumentSession documentSession;
   4:   
   5:          public UserService(IDocumentSession documentSession)
   6:          {
   7:              this.documentSession = documentSession;
   8:          }
   9:   
  10:          public bool DoesUserExistWithUsername(string username)
  11:          {
  12:              return documentSession.DynamicQuery<User>()
  13:                  .Where(x => x.Username == username)
  14:                  .Any();
  15:          }
  16:      }

And this is how we query the documents stored inside Raven, a simple LINQ query! This time when we run the tests they pass -  which means we have a controller action complete with a validated model ready for processing. I’m still not happy with where that functionality has been placed, but until a better solution presents itself it’ll do as a way of letting us get on.

Registration Command

So, with the above taken place, we have arrived inside the Register action and we know if the model state is valid or not so we send  the appropriate command through our command invoker like so

   1:          [AcceptVerbs(HttpVerbs.Post)]
   2:          public ActionResult Register(UserRegisterViewModel model)
   3:          {
   4:              if (ModelState.IsValid)
   5:              {
   6:                  // Send command
   7:                  commandInvoker.Execute(new RegisterNewUserCommand(model.Username, model.Password));
   8:   
   9:                  // Go back to home
  10:                  return RedirectToAction("Index", "Home");
  11:              }
  12:              else
  13:              {
  14:                  // Return back to the page
  15:                  return View();
  16:              }            
  17:          }

And receive it through our command handler like so:

   1:      public class RegisterNewUserCommandHandler : ICommandHandler<RegisterNewUserCommand>
   2:      {
   3:          private IUserRepository userRepository;
   4:   
   5:          public RegisterNewUserCommandHandler(IUserRepository userRepository)
   6:          {
   7:              this.userRepository = userRepository;
   8:          }
   9:   
  10:          public void Handle(RegisterNewUserCommand command)
  11:          {
  12:              User newUser = new User(command.Username, command.Password);
  13:              userRepository.Save(newUser);
  14:          }
  15:      }

That’s it, job done – we have an entire piece of functionality written with barely any fuss at all.

Tests for the above are all present in the github repository, left out of this post with the exception of the RavenDB interaction

In the next install, we’ll add the sign-in/sign-out functionality to our system.

This used to ask if you wanted to hire me

But chances are I'm not available, as I'm busy shipping stuff.

Drop me an e-mail anyway, as I like interesting problems.

Get in touch

blog comments powered by Disqus