2011-12-22 10 views
12

No puedo entender por qué el valor HasChanged de mi objeto SqlCacheDependency regresa originalmente de la ejecución del comando como falso, pero en algún lugar casi inmediatamente después de que vuelve de la base de datos, el valor cambia a verdadero.¿Por qué mi SqlCacheDependency HasChanged vuelve falso pero casi inmediatamente después de los cambios en verdadero?

A veces esto sucede incluso antes de que el elemento se inserte en la memoria caché, haciendo que la memoria caché lo descarte inmediatamente, a veces después de la inserción, y puedo tomar un enumerador que ve la clave en la memoria caché pero incluso antes de ese artículo en la memoria caché ha sido eliminado.

sproc:

ALTER PROCEDURE [dbo].[ntz_dal_ER_X_Note_SelectAllWER_ID] 
     @ER_ID int 
AS 
BEGIN 
    SELECT 
     ER_X_Note_ID, 
     ER_ID, 
     Note_ID 
    FROM dbo.ER_X_Note e 
    WHERE 
     ER_ID = @ER_ID 
END 

La base de datos es MS SQL Server 2008, Service Broker está habilitado, y algunos de salida hace caché y permanecerá en caché. Por ejemplo, éste funciona bien:

ALTER PROC [dbo].[ntz_dal_GetCacheControllerByEntityName] (
    @Name varchar(50) 
) AS 
BEGIN 
    SELECT 
     CacheController_ID, 
     EntityName, 
     CacheEnabled, 
     Expiration 
    From dbo.CacheController cc 
    WHERE EntityName = @Name 
END 

El código que llama al procedimiento almacenado en cuestión que falla:

DataSet toReturn; 
    Hashtable paramHash = new Hashtable(); 
    paramHash.Add("ER_ID", _eR_ID.IsNull ? null : _eR_ID.Value.ToString()); 
    string cacheName = BuildCacheString("ntz_dal_ER_X_Note_SelectAllWER_ID", paramHash); 
    toReturn = (DataSet)GetFromCache(cacheName); 
    if (toReturn == null) 
    { 

     // Set up parameters (1 input and 0 output) 
     SqlParameter[] arParms = { 
       new SqlParameter("@ER_ID", _eR_ID), 
      }; 
     SqlCacheDependency scd; 

     // Execute query. 
     toReturn = _dbTransaction != null 
      ? _dbConnection.ExecuteDataset(_dbTransaction, "dbo.[ntz_dal_ER_X_Note_SelectAllWER_ID]", out scd, arParms) 
      : _dbConnection.ExecuteDataset("dbo.[ntz_dal_ER_X_Note_SelectAllWER_ID]", out scd, arParms); 

     AddToCache(cacheName, toReturn, scd); 
    } 

    return toReturn; 

código que funciona

 const string sprocName = "ntz_dal_GetCacheControllerByEntityName"; 
     string cacheControlPrefix = "CacheController_" + CachePrefix; 
     CacheControl controller = (CacheControl)_cache[cacheControlPrefix]; 
     if (controller == null) 
     { 
      try 
      { 
       SqlParameter[] arParms = { 
              new SqlParameter("@Name", CachePrefix), 
             }; 
       SqlCacheDependency sqlCacheDependency; 

       // Execute query. 
       DataSet result = _dbTransaction != null 
            ? _dbConnection.ExecuteDataset(_dbTransaction, sprocName, out sqlCacheDependency, arParms) 
            : _dbConnection.ExecuteDataset(sprocName, out sqlCacheDependency, arParms); 

       controller = result.Tables[0].Rows.Count == 0 
           ? new CacheControl(false) 
           : new CacheControl(result.Tables[0].Rows[0]); 

       _cache.Insert(cacheControlPrefix, controller, sqlCacheDependency); 
      } 
      catch (Exception ex) 
      { 
       // if sproc retreival fails cache the result of false so we don't keep trying 
       // this is the only case where it can be added with no expiration date 
       controller = new CacheControl(false); 

       // direct cache insert, no dependency, no expiration, never try again for this entity 
       if (HttpContext.Current != null && UseCaching && _cache != null) _cache.Insert(cacheControlPrefix, controller); 
      } 
     } 
     return controller; 

