web-dev-qa-db-fra.com

Mise en cache dans WCF?

Je crée un service WCF. J'ai besoin de stocker des données de référence dans le cache que je rechercherai chaque fois que je recevrai des entrées de la méthode ... Quelle est la bonne façon de procéder? Je voudrais également définir une politique d'expiration pour le cache qui l'invalidera après un certain intervalle de temps.

42
Developer

Si vous utilisez .NET 4, la méthode recommandée consiste à utiliser MemoryCache

30
Juozas Kontvainis

Toute solution de mise en cache doit résoudre deux problèmes de base

1) Stockage des éléments du cache et récupération

2) Invalidation du cache

Étant donné que la mise en cache Http est bien connue, je ne vais pas l'expliquer en détail. Vous pouvez utiliser l'attribut de compatibilité asp seul avec une configuration Web, où vous obtiendrez la mise en cache par charme.

[AspNetCacheProfile("MyProfile")]
        public Customer GetName(string id)
        {
             // ...
        }

Et la configuration Web est comme

<system.serviceModel>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />    
</system.serviceModel>
<system.web>
   <caching>
      <outputCacheSettings>
         <outputCacheProfiles>
            <add name=" MyProfile" duration="600" varyByParam="none" sqlDependency="MyTestDatabase:MyTable"/>
         </outputCacheProfiles>
      </outputCacheSettings>
   </caching>
</system.web>

Mais cela ne convient pas à la plupart des scénarios, en particulier lorsque vous avez un grand objet complexe à mettre en cache. Par exemple, j'ai eu une situation où je voulais mettre en cache une image générée par le système (la sortie du contrat d'exploitation est une image générée par le système qui dépend de l'entrée). Dans un tel cas, vous devez implémenter votre propre cache. J'ai utilisé des blocs de mise en cache de bibliothèque d'entreprise Microsoft qui répondaient à toutes mes exigences de stockage de mise en cache. Cependant, vous devez toujours effectuer la plomberie pour intégrer le bloc de mise en cache de la bibliothèque d'entreprise Microsoft à votre service WCF. Vous devez d'abord intercepter le canal de communication WCF pour implémenter le cache. Une discussion détaillée sur la façon d'intercepter le canal de communication WCF peut être trouvée à http://msdn.Microsoft.com/en-us/magazine/cc163302.aspx . Voici comment vous effectuez la plomberie pour la mise en cache WCF

Basic Plumbing Architecture

Étape 0 Supposons que vous ayez un contrat d’opération comme suit et que vous souhaitiez mettre en cache le retour d’élément par cette méthode.

[OperationContract]
MyCompositeClass Rotate(int angle)

Étape 1 Vous devez d'abord enregistrer votre cache personnalisé dans le pipeline WCF. Pour ce faire, je vais utiliser un attribut afin de pouvoir décorer joliment mon appel WCF selon les principes de programmation orient orienté.

using System;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Reflection;

    [AttributeUsage(AttributeTargets.Method)]
    public class MyCacheRegister : Attribute, IOperationBehavior
    {
        ConstructorInfo _chacherImplementation;
        public ImageCache(Type provider)
        {
            if (provider == null)
            {
                throw new ArgumentNullException("Provider can't be null");
            }
            else if (provider.IsAssignableFrom(typeof(IOperationInvoker)))
            {
                throw new ArgumentException("The type " + provider.AssemblyQualifiedName + " does not implements the interface " + typeof(IOperationInvoker).AssemblyQualifiedName);
            }
            else
            {
                try
                {
                    Type[] constructorSignatureTypes = new Type[1];
                    constructorSignatureTypes[0] = typeof(IOperationInvoker);
                    _chacherImplementation = provider.GetConstructor(constructorSignatureTypes);

                }
                catch
                {
                    throw new ArgumentException("There is no constructor in " + provider.AssemblyQualifiedName + " that accept " + typeof(IOperationInvoker).AssemblyQualifiedName + " as a parameter");
                }

            }


        }

        public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
        {
            return;
        }

        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
        {
            return;
        }

        /// <summary>
        /// Decorate the method call with the cacher
        /// </summary>
        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
        {
            //decorator pattern, decorate with a  cacher
            object[] constructorParam = new object[1];
            constructorParam[0] = dispatchOperation.Invoker;
            dispatchOperation.Invoker = (IOperationInvoker)_chacherImplementation.Invoke(constructorParam);
        }

        public void Validate(OperationDescription operationDescription)
        {
            return;
        }
    }

Étape 2

Ensuite, vous devez implémenter le point où l'objet de cache sera récupéré.

using System;
using System.ServiceModel.Dispatcher;
using Microsoft.Practices.EnterpriseLibrary.Caching;
using Microsoft.Practices.EnterpriseLibrary.Common;
using System.IO;

    class RotateCacher : IOperationInvoker
    {

        private IOperationInvoker _innerOperationInvoker;
        public RotateImageCacher(IOperationInvoker innerInvoker)
        {
            _innerOperationInvoker = innerInvoker;
        }
        public object[] AllocateInputs()
        {
            Object[] result = _innerOperationInvoker.AllocateInputs();
            return result;
        }

        public object Invoke(object instance, object[] inputs, out object[] outputs)
        {
            object result=null;

///TODO: You will have more object in the input if you have more ///parameters in your method

            string angle = inputs[1].ToString();

            ///TODO: create a unique key from the inputs
            string key = angle;

            string provider = System.Configuration.ConfigurationManager.AppSettings["CacheProviderName"];
            ///Important Provider will be DiskCache or MemoryCache for the moment
provider =”DiskCache”;
///TODO: call enterprise library cache manager, You can have your own 
/// custom cache like Hashtable

    ICacheManager manager = CacheFactory.GetCacheManager(provider);

            if (manager.Contains(key))
            {

                result =(MyCompositeClass) manager[key];

            }
            else
            {
                result =(MyCompositeClass) _innerOperationInvoker.Invoke(instance, inputs, out outputs);
                manager.Add(key, result);
            }
            return result;
        }

        public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
        {
            IAsyncResult result = _innerOperationInvoker.InvokeBegin(instance, inputs, callback, state);
            return result;
        }

        public object InvokeEnd(object instance, out object[] outputs, IAsyncResult asyncResult)
        {
            object result = _innerOperationInvoker.InvokeEnd(instance, out outputs, asyncResult);
            return result;
        }

        public bool IsSynchronous
        {
            get { return _innerOperationInvoker.IsSynchronous; }
        }
    }

