2

I have used NHibernate in previous enterprise applications but its complex setup and scattered documentation are pushing me to try to use the much simpler PetaPoco in my next application. The main issue I am facing is the implementation of transparent lazy loading to support the following scenario:

public class Customer {
    public string CustomerName { get; set; }
    public List<Order> Orders { get; set; } // Orders should be lazy-loaded
}

public class Order {
    public Int32 OrderNumber { get; set; }
    public List<Product> Products { get; set; } // Products should be lazy-loaded
}

public class Product {
    public string ProductName { get; set; }
}

I gather that I need to use Castle DynamicProxy but I am struggling to understand how to implement the interceptor that will intercept calls to the getters and trigger the retrieval of the related orders or products. Also, once I have the interceptor, how do I ensure it gets used when I retrieve data using PetaPoco?

Any assistance would be greatly appreciated.

Thanks in advance.


Edit: @Groo: I was thinking of creating a layer above the PetaPoco select functions to add interceptors for related collections (using a custom attribute), e.g.:

public class ReferencedByAttribute : Attribute
{
    public String ForeignKeyPropertyName { get; set; }
    public ReferencedByAttribute(String foreignKeyPropertyName)
    {
        this.ForeignKeyPropertyName = foreignKeyPropertyName;
    }
}

using Castle.DynamicProxy;
public class LazyLoadingInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        // TODO: Intercept first call to getter and retrieve related objects from the database
        invocation.Proceed();
    }
}

public class PetaPocoWrapper {
    public List<T> Fetch<T>(String sql) {
        PetaPoco.Database db = new PetaPoco.Database("connectionstring");
        List<T> entities = db.Fetch<T>(sql);
        ProxyGenerator generator = new ProxyGenerator();
        foreach(T entity in entities)
        {
            foreach (PropertyInfo pi in entity.GetType().GetProperties())
            {
                if (pi.GetCustomAttributes(typeof(ReferencedByAttribute), true).Count() == 1) {
                    pi.SetValue(entity, generator.CreateClassProxy(pi.PropertyType, new LazyLoadingInterceptor()), null);
                }
            }
        }
        return entities;
    }
}
JimmyTheOne
  • 233
  • 2
  • 13
  • I know this is not the answer you're looking for, but why would you want to reinvent the wheel, when EF and NH support this out of the box? Especially if you've already used NH before? Classes like these would be almost entirely mapped "by convention", so you would basically only need to specify which properties you want to map. – vgru Apr 30 '14 at 06:51
  • As for your actual question, the only way to achieve this is to modify the PetaPoco source, namely the part where the data is supposed to be fetched. You want to prevent PetaPoco from fetching the actual data, and instead create a proxy which will keep a reference to the `PetaPoco.Database` instance in order to reuse it when any collection-like property is accessed for the first time. Now think of tables which include foreign keys and need to lazily instantiate them also, and you'll see that it's not an easy task. It might be fun to code this, but it certainly won't make ORM "much simpler". – vgru Apr 30 '14 at 07:07
  • @Groo: Thanks for your suggestions. There are a few reasons why I would like to reinvent this particular wheel: PetaPoco is much faster to setup, it requires less coding in the model layer and it seems to be faster at retrieving data. Also, company standards dictate the use of either NH or PetaPoco (no EF for me). If NH had a solid centralised set of tutorials incorporating best practices, it would be tremendously easier for our developers to learn and maintain. As it is, it's a bit of a black box nightmare. – JimmyTheOne May 02 '14 at 00:40
  • Well, we've been using it for some time, and we found it rather simple to use. I guess it can depend on your particular use case, but using [Fluent NHibernate's automapping](http://www.d80.co.uk/post/2011/02/20/Linq-to-NHibernate-Tutorial.aspx) you can pretty much get it running in minutes (although, we used scripted .hbm config files from the start). Also, LINQ provider was tremendously improved starting with NH 3.0. If nothing else, you can really have POCO objects (PetaPoco requires specific attributes to be added to entities in your domain layer if you need to create custom mapping rules). – vgru May 03 '14 at 16:42
  • How do you get NHibernate running in minutes when you have to create a NHibernate configuration files, a session manager class, a session HTTP module class, a repository pattern implementation, logging, entity classes, mapping classes, DAO classes and a framework for running NHibernate validation? Also, how do you figure out what to implement in the session manager and session HTTP module classes? Thanks again. – JimmyTheOne May 06 '14 at 23:15
  • For example, [this page](http://daveden.wordpress.com/2012/06/12/fluent-nhibernate-auto-mappings-asp-net-mvc-3-and-castle-windsor) (you can find the [source code on GitHub](http://www.github.com/Pajamaman/Castle-Fluent-NHibernate-MVC-3)) contains some pretty solid boilerplate code for a MVC app using an NH repository with Castle DI. – vgru May 07 '14 at 08:44
  • @Groo: The first two links you provided (Linq-to-NHibernate-Tutorial and SessionManager) are returning 404 errors. The MVC example is interesting. Thanks again for taking the time to help. – JimmyTheOne May 08 '14 at 01:20
  • Oops, I didn't realize I messed up those links. Ok, again: In the [article above](http://d80.co.uk/post/2011/02/20/Linq-to-NHibernate-Tutorial.aspx), you can see that fluent mapping classes are the only thing you need to do from scratch in a project (e.g. CarMap in the example code) - but I believe that's pretty sensible - you need to think about what you're going to map anyway. SessionManager is pretty much the same all the time, repository code also (well, at least the [generic stuff similar to this](http://agilefreak.wordpress.com/2011/05/19/unit-of-work-and-repository-pattern/). – vgru May 08 '14 at 08:02
  • But I believe the MVC example encompasses the ideas from these two links anyway. – vgru May 08 '14 at 08:04

1 Answers1

1

You have to do it the old fashioned way

    private IList<Product> _Items;
    public IList<Product> Items {
        get {
            if (_Items == null) {
                _Items = DbHelper.CurrentDb().Fetch<Product>("SELECT ..");
            }
            return _Items;
        }
        set {
            _Items = value;
        }
    }
Eduardo Molteni
  • 38,786
  • 23
  • 141
  • 206
  • I had certainly considered this solution, but was hoping to find a different approach that is more transparent to the developers and could save time when coding new systems. – JimmyTheOne May 02 '14 at 00:44
  • Petapoco it's a micro ORM you wont find this kind of things. – Eduardo Molteni May 04 '14 at 15:19
  • I know that Petapoco is a micro ORM and will not have this functionality out of the box. That is why I was trying to extend Petapoco with this functionality. – JimmyTheOne May 06 '14 at 23:06