9

我为 .NET MemoryCache 编写了自己的自定义更改监视器类。它似乎初始化得很好,但是当我尝试将它添加到缓存时,它会抛出一个InvalidOperation异常 -The method has already been invoked, and can only be invoked once.

我的变更监视器类:

internal class MyChangeMonitor : ChangeMonitor
{
    private Timer _timer;
    private readonly string _uniqueId;
    private readonly TypeAsOf _typeAsOf;
    private readonly string _tableName;

    public GprsChangeMonitor(TypeAsOf typeAsOf, string tableName)
    {
        bool initComplete = false;
        try
        {
            _typeAsOf = typeAsOf;
            _tableName = tableName;

            _uniqueId = Guid.NewGuid().ToString();
            TimeSpan ts = new TimeSpan(0, 0, 5, 0, 0);
            _timer = new Timer {Interval = ts.TotalMilliseconds};
            _timer.Elapsed += CheckForChanges;
            _timer.Enabled = true;
            _timer.Start();
            initComplete = true;
        }
        finally 
        {
            base.InitializationComplete();
            if(!initComplete)
                Dispose(true);
        }
    }

    void CheckForChanges(object sender, System.Timers.ElapsedEventArgs e)
    {
        //check for changes, if different
        base.OnChanged(_typeAsOf);
    }
 }

我用来创建缓存策略并将键/值对添加到缓存中的代码:

CacheItemPolicy policy = new CacheItemPolicy
{
    UpdateCallback = OnCacheEntryUpdateCallback
};

policy.AbsoluteExpiration = SystemTime.Today.AddHours(24);
//monitor the for changes
string tableName = QuickRefreshItems[type];
MyChangeMonitor cm = new MyChangeMonitor(typeAsOf, tableName);
policy.ChangeMonitors.Add(cm);
cm.NotifyOnChanged(OnRefreshQuickLoadCacheItems);

MyCache.Set(cacheKey, value, policy);

Set调用抛出了无效的操作异常,这很奇怪,因为根据 MSDN 文档,它只抛出ArgumentNullArgumentArgumentOutOfRangeNotSupported异常。

我确信我一定犯了一个简单的错误。但是很难找到关于编写自己的自定义更改监视器的好的文档或示例。任何帮助,将不胜感激。

4

3 回答 3

6

我知道评论有答案,但我希望它更明显......

使用 a 时ChangeMonitor,如果缓存条目不存在,它将立即触发。
MSDN文档是这样说明的

由于以下任何原因,受监控的条目被视为已更改:

A)在调用CreateCacheEntryChangeMonitor 方法时该键不存在。在这种情况下,生成的 CacheEntryChangeMonitor 实例会立即设置为更改状态。这意味着当代码随后绑定更改通知回调时,会立即触发回调

B) 关联的缓存条目已从缓存中删除。如果条目被显式删除、过期或被逐出以恢复内存,则可能会发生这种情况

于 2012-08-24T16:21:13.820 回答
3

我有完全相同的错误:

    Source: System.Runtime.Caching
    Exception type: System.InvalidOperationException
    Message: The method has already been invoked, and can only be invoked once.
    Stacktrace:    at System.Runtime.Caching.ChangeMonitor.NotifyOnChanged(OnChangedCallback onChangedCallback)
                   at System.Runtime.Caching.MemoryCacheEntry.CallNotifyOnChanged()
                   at System.Runtime.Caching.MemoryCacheStore.AddToCache(MemoryCacheEntry entry)
                   at System.Runtime.Caching.MemoryCacheStore.Set(MemoryCacheKey key, MemoryCacheEntry entry)
                   at System.Runtime.Caching.MemoryCache.Set(String key, Object value, CacheItemPolicy policy, String regionName)

我已经搜索了几个小时......直到逻辑之光击中我:

我正在使用一个被重用的静态策略对象..(如果它们相等,我的一些无意识过程会重用所有对象,也许我害怕构造消耗内存中一些字节的对象)

通过为缓存中的每个项目创建一个新的策略对象,错误就消失了。如果你仔细想想,这很合乎逻辑。

于 2017-01-18T09:15:38.690 回答
2

由于我刚刚遇到同样的问题并进行了自己的调查,因此发布了一个较晚的答案。

当您使用缓存项目策略注册更改监视器时, policy.ChangeMonitors.Add(cm)实现CacheItemPolicy会通过ChangeMonitor.NotifyOnChanged. 你不应该调用cm.NotifyOnChanged来注册另一个回调,否则它会The method has already been invoked, and can only be invoked once在那个时候抛出。

相反,使用CacheItemPolicy.UpdateCallbackCacheItemPolicy.RemovedCallback更新/删除缓存项,例如,如本博文中所述。

于 2017-07-25T20:49:19.573 回答