Lazy Loading with Linq to SQL POCO s

Yes, here is that post. While doing Linq to SQL POCO screen cast, and writing the last post about how to achieve POCOs, one of the issues that came up was how to do lazy loading - since we are using pure IList, we of course were not getting lazy loading. That’s something that we didn’t want, so there had to be some workaround this limitation.

The reason that it took me a while to come up with a fair solution, is not that only I am very lazy, but also this was a challenging task. Before I outline the solution, let me explain my train of thoughts so you would understand my pain while hitting the Linq to SQL design decision blocks.

PITA Points

So, to get lazy loading, what we need to do is to intercept a call to a collection, then load it because it is requested at that time. For this, an obvious solution is using Dynamic Proxies. For this matter, I chose LinFu Dynamic proxy and it gave me lots of playground. This is in one pocket.

The most usual thing to do would be, to make the process transparent from user as much as possible. So my idea was to Proxy out the table definitions in context, and I went ahead for that, but what’s that? Table<T> is sealed ! Well done ! So I can’t proxy the sealed tables, because DynamicProxy works on an inheritance basis (with LinFu duck typing, it is possible to unseal, but again Table<T> doesn’t have 1 aggregate interface to choose as a contract. Nice isn’t it ?).

Then I have to proxy the entities, and their related properties. This comes with the implication of marking the to-be lazily loaded properties as virtual.

And another point is that EntitySet doesn’t have a non-generic implementation, so I can’t reach the association without type. This comes with another implication that the Interceptor I am going to write needs to know about the relationship to load, but hey, we can hide this into a repository, and that’s what repositories are for, aren’t they ? (This will make more sense at the end of the post)

Let’s go ahead !

I am still going to use the same simple Questions - Answers model from the last article, but I need to slightly change Question entity as following (it is still a POCO):

   1: public class Question
   2: {
   3:     private int _QuestionId;
   4:
   5:     public virtual int QuestionId
   6:     {
   7:         get
   8:         {
   9:             return _QuestionId;
  10:         }
  11:         set
  12:         {
  13:             _QuestionId = value;
  14:         }
  15:     }
  16:
  17:     private string _QuestionText;
  18:
  19:     public virtual string QuestionText
  20:     {
  21:         get
  22:         {
  23:             return _QuestionText;
  24:         }
  25:         set
  26:         {
  27:             _QuestionText = value;
  28:         }
  29:     }
  30:
  31:     private IList<Answer> _Answer;
  32:
  33:     public virtual IList<Answer> Answer
  34:     {
  35:         get
  36:         {
  37:             return _Answer;
  38:         }
  39:         set
  40:         {
  41:             _Answer = value;
  42:         }
  43:     }
  44: }

Answers list is virtual from now on,so that LinFu can override comfotably.

So let’s look at the tests for lazy loading. What I am going to check for it first is, of course, after getting the instance can I demand for the list and get it successfully ? For e.g, can I do a count ?

   1: [TestMethod()]
   2: public void should_get_correct_answer_count_when_lazily_loaded()
   3: {
   4:   LazyLoadingRepository target = new LazyLoadingRepository(); // TODO: Initialize to an appropriate value
   5:   int id = 1; // TODO: Initialize to an appropriate value
   6:   using (QuestionDataContext context = new QuestionDataContext())
   7:   {
   8:     Question actual;
   9:     actual = target.GetQuestion(context, id);
  10:     Assert.IsNotNull(actual);
  11:     Assert.AreEqual(actual.Answer.Count, 1);
  12:    }
  13: }

Note that this can always be further refactored in order to get by specification, which I am leaving that as an exercise to reader (I always wanted to do that).

Now I need to do a negative test, to see that function does not fool me and does not eagerly load everything. So when I try to access the answers without a context, I should get a Data Context disposed exception:

   1: [TestMethod()]
   2: public void should_throw_when_lazily_loaded_and_reached_outside_the_context()
   3: {
   4:   LazyLoadingRepository target = new LazyLoadingRepository(); // TODO: Initialize to an appropriate value
   5:   int id = 1; // TODO: Initialize to an appropriate value
   6:   Question actual;
   7:   using (QuestionDataContext context = new QuestionDataContext())
   8:   {
   9:      actual = target.GetQuestion(context, id);
  10:      Assert.IsNotNull(actual);
  11:   }
  12:
  13:   try
  14:   {
  15:     int count = actual.Answer.Count;
  16:   }
  17:   catch (Exception ex)
  18:   {
  19:      Assert.IsInstanceOfType(ex, typeof(ObjectDisposedException));
  20:      return;
  21:    }
  22:    throw new Exception(“Should have thrown Object disposed exception, sorry !”);
  23: }

Good, my tests are failing, what a depressive world we inhabit. With the previous implementation which was without lazy loading, The first test would fail with “Object Reference not set to instance of an object” exception, and int the second test Assert would fail because the exception is a NullReferenceException, not an ObjectDisposedException.

Now let’s try to pass these tests with some magic. Here is where LinFu calls us to the dark side. In GetQuestion, we are not going to return the actual object, but instead a proxied Question object. To create a proxy with LinFu, we need a custom interceptor which implements Linfu.DynamicProxy.IInvokeWrapper. Our invoke wrapper needs to know the DataContext, to load the entities and the relationship specification to load the related data. With the light of this info, here is how GetQuestion looks like :

   1: public Question GetQuestion(QuestionDataContext context, int id)
   2: {
   3:   EntityInvokeWrapper<Answer> interceptor = new EntityInvokeWrapper<Answer>(context, (Answer a) => a.QuestionId == id);
   4:   ProxyFactory factory = new ProxyFactory();
   5:   Question retVal = factory.CreateProxy<Question>(interceptor);
   6:   return retVal;
   7: }