está sobrecargado El método AddToCache y tiene más pruebas en él; El _cache.Insert directo en el método de trabajo es eludir esas otras pruebas. El código de trabajo ayuda a determinar si el almacenamiento en ca ocurre en absoluto.

Se puede ver que cuando los datos "no trabajo" se recupera en un principio, todo está bien:

enter image description here

Pero en algún lugar al azar allá de ese punto, en este caso, simplemente entrar en el siguiente método

enter image description here

Y sin embargo, los datos no cambia en absoluto; Soy el único que toca esta instancia de la base de datos.

Respuesta

6

Fue realmente, muy simple, tan simple que lo pasé por alto completamente.

En este artículo Creating a Query for Notification, que DID socavación varias veces, se dice claramente:

Valores de la opción SET

Cuando se ejecuta una instrucción SELECT debajo de una solicitud de notificación, la conexión que envía la solicitud debe tener las opciones para la conexión establecida de la siguiente manera:

ANSI_NULLS ON 
ANSI_PADDING ON 
ANSI_WARNINGS ON 
CONCAT_NULL_YIELDS_NULL ON 
QUOTED_IDENTIFIER ON 
NUMERIC_ROUNDABORT OFF 
ARITHABORT ON 

Bueno, leí y volví a leer y volver a leer el sproc, y todavía no veía que ANSI_NULLS y QUOTED_IDENTIFIER estaban "OFF", no ON.

Mi conjunto de datos ahora está almacenando en caché y conservando los datos correctamente sin falsos indicadores de cambio.

1

Tengo la corazonada de que el problema es con su _eR_ID. Creo que debería intentar agregar una variable local al procedimiento de falla que usa un valor imposible para _eR_ID, como -1. Nunca confío en lo que sucederá cuando los nulos estén involucrados y creo que esta podría ser la fuente de su problema.

Aquí es la versión modificada que recomiendo probar:

DataSet toReturn; 
Hashtable paramHash = new Hashtable(); 

int local_er_ID = eR_ID.IsNull ? -1 : _eR_ID.Value; 
paramHash.Add("ER_ID", local_eR_ID.ToString()); 

string cacheName = BuildCacheString("ntz_dal_ER_X_Note_SelectAllWER_ID", paramHash); 
toReturn = (DataSet)GetFromCache(cacheName); 
if (toReturn == null) 
{ 

    // Set up parameters (1 input and 0 output) 
    SqlParameter[] arParms = { 
      new SqlParameter("@ER_ID", local_eR_ID), 
     }; 
    SqlCacheDependency scd; 

    // Execute query. 
    toReturn = _dbTransaction != null 
     ? _dbConnection.ExecuteDataset(_dbTransaction, "dbo.[ntz_dal_ER_X_Note_SelectAllWER_ID]", out scd, arParms) 
     : _dbConnection.ExecuteDataset("dbo.[ntz_dal_ER_X_Note_SelectAllWER_ID]", out scd, arParms); 

    AddToCache(cacheName, toReturn, scd); 
} 

return toReturn; 

Importante

Al crear el código anterior, creo que he descubierto el origen del problema: cuando se ajusta el parámetro de procedimiento almacenado , está usando _eR_ID pero cuando configura paramHash está usando _eR_ID.Value.

La reescritura de código resolverá este problema, pero sospecho que esta es la raíz del problema.

+0

Lo intentaré, pero creo que es poco probable, porque el param hash solo se usa para construir el nombre de la clave bajo la cual se crea el elemento de caché. –

+1

