Register IMemoryCache with SimpleContainer using Caliburn Micro

Viewed 243

I've registered an instance of the IMemoryCache (Microsoft.Extensions.Cachine.Memory 3.1.5) as a singleton with a SimpleContainer for use with Caliburn Micro for a WPF application that I'm writing in .Net Core 3.1. In my Bootstrapper, I have the following code

public class Bootstrapper : BootstrapperBase
{
    private SimpleContainer _container = new SimpleContainer();

    public Bootstrapper()
    {
        Initialize();
    }

    protected override void Configure()
    {
        _container.Instance(_container);

        _container
            .Singleton<IWindowManager, WindowManager>()
            .Singleton<IEventAggregator, EventAggregator>()
            .Singleton<IMemoryCache, MemoryCache>();
     }

When I come to use the IMemoryCache in a class it throws an Exception as it requires MemoryCacheOptions to be passed into the constructor of the MemoryCache.

I've tried to add MemoryCacheOptions to the Singleton registration but that's not accepted.

This is the constructor of my ReferenceDataHelper class where I'm trying to inject the IMemoryCache, it fails with a System.Reflection.TargetInvocationException when it constructs the ReferenceDataHelper class.

    public class ReferenceDataHelper : IReferenceDataHelper
{
    private IMemoryCache _memoryCache;
    private IReferenceDataHandler _referenceDataHandler;

    public ReferenceDataHelper(IReferenceDataHandler referenceDataHandler, IMemoryCache memoryCache)
    {
        _referenceDataHandler = referenceDataHandler; 
        _memoryCache = memoryCache;
    }

I can't figure out how to set this up - can anyone offer some help? Thanks!

2 Answers

You could create an instance yourself and then register that instance with the container.

protected override void Configure()
{
    _container.Instance(_container);

    _container
        .Singleton<IWindowManager, WindowManager>()
        .Singleton<IEventAggregator, EventAggregator>()
        .Instance<IMemoryCache>(new MemoryCache(new MemoryCacheOptions()));
}

An instance provided to the container is effectively a singleton because the container will only ever provide the instance you gave it. The benefit of providing an instance to the container is you get to control how the object is created.

I think I have found a solution but I'm not sure if this is the best way to solve it.

I have created a wrapper that implement IMemoryCache, in this class I instantiate a new MemoryCache opbject and pass in the MemoryCacheOptions.

public class ReferenceDataMemoryCache : IMemoryCache
{
    private MemoryCache memoryCache;
    public ReferenceDataMemoryCache()
    {
        MemoryCacheOptions memoryCacheOptions = new MemoryCacheOptions();
        memoryCache = new MemoryCache(memoryCacheOptions);
    }
    public ICacheEntry CreateEntry(object key)
    {
        return memoryCache.CreateEntry(key);
    }

    public void Dispose()
    {
        memoryCache.Dispose();
    }

    public void Remove(object key)
    {
        memoryCache.Remove(key);
        
    }

    public bool TryGetValue(object key, out object value)
    {
        return memoryCache.TryGetValue(key, out value);
    }
}

I updated the Bootstrapper to register the singleton with my wrapper class.

protected override void Configure()
{
    _container.Instance(_container);

    _container
        .Singleton<IWindowManager, WindowManager>()
        .Singleton<IEventAggregator, EventAggregator>()
        .Singleton<IMemoryCache, MemoryCache>();
 }

While this works, if this were a larger class (IMemoryCache has a small number of methods to implement) it feels like a lot of extra work to pass the MemoryCacheOptions to the constructor.

If anyone has thoughts on this approach it would be appreciated.

Related