This will help me to override Answers list, and replace it with my own implementation. In IInvokeWrapper, we need to implement BeforeInvoke, DoInvoke and AfterInvoke. We are only interested in DoInvoke. I am trying to not to reinvent the wheel, so in background I am using EntitySet’s lazy loading mechanism but that’s transparent to user, since I am proxying the IList<T> with an EntitySet<T> too. But how do I assoicate it with the table in the context and the relationship that I get ? Here is the answer:

   1: public class EntityInvokeWrapper<TChi> : IInvokeWrapper
   2:        where TChi : class
   3: {
   4:   private DataContext Context
   5:   {
   6:     get;
   7:     set;
   8:   }
   9:
  10:   private Func<TChi, bool> RelationshipSpecification
  11:   {
  12:     get;
  13:     set;
  14:   }
  15:
  16:   public EntityInvokeWrapper(DataContext context, Func<TChi, bool> relationship)
  17:   {
  18:     this.Context = context;
  19:     this.RelationshipSpecification = relationship;
  20:   }
  21:
  22:   #region IInvokeWrapper Members
  23:
  24:   public void AfterInvoke(InvocationInfo info, object returnValue)
  25:   {
  26:      //Console.WriteLine(”After”);
  27:   }
  28:
  29:   public void BeforeInvoke(InvocationInfo info)
  30:   {
  31:     //Console.WriteLine(”Before”);
  32:   }
  33:
  34:   public object DoInvoke(InvocationInfo info)
  35:   {
  36:     //Console.WriteLine(”During”);
  37:     string name = info.TargetMethod.Name;
  38:     if (name.StartsWith(“get_”) &&
  39:   info.TargetMethod.ReturnType.GetInterfaces().Contains(typeof(IEnumerable<TChi>)))
  40:     {
  41:       //Console.WriteLine(”Enumerable detected!”);
  42:       EntitySet<TChi> wrapper = new EntitySet<TChi>();
  43:       wrapper.SetSource(this.Context.GetTable<TChi>().Where<TChi>(this.RelationshipSpecification));
  44:       return wrapper;
  45:     }
  46:
  47:     return OriginalCall(info);
  48:    }
  49:
  50:   private object OriginalCall(InvocationInfo info)
  51:   {
  52:     //Console.WriteLine(”Original = ” + info.Target);
  53:     return info.TargetMethod.Invoke(info.Target, info.Arguments);
  54:    }
  55:
  56:     #endregion
  57: }

So as you see, we are doing the magic in DoInvoke, and checking that if it is a property, with an access to the enumeration of the child type (TChi) that we are interested in, we are silently stepping and saying that, hey, what you need for this is an EntitySet, but you can use it as an IList ;) Line 43 does the association between our EntitySet and the context table, and we are returning the EntitySet which has the full lazy loading support - so when it is accessed, it will perform the necessary query on its source. But the Actual Entity is clueless about what’s happening, and all the consumers of the entity who need that lazy loading will treat it as List<T> as it is one.

Conclusion

In this article, I outlined a solution to enable the lazy loading while using POCOs in Linq to SQL. I tried to reuse as much as possible, and wanted to show the pain points what kept me away from a best design. Of course I expect this will always not be a comprehensive solution, but as always, if it gives some ideas, I am happy.

Before leaving, be sure to check out great LinFu stuff from Philip Laureno as it also has other goodies such as simulated duck typing and mixin support, and even a dependency injection framework. Comments and free beers are welcome as usual.

kick it on DotNetKicks.com

Share it on: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google
  • Blogosphere News
  • e-mail
  • YahooMyWeb
  • DotNetKicks
  • DZone

5 Responses so far »

  1. 1

    Russ Painter said,

    October 30, 2008 @ 8:25 am

    Thanks. Although I think I need to read that a couple more times. Do you have source code to share?

  2. 2

    Sidar Ok said,

    October 31, 2008 @ 12:16 am

    No problem, ask any questions you come up with. Here are the sources : http://www.sidarok.com/files/LazyLoadingPocoSample.rar

    I intentionally left it dirty to show the paths I tried to go down. Hope it helps.

  3. 3

    Michal Sakowicz said,

    December 7, 2008 @ 6:28 pm

    Great post, well done! Correct me if I am wrong but as far as I can see current implementation allows you to lazy load only one additional object/collection per class?
    What if Question class would have additional property, beside Answer, that we want to lazy load?

  4. 4

    Sidar Ok said,

    December 8, 2008 @ 2:23 pm

    @Michal,

    You shouldn’t have any problems by calling the generic implementation for multiple properties since a different proxy will be created for each of the properties that needs to be lazily loaded. You need to create another EntityInvokeWrapper for your second entity and hook it up to the loading process as shown above.

  5. 5

    Onur said,

    August 6, 2011 @ 12:05 pm

    hocam gunlerdir

    entity imde bulunan

    public virtual IList ActAreaList { get; set; } degiskenini serialize etmek icin ugrasiyorum nasil yapabilirim?

    Aldigim hata ise su sekilde:

    Cannot serialize member […].ActAreaList of type System.Collections.Generic.IList`1[[[…].ActArea, […], Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] because it is an interface.

    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Comment RSS · TrackBack URI

Say your words

You must be logged in to post a comment.