Wednesday, November 26, 2008

Being Specific with your Generics – Visitor Pattern without the Visitors

Generics are very useful when you have a particular functionality / logic that you want to apply on different types. Instead of having to duplicate the functionality for all your types you can put it in a generic class / method and have all the different types use it. One of the most used examples of this I think is it List<T> this is basically a strongly typed list that can be used for any type, and it works the same for any type.

This is great, but… (yes there is a but), what if what you have is 90% generic and 10% specific to the provided type, lets walk through an example that I encountered when working with Castle ActiveRecord. Each Business Data Object that you want to persist to the database using Castle ActiveRecord needs to be derived from ActiveRecordBase<T> where T is the derived class. This gives certain default functionality to each derived object like Save(). I’ll use the Blog and Post analogy that Ayende uses a lot too as it makes sense.

class Blog : ActiveRecordBase
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using Castle.ActiveRecord;

namespace Fohjin.SpecificWithGenerics
{
    [ActiveRecord]
    public class Blog : ActiveRecordBase<Blog>
    {
        [PrimaryKey]
        public virtual int Id { get; set; }
        [Property]
        public string Name { get; set; }
        [Property]
        public string Author { get; set; }
        [HasMany(typeof(Post))]
        public IList<Post> Posts { get; set; }
    }
}



Post : ActiveRecordBase
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using Castle.ActiveRecord;

namespace Fohjin.SpecificWithGenerics
{
    [ActiveRecord]
    public class Blusing Castle.ActiveRecord;

namespace Fohjin.SpecificWithGenerics
{
    [ActiveRecord]
    public class Post : ActiveRecordBase<Post>
    {
        [PrimaryKey]
        public virtual int Id { get; set; }
        [Property]
        public virtual string PostId { get; set; }
        [BelongsTo("BlogId")]
        public virtual Blog Blog { get; set; }
        [Property]
        public string Title { get; set; }
        [Property]
        public string Content { get; set; }
    }
}
og : ActiveRecordBase<Blog>
    {
        [PrimaryKey]
        public virtual int Id { get; set; }
        [Property]
        public string Name { get; set; }
        [Property]
        public string Author { get; set; }
        [HasMany(typeof(Post))]
        public IList<Post> Posts { get; set; }
    }
}


Now that this is working fine, we wanted to add some generic functionality to both classes, a GetOrCreate (object id) method, which as the name already specifies tries to get an items based on the provided Id from the datebase and if that fails create a new one. So I created a RepositoryItemBase<T> class:

class RepositoryItemBase : ActiveRecordBase where T : class, new()
1
2
3
4
5
6
7
8
9
10
11
12
13
using Castle.ActiveRecord;

namespace Fohjin.SpecificWithGenerics
{
    public class RepositoryItemBase<T> : ActiveRecordBase<T> where T : class, new()
    {
        public T GetOrCreate(object id)
        {
            return Find(id) ?? new T();
        }
    }
}


So instead of having our Business Data Objects inherit from ActiveRecordBase<T> they will inherit from RepositoryItemBase<T> that inherits from ActiveRecordBase<T> again. Thus in affect extending the ActiveRecordBase<T> with the GetOrCreate functionality.

Now this works all fine, except that our Post class has a PostId which is not the PrimaryKey, but this is the key that is used to find the blog posts. This means that we need to specifically handle some generic types different than others. In my example I only have one extension and you could argue to add that method to the actual Business Data Objects, but in our solution we have many more Business Data Objects and there are only a few different GetOrCreate variants. And there are more generic extension methods. I could also create new derived objects from RepositoryItemBase<T> and have the specific functionality in there, but that didn’t look to appealing to me. So here is my implementation using an adapted Visitor Pattern using a bit like a very simple IoC container and Delegates.

class RepositoryItemBase : ActiveRecordBase where T : class, new()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using System;
using System.Collections.Generic;
using Castle.ActiveRecord;

namespace Fohjin.SpecificWithGenerics
{
    public class RepositoryItemBase<T> : ActiveRecordBase<T> where T : class, new()
    {
        private readonly IDictionary<Type, Func<object, T>> delegatesByType;

        public RepositoryItemBase()
        {
            delegatesByType = new Dictionary<Type, Func<object, T>>();
            InitializeDelegatesByType();
        }

        public T GetOrCreate(object id)
        {
            if (!delegatesByType.ContainsKey(typeof(T)))
                throw new TypeLoadException("The provided type \"{0}\" is not supported!", typeof(T).ToString());

            return delegatesByType[typeof(T)].Invoke(id);
        }

