How to use Unity Interception to create Attribute Based Cache

1- Download Unity Interception using NuGet.

2- Create a custom attribute

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class CacheAttribute : Attribute
{
    public double AbsoluteExpiration { get; private set; }

    public CacheAttribute(double absoluteExpiration)
    {
        AbsoluteExpiration = absoluteExpiration;
    }
}

3- Create  Interception Behavior

public class CachingInterceptionBehavior : IInterceptionBehavior
{
    private object _LockSync = new object();

    public bool WillExecute
    {
        get
        {
            return true;
        }
    }

    public CachingInterceptionBehavior() { }

    public IEnumerable GetRequiredInterfaces()
    {
        return Type.EmptyTypes;
    }

    public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
        IMethodReturn result = null;

        CacheAttribute cacheAttr = input.Target.GetType().GetMethods().FirstOrDefault(m => m.Name == input.MethodBase.Name)
            ?.GetCustomAttributes(typeof(CacheAttribute), false).FirstOrDefault() as CacheAttribute;

        if (cacheAttr != null)
        {
            result = GetItem(input.MethodBase.Name, false) as IMethodReturn;
            if (result == null)
            {
                result = InvokeSource(input, getNext);
                if (result.Exception == null)
                    AddItem(input.MethodBase.Name, result, cacheAttr.AbsoluteExpiration);
            }
        }

        if (result == null)
            result = InvokeSource(input, getNext);

        return result;
    }

    private IMethodReturn InvokeSource(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
    {
        return getNext()(input, getNext);
    }

    private void AddItem(string key, object value, double seconds)
    {
        lock (_LockSync)
        {
            MemoryCache.Default.Add(key, value, DateTimeOffset.Now.AddSeconds(seconds));
        }
    }

    private void RemoveItem(string key)
    {
        lock (_LockSync)
        {
            MemoryCache.Default.Remove(key);
        }
    }

    private object GetItem(string key, bool remove)
    {
        lock (_LockSync)
        {
            var res = MemoryCache.Default[key];

            if (res != null)
            {
                if (remove == true)
                    MemoryCache.Default.Remove(key);
            }

            return res;
        }
    }
}

4- Register both the Interception extension and the new Cache behavior

Container.AddNewExtension()
         .RegisterType(new ContainerControlledLifetimeManager());

5- That’s it. It’s time to start decorating your functions

[Cache(absoluteExpiration: 60)]
public async Task GetValue()
{
  .....
}

 

Managing Object lifetime using Prism and Unity

 

The Problem:

The problem with PRISM implementation is the lack of control over objects lifetime. Consequently, there is no flexibility to build certain scenarios where you want to dispose your old objects and recreate them again once needed. An example of this, imagine you have a parent view which controls the navigation between its child views, and you want to define a custom behavior to get rid of(Dispose) this parent and all its descendants (including any descendant objects) at the moment you navigate away to a different parent?

I found it impossible to achieve such a scenario with the current implementation, even if you explicitly managed your container lifetime. One reason for that is the Region Manager registers it’s views in a separate view registry which is controlled by the Region Manager itself, leaving no control from the outside.

The Idea:

One of the solutions observed is to extend Prism capabilities to allow the Region Manager to register its views in a container (It could be a child container of a container, I will discuss this later) rather than at its own registry, hence disposing this container, whenever needed, will  dispose its registered view and any other unwelcome dependencies.

Essentially anywhere, which you will be on control of calling Dispose() on the View/View Model in response to some user action (such as a Tab close, or  closing/removing a View) might benefit from using  a child container of a container.

A typical arrangement when using Prism might be something like this:

PRISM module

The Solution:

Here is what I did:

  1. Extend the Region Manager with two more functionalities:
    • RequestNavigateUsingContainer: This function replaces RegionManager.RequestNavigate, and it does the following, it uses the given container as a source of view repository to request navigate from.
    • RequestNavigateToDisposableView: This function replaces RegionManager.RequestNavigate as well, and it does the following:
      • Creates a child container of the given container.
      • Uses the new container to register an instance of the required view.
      • Navigates to this view using the new container.
  2. Extend Unity with RegisterViewForNavigation function: A function to register a type of view to be used later with navigation (You should use this function in conjunction with RequestNavigateUsingContainer discussed above). IMPORTNAT: When using the Unity container the registering against the typeof(Object) is a vital part of the registration. If you do not use this function to register your objects, you will likely just get the View name shown as a string representation rather than the actual View.
  3. Extend RegionNavigationContentLoader and RegionNavigationService to allow navigation using a container.
  4. Extend the Region Behavior with functionality to dispose both the view and it’s view model once a delete is detected at Region Manager view collection: This helper behavior will fetch the view and it’s view model and call the dispose method on both of them when it detects a view is being deleted from the view collection (Deleting a view from the view collection is the job of the DisposableView class. Defined at #7).
  5. Configured Prism to use the following: (Including the required references to ask Prism to adhere the new extension in the Bootstrapper class)
    • The new NavigationContentLoader and the new RegionNavigationService.
    • The new Region Behavior.
  6. Created a DisposableView class to be used as a parent for any disposable view: This class holds a reference to the View’s container, and it’s of removing itself from the view collection as well as disposing the holding container.

 

How to Use:

The first thing to do is, you need to build your chart on how you want to keep/dispose your objects:

  • If you decide to keep a certain view a live, here is what you can do:
    • Register a type of your view using RegisterViewForNavigation method (It needs to be registered using the HierarchicalLifetimeManager if you want it to be disposed when the holding container is disposed).
    • Use the RequestNavigateUsingContainer (Defined at #1 a) to request navigate to this view.
  • If you want your view to get disposed every time you navigate back and forth (Disposable View), here is what you can do:
    • Make your view Inherits from DisposableView (Defined at #6)
    • Use the RequestNavigateToDisposableView(Defined at #1 b) to navigate to this view.

IMPORTANT: Please don’t use RegionManager.RegisterViewWithRegion nor RegionManager.RequestNavigate. The whole idea is to replace those methods and override their behavior, I could have re-implemented them with the new behavior, but I chose not to because maybe you think that the current implementation is sufficient for your situation.

Best Practice in case of  Independent Module:

If you have an independent module (Its life time is not based on other Modules, I would strongly recommend to create a Disposable Parent View for this module (Acts as a wrapper for all your defined views). The benefit of this approach is to avoid any memory leak and to guarantee the complete memory release of all the objects owned by this module once you navigate to another module, you also will free up the space allocated in the memory for this module.

Download

I have written a class library that extends PRISM with functionality discussed above along with a simple example. You can download it from this link(Prism.Extension.Example.zip). You need to remove .pdf extension to be able to unzip it.

References

  1. Prism 5.0 source code
  2. ViewModel-st-Child-Container-PRISM-Navigation

 

Thank you for reading my post. I tried to make it as clear as I could, but in case I failed, Please don’t hesitate to ask me.