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: }
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: }
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.
2020 © Rob Ashton. ALL Rights Reserved.