Search code examples
c#postsharpaop

Postsharp MethodInterceptionAspect attribute throws EntrypointNotFoundException


I have recently updated PostSharp to v4.1.13 and I have started receiving this error when I try to build my solution:

The custom attribute 'True.Kentico.Caching.KenticoCacheAttribute' constructor threw the exception EntryPointNotFoundException: Entry point was not found.

This attribute implements caching, and has worked before, so I'm wondering what would be the cause of it breaking. I am using this attribute in other assemblies referencing the aspect's containing assembly, and I get that error in there as well.

The following is the code for the attribute. Apologies, it contains a number of constructors.

[Serializable]
public class KenticoCacheAttribute : MethodInterceptionAspect
{
    public int CacheMinutes { get; set; }

    /// <summary>
    /// the string value of the cache dependency key. If it uses parameters from the method, include a {0} to format with the method parameter 
    /// </summary>
    public string CacheDependency { get; set; }
    public string[] CacheDependencyStrings { get; set; }

    public KenticoCacheDependencyObtainFrom ObtainCacheDependencyFrom { get; set; }

    /// <summary>
    /// whether caching is enabled - default from app settings
    /// </summary>
    public bool CacheEnabled { get; set; }

    /// <summary>
    /// this is the index of the parameter that will be used to format the cachedependency, if required 
    /// </summary>
    public int CacheDependencyParameterIndex { get; set; }


    public string CacheDependencyObjectProperty { get; set; }

    private string _methodName;

    /// <summary>
    /// initializes the cache attribute to use a static dependency
    /// </summary>
    /// <param name="cacheDependency"></param>
    public KenticoCacheAttribute(string cacheDependency)
    {
        CacheMinutes = EngineContext.Current.Resolve<AppSettings>().Cache.TTL.GlobalSetting;
        CacheEnabled = !EngineContext.Current.Resolve<AppSettings>().Cache.IgnoreCache;
        CacheDependencyParameterIndex = -1;
        CacheDependency = cacheDependency;
        if (String.IsNullOrEmpty(cacheDependency))
            ObtainCacheDependencyFrom = KenticoCacheDependencyObtainFrom.NoDependency;
        else
        {

            ObtainCacheDependencyFrom = KenticoCacheDependencyObtainFrom.Static;
        }
    }

    /// <summary>
    /// initializes the cache attribute to use a static dependency using multiple depndencies
    /// </summary>
    /// <param name="cacheDependencystrings">an array of strings</param>
    public KenticoCacheAttribute(string[] cacheDependencystrings)
    {
        CacheMinutes = EngineContext.Current.Resolve<AppSettings>().Cache.TTL.GlobalSetting;
        CacheEnabled = !EngineContext.Current.Resolve<AppSettings>().Cache.IgnoreCache;
        CacheDependencyParameterIndex = -1;
        CacheDependencyStrings = cacheDependencystrings;
        ObtainCacheDependencyFrom = KenticoCacheDependencyObtainFrom.MultipleDependenciesStatic;
    }

    /// <summary>
    /// initializes the cache attribute to take the dependency from the input parameter 
    /// </summary>
    /// <param name="cacheDependency">the static string to be used for the cache dependency. you may include {CurrentSiteName} to be replaced with the current site name, and {0} to be replaced with the value passed in as one of the method parameters</param>
    /// <param name="cacheDependencyParameterIndex">the index of the parameter in the method parameters that will be used to create the cache dependency key</param>
    public KenticoCacheAttribute(string cacheDependency, int cacheDependencyParameterIndex)
    {
        CacheMinutes = EngineContext.Current.Resolve<AppSettings>().Cache.TTL.GlobalSetting;
        CacheEnabled = !EngineContext.Current.Resolve<AppSettings>().Cache.IgnoreCache;
        CacheDependencyParameterIndex = cacheDependencyParameterIndex;
        CacheDependency = cacheDependency;
        if (String.IsNullOrEmpty(cacheDependency))
        {

            ObtainCacheDependencyFrom = KenticoCacheDependencyObtainFrom.MultipleDependenciesFromParameter;
        }
        else
        {

            ObtainCacheDependencyFrom = KenticoCacheDependencyObtainFrom.FromMethodParameter;
        }
    }

    /// <summary>
    /// initialize the cache attribute to obtain the cache dependency from the named property of the return object 
    /// </summary>
    /// <param name="cacheDependency">the static string to be used for the cache dependency. you may include {CurrentSiteName} to be replaced with the current site name, and {0} to be replaced with the value in the named parameter </param>
    /// <param name="cacheDependencyObjectPropertyName">the name of the property of the return object that will be used to replace the placeholder in the static string to build the cache dependency string</param>
    public KenticoCacheAttribute(string cacheDependency, string cacheDependencyObjectPropertyName)
    {
        CacheMinutes = EngineContext.Current.Resolve<AppSettings>().Cache.TTL.GlobalSetting;
        CacheEnabled = !EngineContext.Current.Resolve<AppSettings>().Cache.IgnoreCache;
        CacheDependencyParameterIndex = -1;
        CacheDependency = cacheDependency;
        ObtainCacheDependencyFrom = KenticoCacheDependencyObtainFrom.FromReturnObject;
        CacheDependencyObjectProperty = cacheDependencyObjectPropertyName;
    }

    /// <summary>
    /// initialize the cache attribute to obtain the cache dependency from the named property of the return object 
    /// </summary>
    /// <param name="cacheDependency">the static string to be used for the cache dependency. you may include {CurrentSiteName} to be replaced with the current site name, and {0} to be replaced with the value in the named parameter </param>
    /// <param name="cacheDependencyParameterIndex"></param>
    /// <param name="cacheDependencyObjectPropertyName">the name of the property of the return object that will be used to replace the placeholder in the static string to build the cache dependency string</param>
    public KenticoCacheAttribute(string cacheDependency, int cacheDependencyParameterIndex, string cacheDependencyObjectPropertyName)
    {
        CacheMinutes = EngineContext.Current.Resolve<AppSettings>().Cache.TTL.GlobalSetting;
        CacheEnabled = !EngineContext.Current.Resolve<AppSettings>().Cache.IgnoreCache;
        CacheDependencyParameterIndex = cacheDependencyParameterIndex;
        CacheDependency = cacheDependency;
        ObtainCacheDependencyFrom = KenticoCacheDependencyObtainFrom.FromMethodParameterObjectProperty;
        CacheDependencyObjectProperty = cacheDependencyObjectPropertyName;
    }

    public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
    {
        _methodName = method.Name;
    }

    public override void OnInvoke(MethodInterceptionArgs args)
    {
        var methodInfo = args.Method as MethodInfo;
        if (methodInfo != null && (methodInfo.ReturnType != typeof(void) && CacheEnabled))
        {
            var cacheKey = BuildCacheKey(args.Arguments);

            var cacheSettings = new CacheSettings(CacheMinutes, cacheKey);
            var data = CacheHelper.Cache(cs => GetData(cs, args), cacheSettings);
            args.ReturnValue = data;
        }
        else
            base.OnInvoke(args);
    }

    private object GetData(CacheSettings cs, MethodInterceptionArgs args)
    {
        var data = args.Invoke(args.Arguments);

        // Checks whether data was loaded and whether the data should be cached (based on the CacheSettings)
        if ((data != null) && cs.Cached)
        {
            // Sets a cache dependency for the data
            // The data is removed from the cache if the objects represented by the dummy key are modified (all user objects in this case)
            var dependencyResolver = CacheDependencyFactory.GetDependecyFormatter(ObtainCacheDependencyFrom);

            var dependencyString = dependencyResolver.Format(new CacheDependencyFormatParameters()
            {
                CacheDependencyBase = CacheDependency,
                CacheDependencybaseString = CacheDependencyStrings,
                InputParameterIndex = CacheDependencyParameterIndex,
                ReturnParameterName = CacheDependencyObjectProperty,
                InputParameterData = args,
                ReturnParameterData = data
            });

            cs.CacheDependency = CacheHelper.GetCacheDependency(dependencyString);
        }



        return data;
    }

    private string BuildCacheKey(Arguments arguments)
    {
        var sb = new StringBuilder();
        sb.Append(_methodName);
        foreach (var argument in arguments.ToArray())
        {
            sb.Append(argument == null ? "_" : argument.ToString());
        }
        sb.Append(String.Format("{0}_{1}", SiteContext.CurrentSiteName,
            SiteContext.CurrentSite.DefaultVisitorCulture));
        return sb.ToString();
    }
}

Any help would be appreciated!


Solution

  • So it turns out that the issue is with these lines of code in the constructors:

    CacheMinutes = EngineContext.Current.Resolve<AppSettings>().Cache.TTL.GlobalSetting;
    CacheEnabled = !EngineContext.Current.Resolve<AppSettings>().Cache.IgnoreCache;
    

    When PostSharp runs, The dependencyInjection framework has not yet been initialised, so the execution fails. It's strange that it was working before, but hey. I moved this code into the OnInvoke method, which should only run when the DI Enginecontext has been initialised :

    public override void OnInvoke(MethodInterceptionArgs args)
        {
    
            CacheMinutes = EngineContext.Current.Resolve<AppSettings>().Cache.TTL.GlobalSetting;
            CacheEnabled = !EngineContext.Current.Resolve<AppSettings>().Cache.IgnoreCache;
            var methodInfo = args.Method as MethodInfo;
    ......