Soporte de almacenamiento en memoria caché para servicios web HTTP de WCF

.NET Framework versión 4 le permite usar el mecanismo de almacenamiento en memoria caché declarativo que ya está disponible en ASP.NET en los servicios Web HTTP de WCF. Esto le permite almacenar en memoria caché las respuestas de las operaciones de servicio Web HTTP de WCF. Cuando un usuario envía un protocolo HTTP GET al servicio que está configurado para almacenarlo en memoria caché, ASP.NET devuelve la respuesta almacenada en memoria caché y no se llama al método de servicio. Cuando la memoria caché expira, la próxima vez que un usuario envía un protocolo HTTP GET, se llama al método de servicio y la respuesta se vuelve a almacenar en memoria caché. Para obtener más información sobre el almacenamiento en memoria caché ASP.NET, vea Información general sobre almacenamiento en caché ASP.NET

Almacenamiento en memoria caché básico del servicio Web HTTP

Para habilitar el almacenamiento en memoria caché del servicio WEB HTTP, debe habilitar primero la compatibilidad de ASP.NET aplicando AspNetCompatibilityRequirementsAttribute al servicio, definiendo RequirementsMode como Allowed o Required.

.NET Framework 4 introduce un nuevo atributo llamado AspNetCacheProfileAttribute que le permite especificar un nombre de perfil de memoria caché. Este atributo se aplica a una operación del servicio. El siguiente ejemplo aplica AspNetCompatibilityRequirementsAttribute a un servicio para habilitar la compatibilidad de ASP.NET y configura la operación GetCustomer para almacenar en memoria caché. El atributo AspNetCacheProfileAttribute especifica un perfil de memoria caché que contiene la configuración de memoria caché que se va a utilizar.

[ServiceContract] AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)]
    public class Service
    { 
        [WebGet(UriTemplate = "{id}")]
        [AspNetCacheProfile("CacheFor60Seconds")]
        public Customer GetCustomer(string id)
        {
             // ...
        }
}

También debe activar el modo de compatibilidad de ASP.NET en el archivo Web.config, tal y como se muestra en el siguiente ejemplo.

<system.serviceModel>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />    
</system.serviceModel>
Ee230443.Warning(es-es,VS.100).gif Precaución:
Si no se activa el modo de compatibilidad de ASP.NET y se utiliza AspNetCacheProfileAttribute, se produce una excepción.

El nombre de perfil de memoria caché especificado por AspNetCacheProfileAttribute identifica un perfil de memoria caché agregado al archivo de configuración Web.config. El perfil de memoria caché se define en un elemento <outputCacheSetting>, tal y como se muestra en el siguiente ejemplo de configuración.

<!-- ...  -->
<system.web>
   <caching>
      <outputCacheSettings>
         <outputCacheProfiles>
            <add name="CacheFor60Seconds" duration="60" varyByParam="none" sqlDependency="MyTestDatabase:MyTable"/>
         </outputCacheProfiles>
      </outputCacheSettings>
   </caching>
   <!-- ... -->
</system.web>

Éste es el mismo elemento de configuración que está disponible para las aplicaciones ASP.NET. Para obtener más información sobre los perfiles de memoria caché de ASP.NET, vea OutputCacheProfile. Para los servicios Web HTTP, los atributos más importantes del perfil de la memoria caché son: cacheDuration y varyByParam. Se requieren ambos atributos. cacheDuration establece la cantidad de tiempo (en segundos) durante el que una respuesta debería estar almacenada en la memoria caché. varyByParam le permite especificar un parámetro de cadena de consulta que se utiliza para almacenar respuestas en memoria caché. Todas las solicitudes realizadas con valores de parámetro de cadena de consulta diferentes se almacenan en memoria caché por separado. Por ejemplo, una vez que se realiza una solicitud inicial a http://MyServer/MyHttpService/MyOperation?param=10, a todas las solicitudes subsiguientes realizadas con el mismo URI se les devolverá la respuesta almacenada en memoria caché (siempre y cuando la duración del almacenamiento en memoria caché no haya transcurrido). Las respuestas para una solicitud similar que es igual pero tiene un valor diferente para el parámetro de cadena de consulta de parámetros se almacenan en la memoria caché por separado. Si no desea este comportamiento de almacenamiento en caché por separado, establezca varyByParam en "none".

Dependencia de memoria caché de SQL

Las respuestas del servicio Web HTTP también pueden estar almacenadas en memoria caché con una dependencia de memoria caché de SQL. Si el servicio Web HTTP de WCF depende de los datos almacenados en una base de datos SQL, puede que desee almacenar en memoria caché la respuesta del servicio e invalidar la respuesta almacenada en memoria caché cuando se produzcan cambios en los datos de tabla de base de datos SQL. Este comportamiento se configura completamente en el archivo Web.config. Debe definir primero una cadena de conexión en el elemento <connectionStrings>.

<connectionStrings>
    <add name="connectString"
        connectionString="Data Source=MyService;Initial Catalog=MyTestDatabase;Integrated Security=True"
        providerName="System.Data.SqlClient" />
  </connectionStrings>

A continuación, debe habilitar la dependencia de memoria caché de SQL dentro de un elemento <caching> en el elemento <system.web>, tal y como se muestra en el siguiente ejemplo de configuración.

<system.web>
   <caching>
      <sqlCacheDependency enabled="true" pollTime="1000" >
         <databases>
            <add name="MyTestDatabase" connectionStringName="connectString" />
         </databases>
      </sqlCacheDependency>
      <!-- ... -->
   </caching>
   <!-- ... -->
</system.web>

