Tuesday, October 26, 2010

RavenDb: Playing with Inheritance and Polymorphism

Over the last couple of days I’ve been playing with RavenDb. So far it’s been a very nice experience. I love the way POCO objects get persisted as complete graphs. You can have nicely encapsulated models too, with private setters, and the client API is a joy to use.

I’m putting together a little project as an excuse to play with Raven, MVC3 and NuPack. I’ll put it up on github soon, but simply put it’s a virtual pocket money bank account for parents and their kids. The model has a concept of Parent and Child both of which inherit a User abstract base class. One of the first things I wanted to check was that the Raven can return a polymorphic collections of Users.

Here’s a test that shows that it can:

[Test]
public void Should_be_able_to_treat_Parents_and_Children_Polymorphically()
{
    using (var session = store.OpenSession())
    {
        var mike = new Parent("Mike Hadlow", "mike@yahoo.com");
        var leo = mike.CreateChild("Leo", "leohadlow", "xxx");
        var yuna = mike.CreateChild("Yuna", "yunahadlow", "xxx");
        var john = new Parent("John Robinson", "john@gmail.com");
        var jim = john.CreateChild("Jim", "jimrobinson", "xxx");

        session.Store(mike);
        session.Store(leo);
        session.Store(yuna);
        session.Store(john);
        session.Store(jim);
        session.SaveChanges();
    }

    using (var session = store.OpenSession())
    {
        var users = session.LuceneQuery<User>().WaitForNonStaleResults().ToArray();

        users.Length.ShouldEqual(5);

        users[0].Id.ShouldEqual("users/mike@yahoo.com");
        users[1].Id.ShouldEqual("users/leohadlow");
        users[2].Id.ShouldEqual("users/yunahadlow");
        users[3].Id.ShouldEqual("users/john@gmail.com");
        users[4].Id.ShouldEqual("users/jimrobinson");

        users[0].GetType().Name.ShouldEqual("Parent");
        users[1].GetType().Name.ShouldEqual("Child");
        users[2].GetType().Name.ShouldEqual("Child");
        users[3].GetType().Name.ShouldEqual("Parent");
        users[4].GetType().Name.ShouldEqual("Child");
    }
}

For this to work, you have to configure Raven to understand that the type ‘User’ is represented by documents with ids that look like ‘users/xxx’:

var documentStore = new DocumentStore
{
    Configuration = new RavenConfiguration
    {
        DataDirectory = path,
        RunInUnreliableYetFastModeThatIsNotSuitableForProduction = true
    },
    Conventions =
    {
        FindTypeTagName = type => typeof(User).IsAssignableFrom(type) ? "users" : null
    }
};

Other than that it just works.

Here’s my User class:

public abstract class User
{
    public string Id { get; private set; }
    public string Name { get; private set; }
    public string UserName { get; private set; }

    protected User(string name, string userName)
    {
        Id = string.Format("users/{0}", userName);
        Name = name;
        UserName = userName;
    }
}

Note the way that the User.Id gets created in the form ‘users/userName’.

No comments: