Dynamics CRM Online Object Caching Not Caching Correctly

I have a requirement where we need a plugin to get the session id from an external system and cache it for a specific amount of time. I am using an object field to check if the session is actually being cached. When I update the CRM form a couple of times, four versions (at any time in sequence) of the same key appear from the output. I tried clearing the cache and checking again, but still the same results.

Any help is appreciated, thanks in advance.

Output on every page refresh:

20170511_125342: 1: 55a4f7e6-a1d7-e611-8100-c4346bc582c0

20170511_125358: 1: 55a4f7e6-a1d7-e611-8100-c4346bc582c0

20170511_125410: 1: 55a4f7e6-a1d7-e611-8100-c4346bc582c0

20170511_125342: 1: 55a4f7e6-a1d7-e611-8100-c4346bc582c0

20170511_125437: 1: 55a4f7e6-a1d7-e611-8100-c4346bc582c0

20170511_125358: 1: 55a4f7e6-a1d7-e611-8100-c4346bc582c0

20170511_125358: 1: 55a4f7e6-a1d7-e611-8100-c4346bc582c0

20170511_125437: 1: 55a4f7e6-a1d7-e611-8100-c4346bc582c0

For this I have implemented the following code:

public class SessionPlugin : IPlugin
{
    public static readonly ObjectCache Cache = MemoryCache.Default;
    private static readonly string _sessionField = "new_sessionid";
    #endregion

    public void Execute(IServiceProvider serviceProvider)
    {
        var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

        try
        {
            if (context.MessageName.ToLower() != "retrieve" && context.Stage != 40)
                return;

            var userId = context.InitiatingUserId.ToString();

            // Use the userid as key for the cache
            var sessionId = CacheSessionId(userId, GetSessionId(userId));
            sessionId = $"{sessionId}:{Cache.Select(kvp => kvp.Key == userId).ToList().Count}:{userId}";

            // Assign session id to entity
            var entity = (Entity)context.OutputParameters["BusinessEntity"];

            if (entity.Contains(_sessionField))
                entity[_sessionField] = sessionId;
            else
                entity.Attributes.Add(new KeyValuePair<string, object>(_sessionField, sessionId));
        }
        catch (Exception e)
        {
            throw new InvalidPluginExecutionException(e.Message);
        }
    }

    private string CacheSessionId(string key, string sessionId)
    {
        // If value is in cache, return it
        if (Cache.Contains(key))
            return Cache.Get(key).ToString();

        var cacheItemPolicy = new CacheItemPolicy()
        {
            AbsoluteExpiration = ObjectCache.InfiniteAbsoluteExpiration,
            Priority = CacheItemPriority.Default
        };

        Cache.Add(key, sessionId, cacheItemPolicy);

        return sessionId;
    }

    private string GetSessionId(string user)
    {
        // this will be replaced with the actual call to the external service for the session id
        return DateTime.Now.ToString("yyyyMMdd_hhmmss");
    }
}

      

+3


source to share


2 answers


Daryl explained this in detail: fooobar.com/questions/2409872 / ...



Basically, you don't have one instance of MemoryCache for the entire CRM system, your code just proves that there are multiple application domains for each plugin, so even static variables stored in such a plugin might have multiple values ​​that you cannot rely on. ... There is no documentation on MSDN that explains how sanboxing works (especially in the application domain), but of course using static variables is not a good idea. Of course, if you are dealing online, you cannot be sure that there is only one external server or many of them (which will also lead to this behavior)

+3


source


Class level variables should be limited to configuration information. Using a class level variable as you do is not supported. In CRM Online, due to multiple ends of the web interface, a particular request may be executed on a different server by a different instance of the plugin class than another request. In general, let's assume that CRM is stateless, and that if it's not saved and restored, nothing should be considered continuous between plugins execution.

In the SDK:



The plugin's Execute method must be written as stateless because the constructor is not called for every plugin call. In addition, multiple threads on the system can execute the plug-in at the same time. All call state information is stored in the context, so you should not use global variables or try to store any data in member variables for use during the next call to the plug-in if the data was just retrieved from a configuration parameter supplied to the constructor.

Link: https://msdn.microsoft.com/en-us/library/gg328263.aspx

0


source







All Articles