Autofac, DataContext and MemoryCache issue

Prolog

In one of my web application I use MemoryCache to improve performance. The code looks like this:

public class CurrencyRepository(DbContext context)
{
    private DbContext context;

    public CurrencyRepository(DbContext context)
    {
        this.context = context;
    }    

    public List<Currency> GetList()
    {
        /* read data from context*/
    {
}

public class CurrencyService(ICurrencyRepository repo)
{
    public List<Currency> GetList()
    {
        /* read data from MemoryCache*/
    {

    private void InitCache()
    {
        /*populate MemoryCache from repository*/
    }
}

//Autofac module configuration (only relevant part)
     builder.RegisterType<CurrencyRepository>().As<ICurrencyRepository>().InstancePerLifetimeScope();
     builder.RegisterType<CurrencyService>().As<ICurrencyService>().SingleInstance();

This configuration works. CurrencyRepository reads data from DbContext when CurrencyService wants to populate the cache.
Next time request is coming to CurrencyService the cached version is served. This is excellent!

Problem

The problem is that because CurrencyService is SingleInstance it will always keep reference to CurrencyRepository. That implies reference to DbContext with all data changes tracking information. For small pet-projects this is not an issue. We can live with that. But logically CurrencyRepository is needed only when reading data from persistence layer. The issue will be more visible when cache grows because DbContext will also grow.

Solution

What is needed here is CurrencyRepository creation and disposal when cache is populated. I was thinking how to achieve that with Autofac, but I failed.

What I did instead is following:

public class CurrencyService()
{
    public List<Currency> GetList()
    {
        /* read data from MemoryCache*/
    {

    private void InitCache()
    {
        CurrencyRepository repo = new CurrencyRepository(new DbContext());
        /*populate MemoryCache from repository*/

        repo.Dispose();
    }
}

CurrencyService is responsible for creating and disposing CurrencyRepository. It happens on demand, so the reference is not kept all the time.

TDD – there is no TDD? WTF!!!

Solution presented above is difficult to unit test. I agree with that. This line:

CurrencyRepository repo = new CurrencyRepository(new DbContext());

violates tons of rules 🙂

One stupid simple refactoring would be to pass the Autofac container as a parameter in CurrencyService contructor (and some brain cells are again screaming WTF!!!). This way we achieve **true TDD*.

I will discuss TDD aspect for this specific problem in more details in the future posts.

Epilog

The take away from this post is following note:\”Do not copy blindly the code. Think how it will work. Adjust \”how it will work\” to \”how it should work\”.

Follow me:

4 thoughts on “Autofac, DataContext and MemoryCache issue

  1. Tomas Maconko

    Hi, what I’ve noticed is that you use CurrencyService like repository with GetList method. What I suggest in this case to put InitCache method into repository, if you want to use cache (and use it for specific lifetime scope).

    Reply
    1. Pawel Post author

      That will be in next post. CurrencyService could use only CurrencyRepository and inside CurrencyRepository all cache related stuff would exists internally. The point is to show also the evolution of the code.
      Finally CurrencyService will be SingleInstance and holding one reference to repository. The cache in repository will create dbContext only on demand.

      Goal for this post was to get rid of the bug 🙂

      Reply
  2. Marcin Kurtz

    Good idea is to register DBContext as InstancePerDependency and inject delegate factory to CurrencyRepository like CurrencyRepository(Func contextFactory) then use it to get requested context like var context = contextFactory(), Autofac will dspose context for you. This is much cleaner approach and gives you clean path to write unit tests on repository or not, question is, should repository be unit tested 😉

    Reply
    1. Pawel Post author

      Marcin, this is a very interesting idea! I’ll keep that in mind when refactoring! Thanks!

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *