简体   繁体   English

Dynamics CRM Online对象缓存无法正确缓存

[英]Dynamics CRM Online Object caching not caching correctly

I have a requirement where we need a plugin to retrieve a session id from an external system and cache it for a certain time. 我有一个要求,我们需要一个插件来从外部系统检索会话ID并将其缓存一段时间。 I use a field on the entity to test if the session is actually being cached. 我在实体上使用一个字段来测试会话是否实际被缓存。 When I refresh the CRM form a couple of times, from the output, it appears there are four versions (at any time consistently) of the same key. 当我刷新CRM表单几次时,从输出中可以看出,同一个键有四个版本(在任何时候一致)。 I have tried clearing the cache and testing again, but still the same results. 我已经尝试清除缓存并再次测试,但结果仍然相同。

Any help appreciated, thanks in advance. 任何帮助表示感谢,提前谢谢。

Output on each refresh of the page: 每次刷新页面时的输出:

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

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

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

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

20170511_125437: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_125358:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0 20170511_125358:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0

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

To accomplish 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");
    }
}

This has been greatly explained by Daryl here: https://stackoverflow.com/a/35643860/7708157 Daryl在这里对此进行了很大的解释: https//stackoverflow.com/a/35643860/7708157

Basically you are not having one MemoryCache instance per whole CRM system, your code simply proves that there are multiple app domains for every plugin, so even static variables stored in such plugin can have multiple values, which you cannot rely on. 基本上你不是每个CRM系统都有一个MemoryCache实例,你的代码只是证明每个插件都有多个app域,所以即使存储在这个插件中的静态变量也可以有多个值,你不能依赖它们。 There is no documentation on MSDN that would explain how the sanboxing works (especially app domains in this case), but certainly using static variables is not a good idea.Of course if you are dealing with online, you cannot be sure if there is only single front-end server or many of them (which will also result in such behaviour) 在MSDN上没有可以解释sanboxing如何工作的文档(特别是在这种情况下的app域),但肯定使用静态变量并不是一个好主意。当然,如果你在线处理,你不能确定是否只有单个前端服务器或其中许多服务器(这也会导致此类行为)

Class level variables should be limited to configuration information. 类级变量应限于配置信息。 Using a class level variable as you are doing is not supported. 不支持使用您正在执行的类级变量。 In CRM Online, because of multiple web front ends, a specific request may be executed on a different server by a different instance of the plugin class than another request. 在CRM Online中,由于多个Web前端,可以通过插件类的不同实例在不同服务器上执行特定请求而不是另一个请求。 Overall, assume CRM is stateless and that unless persisted and retrieved nothing should be assumed to be continuous between plugin executions. 总的来说,假设CRM是无状态的,除非持久化和检索,否则在插件执行之间不应该假设是连续的。

Per the SDK: 根据SDK:

The plug-in's Execute method should be written to be stateless because the constructor is not called for every invocation of the plug-in. 应该将插件的Execute方法编写为无状态,因为每次调用插件时都不会调用构造函数。 Also, multiple system threads could execute the plug-in at the same time. 此外,多个系统线程可以同时执行插件。 All per invocation state information is stored in the context, so you should not use global variables or attempt to store any data in member variables for use during the next plug-in invocation unless that data was obtained from the configuration parameter provided to the constructor. 所有每个调用状态信息都存储在上下文中,因此您不应使用全局变量或尝试将任何数据存储在成员变量中,以便在下一个插件调用期间使用,除非该数据是从提供给构造函数的配置参数中获取的。

Reference: https://msdn.microsoft.com/en-us/library/gg328263.aspx 参考: https//msdn.microsoft.com/en-us/library/gg328263.aspx

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM