Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC and MemoryCache - how do i use it?

I have this in my Application_Start:

var crumbsCache = new MemoryCache("breadCrumbsNames");
var crumbsList = new List<CacheItem>
                    {
                        //list of new CacheItem();
                    };
foreach (var cacheItem in crumbsList)
{
    crumbsCache.Add(cacheItem, new CacheItemPolicy());
}

Now, in my controllers i am doing this:

var cache = new MemoryCache("breadCrumbsNames");
var cacheItem = cache.GetCacheItem("nameOfCacheItem");

But then cacheItem is always null, what am I doing wrong?

like image 253
ojek Avatar asked Feb 15 '13 16:02

ojek


1 Answers

I think a better option for you would be to use Ninject or some other dependency injection framework to inject your MemoryCache into the controllers as needed.

You will begin by adding Ninject and Ninject.Mvc3 (and any other related bits) to your ASP.NET MVC project. If you are working in Visual Studio, you can use NuGet to do that. It is quite painless and well-automated.

The next step will be to wrap your MemoryCache into some kind of a interface, such as:

public interface IMemoryCacheService
{
    MemoryCache MemoryCache
    {
        get;
        set;
    }
}

And:

public class MemoryCacheService : IMemoryCacheService
{
    public MemoryCacheService()
    {
        MemoryCache = new MemoryCache();
    }

    public MemoryCache MemoryCache
    {
        get;
        set;
    }
}

Then you define a binding within Ninject so that Ninject knows that when you need something of type IMemoryCacheService, it should give you the instance of MemoryCacheService.

I will paste my own Ninject config class here. The one that will be created in your project will be very similar and will be in a folder called App_Start (which will be created automatically if you use NuGet). The class that Ninject creates by default is called NinjectWebCommon.

public static class NinjectConfig
{
    private static readonly Bootstrapper bootstrapper = new Bootstrapper();

    public static void Start()
    {
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
        DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));

        bootstrapper.Initialize(CreateKernel);
    }

    public static void Stop()
    {
        bootstrapper.ShutDown();
    }

    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();

        kernel.Bind<Func<IKernel>>()
              .ToMethod(context => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>()
              .To<HttpApplicationInitializationHttpModule>();
        kernel.RegisterServices();

        return kernel;
    }

    private static void RegisterServices(this IKernel kernel)
    {
        kernel.Bind<IMemoryCacheService>()
              .To<MemoryCacheService>()
              .InSingletonScope();
              // InSingletonScope() is important so Ninject knows
              // to create only one copy and then reuse it every time
              // it is asked for

        // ignore the stuff below... I have left it in here for illustration
        kernel.Bind<IDbTransactionFactory>()
              .To<DbTransactionFactory>()
              .InRequestScope();
        kernel.Bind<IDbModelContext>()
              .To<DbModelContext>()
              .InRequestScope();
        kernel.Bind<IDbModelChangeContext>()
              .To<DbModelChangeContext>()
              .InRequestScope();
        kernel.Bind<IUserContext>()
              .To<UserContext>()
              .InRequestScope();

        kernel.BindAttributeAndFilter<IgnoreNonAjaxRequestsFilter, IgnoreNonAjaxRequestsAttribute>();
        kernel.BindAttributeAndFilter<ProvideApplicationInfoFilter, ProvideApplicationInfoAttribute>();
        kernel.BindAttributeAndFilter<ProvideSessionInfoFilter, ProvideSessionInfoAttribute>();
        kernel.BindAttributeAndFilter<UseDialogLayoutFilter, UseDialogLayoutAttribute>();
        kernel.BindAttributeAndFilter<CheckResourceAccessFilter, CheckResourceAccessAttribute>();
        kernel.BindAttributeAndFilter<CheckResourceStateFilter, CheckResourceStateAttribute>();
    }

    private static void BindAttributeAndFilter<TFilter, TAttribute>(this IKernel kernel)
    {
        kernel.BindFilter<TFilter>(FilterScope.Action, null)
              .WhenControllerHas<TAttribute>();
        kernel.BindFilter<TFilter>(FilterScope.Action, null)
              .WhenActionMethodHas<TAttribute>();
    }
}

Finally, your controllers will change from:

public class HomeController : Controller
{
    public ActionResult Foo()
    {
        ...
    }

    ...
}

to:

public class HomeController : Controller
{
    private IMemoryCacheService memoryCacheService;

    public HomeController(IMemoryCacheService memoryCacheService)
    {
        this.memoryCacheService = memoryCacheService;
    }

    public ActionResult Foo()
    {
        // use this.memoryCacheService in your controller methods...
    }

    ...
}

Say, you made another service as well called IEmailService following the above-mentioned strategy, and you wanted IEmailService to be available in HomeController as well, then:

public class HomeController : Controller
{
    private IMemoryCacheService memoryCacheService;
    private IEmailService emailService;

    public HomeController(IMemoryCacheService memoryCacheService, IEmailService emailService)
    {
        this.memoryCacheService = memoryCacheService;
        this.emailService = emailService;
    }

    public ActionResult Foo()
    {
        // use this.memoryCacheService in your controller methods...
        // and also use this.emailService in your controller methods...
    }

    ...
}

Ninject will change the ASP.NET MVC controller factory to automatically provide the injected arguments to the controller constructors.

I think this sort of approach is better in the long run that keeping global variables, etc.

like image 140
Umar Farooq Khawaja Avatar answered Oct 04 '22 21:10

Umar Farooq Khawaja