        private void InitializeDelegatesByType()
        {
            delegatesByType.Add(typeof(Blog), x => Find(x) ?? new T());
            delegatesByType.Add(typeof(Post), x =>
            {
                var posts = FindAllByProperty("PostId", x);
                if (posts.Length > 0)
                {
                    return posts[0];
                }
                return new Post { PostId = x.ToString() } as T;
            });
        }
    }
}


As you can see I added a IDictionary<Type, Func<object, T>> this is for the IoC principle but instead of returning an object for a specific type it returns a function. This is where the Visitor Pattern comes into play, you define specific logic for each type, just like you would define a Visitor for each type, but instead of creating the different Visitor classes I defined different delegates with the same signature.

edit
I got some comments from people about something I did know, but maybe didn’t want to see :) the class RepositoryItemBase is violating the Open Closed Principle (OCP). As I also explain in a comment to Raymond we moved away from Castle ActiveRecord:

The main reason we were using Castle ActiveRecord was because we could configure our NHibernate mappings using Attributes instead of writing XML. But how Castle ActiveRecord places all the extra logic in the data containers was bothering us on different areas. I want the application to have a single point to do any data related tasks so I ended up with ugly methods that were like this: public class Repository { public void Save(ActiveRecordBase entity) { entity.Save(); } }

So I re-factored Castle ActiveRecord out of the solution and started using Fluent NHibernates AutoMap functionality for the mapping and DB schema generation (while developing) and plain NHibernate for the persistence tasks, much cleaner.

I also re-factored the non primary key name to be SourceId and placed this in the following interface:

interface IRepositoryItemWithSourceId
1
2
3
4
5
6
7
namespace Fohjin.SpecificWithGenerics
{
    public interface IRepositoryItemWithSourceId
    {
        string SourceId { get; set; }
    }
}

Now all the Business Data Objects that have this special Id need to inherit from this interface, and that lets me do to following thing:

class Repository : IRepository
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using FluentNHibernate.Framework;
using InfoDoc.Server.BL.BDO.Interfaces;
using NHibernate;
using NHibernate.Criterion;

namespace Fohjin.SpecificWithGenerics
{
    public class Repository : IRepository
    {
        private readonly ISessionSource sessionSource;

        public Repository(ISessionSource source)
        {
            sessionSource = source;
        }
        public IList<T> Query<T>(Expression<Func<T, bool>> function)
        {
            ISession session = sessionSource.CreateSession();
            FluentNHibernate.Framework.IRepository nHibernateRepository = new FluentNHibernate.Framework.Repository(session);
            return nHibernateRepository.Query(function);
        }

        public T GetOrCreate<T>(object id) where T : class, IRepositoryItemWithSourceId, new()
        {
            var items = Query<T>(r => r.SourceId == id.ToString());
            return items.Count > 0 ? items[0] : new T { SourceId = id.ToString() };
        }
    }
}


As you can see now we are talking Generics again and don't have any dirty techniques to threat a generic value in a specific way. I do plan to explore the technique that Raymond suggested as well as a technique John suggested in a feature post, but for this problem the current solution is probably the best.

I hope you found this useful and as always if it can be improved please let me know.

-Mark

2 comments:

Unknown said...

Hey Mark,

The first thing I would do is remove the InitializeDelegatesByType()
code from your repositoryBase, since now the abstraction (your base class) is depending on implementation (Blogs and Posts), the base class is now not closed for modification, and will have to be reopened for every new type that needs to be added for the find problem.

You could add an AddFindDelegate method so that the initialization can be done in every subclass, or somewhere in configuration.

Maybe what would be a good idea is to use reflection in the base class to find out which property has the primary key attribute associated with it and to get to the right id in that way.

Mark Nijhof said...

Hi Raymond,

Thanks for the feedback :)

I totally agree that the InitializeDelegatesByType() is dirty, also I didn't want to place logic in the sub classes as they are only supposed to be data containers. But since Castle ActiveRecord was already placing all the persistence logic in the ActiveRecordBase I thought that my new GetOrCreate method should be in the same location.

The main reason we were using Castle ActiveRecord was because we could configure our NHibernate mappings using Attributes instead of writing XML. But how Castle ActiveRecord places all the extra logic in the data containers was bothering us on different areas. I want the application to have a single point to do any data related tasks so I ended up with ugly methods that were like this: public class Repository { public void Save(ActiveRecordBase entity) { entity.Save(); } }

So I re-factored Castle ActiveRecord out of the solution and started using Fluent NHibernates AutoMap functionality for the mapping and DB schema generation (while developing) and plain NHibernate for the persistence tasks, much cleaner.

Also take a look at the changes in the post, this is the new solution I came up with after making some concessions.

-Mark