Aquí, la dependencia de memoria caché de SQL está habilitada y se establece un tiempo de sondeo de 1.000 milisegundos. Cada vez que el tiempo de sondeo transcurre, se comprueba la tabla de la base de datos para detectar actualizaciones. Si se detectan cambios, se quita el contenido de la memoria caché y la próxima vez que se invoque la operación del servicio, se almacenará en memoria caché una nueva respuesta. Dentro del elemento <sqlCacheDependency>, agregue las bases de datos y las cadenas de conexión dentro del elemento <databases>, tal y como se muestra en el siguiente ejemplo.

<system.web>
   <caching>
      <sqlCacheDependency enabled="true" pollTime="1000" >
         <databases>
            <add name="MyTestDatabase" connectionStringName="connectString" />
         </databases>
      </sqlCacheDependency>
      <!-- ... -->
   </caching>
   <!-- ... -->
</system.web>

Luego, configure la configuración de la memoria caché de resultados dentro del elemento <caching>, tal y como se muestra en el siguiente ejemplo.

<system.web>
<caching>
      <!-- ...  -->
<outputCacheSettings>
<outputCacheProfiles>
<add name="CacheFor60Seconds" duration="60" varyByParam="none" sqlDependency="MyTestDatabase:MyTable"/>
</outputCacheProfiles>
</outputCacheSettings>
</caching>
<!-- ... -->
</system.web>

Aquí, la duración del almacenamiento en memoria caché está establecida en 60 segundos, varyByParam no está establecido en ninguno y sqlDependency está establecido en una lista delimitada por punto y coma con pares de nombre de base de datos/tabla separados por dos puntos. Cuando los datos de MyTable se cambian, se elimina la respuesta almacenada en memoria caché para la operación del servicio y, cuando se invoca la operación, la nueva respuesta se genera (llamando a la operación del servicio), se almacena en memoria caché y se devuelve al cliente.

Ee230443.Important(es-es,VS.100).gif Nota:
Para que ASP.NET pueda acceder a una base de datos SQL, debe utilizar la Herramienta de registro de SQL Server de ASP.NET. Además, debe permitir el acceso de la cuenta de usuario correspondiente tanto a la base de datos como a la tabla. Para obtener más información, vea Acceso a SQL Server desde una aplicación web.

Almacenamiento en memoria caché basado en HTTP GET condicional

En casos en que se usa web HTTP, los servicios suelen usar un protocolo HTTP GET condicional para implementar el almacenamiento en memoria caché del HTTP inteligente descrito en la especificación de HTTP. Para ello, el servicio debe establecer el valor del encabezado ETag en la respuesta HTTP. También debe comprobar el encabezado If-None-Match en la solicitud HTTP para ver si alguno de los ETag especificados coincide con el ETag actual.

Para las solicitudes GET y HEAD, CheckConditionalRetrieve toma un valor de ETag y lo comprueba con respecto al encabezado If-None-Match de la solicitud. Si el encabezado está presente y hay una coincidencia, se produce WebFaultException con un código de estado HTTP 304 (No modificado) y se añade un encabezado de ETag a la respuesta con el ETag coincidente.

Una sobrecarga del método CheckConditionalRetrieve toma la última fecha modificada y la compara con el encabezado If-Modified-Since de la solicitud. Si el encabezado está presente y el recurso no se ha modificado desde entonces, se produce WebFaultException con un código de estado HTTP 304 (No modificado).

Para las solicitudes PUT, POST y DELETE, CheckConditionalUpdate toma el valor de ETag actual de un recurso. Si el valor de ETag actual es nulo, el método comprueba que el encabezado If-None- Match tiene un valor de "*". Si el valor de ETag actual no es un valor predeterminado, el método comprueba el valor de ETag actual con respecto al encabezado If-Match de la solicitud. En cualquier caso, el método produce WebFaultException con un código de estado HTTP 412 (Error de condición previa) si el encabezado esperado no se encuentra en la solicitud o si su valor no satisface la comprobación condicional y establece el encabezado de ETag de la respuesta al valor de ETag actual.

Tanto los métodos CheckConditional como el método SetETag garantizan que el valor de ETag definido en el encabezado de respuesta es un ETag válido según la especificación de HTTP. Esto incluye la delimitación del valor de ETag con comillas dobles si aún no están presentes y el escape correspondiente de cualquier carácter interno de comillas dobles. No se admite la comparación de ETag débil.

En el siguiente ejemplo, se muestra cómo utilizar estos métodos.

[WebGet(UriTemplate = "{id}"), Description("Returns the specified customer from customers collection. Returns NotFound if there is no such customer. Supports conditional GET.")]
public Customer GetCustomer(string id)
{
    lock (writeLock)
    {
        // return NotFound if there is no item with the specified id.
        object itemEtag = customerEtags[id];
        if (itemEtag == null)
        {
            throw new WebFaultException(HttpStatusCode.NotFound);
        }

        // return NotModified if the client did a conditional GET and the customer item has not changed
        // since when the client last retrieved it
                 WebOperationContext.Current.IncomingRequest.CheckConditionalRetrieve((long)itemEtag);
       Customer result = this.customers[id] as Customer;
       // set the customer etag before returning the result
                WebOperationContext.Current.OutgoingResponse.SetETag((long)itemEtag);
                return result;
    }
}

Consideraciones de seguridad

Las solicitudes que requieren autorización no deberían tener sus respuestas almacenadas en memoria caché, porque la autorización no se realiza cuando la respuesta se sirve desde la memoria caché. Si se almacena en memoria caché tales respuestas, se introduce una vulnerabilidad de seguridad grave. Normalmente, las solicitudes que requieren autorización proporcionan los datos específicos del usuario y, por consiguiente, el almacenamiento en caché del lado servidor no es beneficioso. En tales situaciones, será más apropiado usar el almacenamiento en caché del lado cliente o, simplemente, no usar el almacenamiento en memoria caché.