Étape 3

Enfin, ajoutez votre attribut au-dessus de votre appel de service

[OperationContract]
[MyCacheRegister(typeof(RotateCacher)]
MyCompositeClass Rotate(int angle)

La configuration du bloc de mise en cache de la bibliothèque d'entreprise dépasse le cadre de cette réponse. Vous pouvez utiliser le lien suivant pour l'apprendre. La bonne chose à propos de la bibliothèque d'entreprise est que vous êtes prêt à étendre votre politique de mise en cache. Il a intégré des méthodes d'expiration et de stockage du cache. Vous pouvez également écrire vos propres politiques d'expiration et de stockage du cache. http://www.codeproject.com/KB/web-cache/CachingApplicationBlock.aspx

Une dernière chose, pour que la mise en cache de votre bibliothèque d'entreprise fonctionne, vous devez ajouter les détails de configuration suivants. Vous devez également ajouter des DLL pertinentes à la référence de votre projet.

<configSections>
    <section name="cachingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Caching.Configuration.CacheManagerSettings, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
  </configSections>


  <cachingConfiguration defaultCacheManager="Cache Manager">
    <cacheManagers>
      <add name="MemoryCache" type="Microsoft.Practices.EnterpriseLibrary.Caching.CacheManager, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        expirationPollFrequencyInSeconds="60" maximumElementsInCacheBeforeScavenging="1000"
        numberToRemoveWhenScavenging="10" backingStoreName="NullBackingStore" />
      <add name="DiskCache" type="Microsoft.Practices.EnterpriseLibrary.Caching.CacheManager, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        expirationPollFrequencyInSeconds="60" maximumElementsInCacheBeforeScavenging="1000"
        numberToRemoveWhenScavenging="10" backingStoreName="IsolatedStorageCacheStore" />
    </cacheManagers>
    <backingStores>
      <add type="Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.NullBackingStore, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        name="NullBackingStore" />
      <add name="IsolatedStorageCacheStore" type="Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.IsolatedStorageBackingStore, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
        encryptionProviderName="" partitionName="MyCachePartition" />
    </backingStores>
  </cachingConfiguration>
28

Vous pouvez jeter un œil à Velocity . Il s'agit de la mémoire distribuée de Microsoft cadre de mise en cache . Mais cela peut être un peu trop bêta ...

1
1
nologo

Si vous allez évoluer vers plus d'un serveur dans un système sans état à charge équilibrée, vous voudrez conception pour l'utilisation d'un cache distribué . Les principales choses à faire ici sont:

  1. Utilisez à la fois un cache local et distribué. Ne placez la session ou les éléments de courte durée que dans le cache distribué, les autres éléments de cache localement.

  2. Définissez des délais d'expiration appropriés pour les éléments. Cela variera en fonction du type d'informations et de la proximité de la source dont elles doivent être.

  3. Supprimez des éléments du cache lorsque vous savez qu'ils seront incontinents (comme les mises à jour, les suppressions, etc.).

  4. Prenez soin de concevoir des clés de cache uniques. Créez un modèle du type d'informations que vous prévoyez de mettre en cache et utilisez-le comme modèle pour créer des clés.

1
Paul Fryer

Vous pouvez utiliser System.Web.Cache (même si vous n'êtes pas dans un contexte Web), et c'est ce que je ferais. C'est fondamentalement une grande table de hachage en mémoire avec quelques subtilités pour expirer le contenu.

0
MrKurt

Il existe plusieurs façons de procéder. Une solution assez simple consiste à héberger vous-même l'objet System.Web.Cache et à l'utiliser pour stocker les données de référence. Il y a un bon exemple de cela ici: http://kjellsj.blogspot.com/2007/11/wcf-caching-claims-using.html

0
tomasr

Le WCF REST Starter Kit a la mise en cache, voici un article sur son utilisation ... avec un exemple de code.

http://weblogs.asp.net/gsusx/archive/2008/10/29/adding-caching-to-wcf-restful-services-using-the-rest-starter-kit.aspx

0
Zachary

Plutôt que d'expirer les données de cache de temps en temps, vous pouvez simplement vous assurer d'invalider le cache chaque fois que les données sous-jacentes que vous mettez en cache changent.

Voir cet exemple dans info Q http://www.infoq.com/news/2011/04/Attribute-Caching

[Cache.Cacheable("UserTransactionCache")]
public DataTable GetAllTransactionsForUser(int userId)
{
    return new DataProvider().GetAllTransactionsForUser(userId);
}

[Cache.TriggerInvalidation("UserTransactionCache")]
public void DeleteAllTransactionsForUser(int userId)
{
 ...
}
0
Adam Bell