@TheEvilGreebo: Lo siento, no creo que me haya comunicado lo suficientemente bien. Creo que el problema es esta línea de código 'new SqlParameter (" @ ER_ID ", _eR_ID)'. Esto pasa todo el objeto _eR_ID al parámetro y no estoy seguro exactamente cómo lo traduce a un valor para Sql (sospecho que llama a ToString() en el objeto). Anteriormente utilicé _eR_ID.Value, por lo que creo que el cambio mínimo que necesita es 'new SqlParameter (" @ ER_ID ", _eR_ID.Value)'. –

+0

Oh, ya veo lo que quieres decir. Bueno, _eR_ID es un SqlInt32, así que uno pensaría que inicializar un nuevo SqlParm usando eso no causaría problemas, ¡pero vale la pena intentarlo! –

0

Al encontrarme con el mismo problema y encontrar las mismas respuestas en línea sin ninguna ayuda, estaba buscando la respuesta de suscripción inválida xml del generador de perfiles.

Encontré un ejemplo en el sitio de soporte de msdn que tenía un orden de código ligeramente diferente. Cuando lo probé, me di cuenta del problema: no abra su objeto de conexión hasta después de haber creado el objeto de comando y el objeto de dependencia de caché. Aquí es el orden que debe seguir y todo estará bien:

  1. Asegúrese de activar las notificaciones (SqlCahceDependencyAdmin) y ejecutar SqlDependency.Start primera
  2. Crear el objeto de conexión
  3. Crear el objeto de comando y asignar comandos texto, tipo y objeto de conexión (cualquier combinación de constructores, propiedades de configuración o uso de CreateCommand).
  4. Crear la caché de SQL dependencia de los objetos
  5. Abrir objeto la conexión
  6. Ejecutar la consulta
  7. Añadir artículo a almacenar en caché utilizando la dependencia.

Si sigue este orden y sigue todos los demás requisitos en su declaración seleccionada, no tiene ningún problema de permisos, ¡esto funcionará!

Creo que el problema tiene que ver con cómo .NET Framework administra la conexión, específicamente qué configuraciones se establecen. Intenté anular esto en mi prueba de comando sql pero nunca funcionó. Esto es solo una suposición, lo que sí sé es que cambiar el orden solucionó el problema de inmediato.

Pude armarlo de las siguientes publicaciones en msdn.

Esta publicación fue una de las causas más comunes de la suscripción no válida, y muestra cómo el cliente .Net establece las propiedades que están en contraste con lo que requiere la notificación.

https://social.msdn.microsoft.com/Forums/en-US/cf3853f3-0ea1-41b9-987e-9922e5766066/changing-default-set-options-forced-by-net?forum=adodotnetdataproviders

Entonces este post era de un usuario que, como yo, había reducido su código al formato más simple. Mi patrón de código original era similar al suyo.

https://social.technet.microsoft.com/Forums/windows/en-US/5a29d49b-8c2c-4fe8-b8de-d632a3f60f68/subscriptions-always-invalid-usual-suspects-checked-no-joy?forum=sqlservicebroker

Entonces me encontré con este post, también es un muy simple reducción del problema, sólo el suyo era un tema sencillo - necesitando nombre de 2 partes para las tablas. En su caso, la sugerencia resolvió el problema. Después de mirar su código noté que la principal diferencia estaba esperando abrir el objeto de conexión hasta DESPUÉS de que se crearan el objeto de comando Y el objeto de dependencia. Mi única suposición es bajo el capó (aún no he iniciado el reflector para verificarlo, solo una suposición), el objeto Connection se abre de manera diferente, o el orden de los eventos y el comando ocurren de manera diferente, debido a esta asociación.

https://social.msdn.microsoft.com/Forums/sqlserver/en-US/bc9ca094-a989-4403-82c6-7f608ed462ce/sql-server-not-creating-subscription-for-simple-select-query-when-using-sqlcachedependency?forum=sqlservicebroker

espero que esto ayude a alguien más en un problema similar.

+0

No creo que esto sea correcto. El orden es importante, pero afaik cuando abres la conexión no lo es. –

Cuestiones relacionadas