2011-04-01 21 views
8

He escrito mi propia clase de monitor de cambios para .Net MemoryCache. Parece que se inicializa bien, pero cuando intento agregarlo al Caché, arroja una excepción InvalidOperation "El método ya se ha invocado y solo se puede invocar una vez".Custom ChangeMonitor para .Net MemoryCache causa una excepción de operación no válida

Mi clase de cambio de monitor

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); 
    } 
} 

El código que utilizo para crear la directiva de caché y añadir el par clave/valor a la memoria caché

 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); 

establecimiento de la llamada inicia la excepción de operación no válido cuales es extraño, porque según la documentación de MSDN, solo arroja ArgumentNull, Argument, ArgumentOutOfRange y Excepciones no admitidas.

Estoy seguro de que debo estar cometiendo un simple error. Pero es difícil encontrar buena documentación o ejemplos sobre cómo escribir su propio monitor de cambios personalizado. Cualquier ayuda se agradece

+0

¿Puede adjuntar un depurador, desactivar Just My Code y luego interrumpir excepciones para ver qué es la pila de llamadas para InvalidOperationException? –

+0

El seguimiento de la pila no es muy útil. Está en System.Runtime.Caching.ChangeMonitor.NotifyOnChanged (OnChangedCallback onChangedCallback), pero no estaba en mi devolución de llamada para NotifyOnChanged, porque no se está llamando. – Keith

+7

Aparentemente, tengo que agregar el monitor de cambios a la política DESPUÉS de agregar el elemento a la memoria caché. Si lo agrego antes, entonces obtengo la excepción. – Keith

Respuesta

6

sé que los comentarios tienen la respuesta, pero yo quería que fuera más evidente ...

Cuando se utiliza un ChangeMonitor, que se disparará inmediatamente si la entrada de caché no lo hace existe.
MSDN documentation states it this way:

A monitored entry is considered to have changed for any of the following reasons:

A) The key does not exist at the time of the call to the CreateCacheEntryChangeMonitor method. In that case, the resulting CacheEntryChangeMonitor instance is immediately set to a changed state. This means that when code subsequently binds a change-notification callback, the callback is triggered immediately.

B) The associated cache entry was removed from the cache. This can occur if the entry is explicitly removed, if it expires, or if it is evicted to recover memory

1

que he tenido exactamente el mismo error:

:

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) 

He buscado durante horas .. hasta que la luz de la lógica me llamó la atención Estaba usando un objeto de política estática que se reutilizó ... (un proceso inconsciente en mí reutiliza todos los objetos si son iguales, tal vez tengo miedo de construir objetos que consuman algunos bytes en la memoria)

Al crear un nuevo objeto de política para cada elemento en la memoria caché, el error desapareció. Bastante lógico si lo piensas.

1

Publicando una respuesta tardía ya que acabo de enfrentar el mismo problema y realicé mi propia investigación.

Al registrar su monitor de cambio con una política elemento de la caché - policy.ChangeMonitors.Add(cm) - la puesta en práctica CacheItemPolicy registra su propio cambio de devolución de llamada en él a través de ChangeMonitor.NotifyOnChanged. Se supone que no debes llamar al cm.NotifyOnChanged para registrar otra devolución de llamada, o arrojará The method has already been invoked, and can only be invoked once en ese punto.

En su lugar, use CacheItemPolicy.UpdateCallback o CacheItemPolicy.RemovedCallback para actualizar/eliminar el elemento de caché, p. como se describe en this blog post.

Cuestiones relacionadas