Jump to content

Autofac, DataContext and MemoryCache issue

Posted on:May 5, 2016 at 02:00 AM

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”.