﻿<?xml version="1.0" encoding="utf-8"?><rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0"><channel><title>Blog de Julien Corioland (MVP)</title><link>http://www.juliencorioland.net/</link><description>Quand les technologies deviennent passion...</description><item><guid isPermaLink="false">azure-communication-entre-instances-avec-les-endpoints-internes</guid><link>http://www.juliencorioland.net/Archives/azure-communication-entre-instances-avec-les-endpoints-internes</link><author>Julien Corioland</author><title>[Azure] Communication entre instances avec les endpoints internes</title><description>&lt;p&gt;Il y a quelques temps j’ai posté deux billets décrivant des techniques pour réaliser de l’invalidation de cache sur plusieurs instances d’un rôle Windows Azure :&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://www.juliencorioland.net/Archives/azure-invalidation-de-settings-ou-de-cache-a-l-aide-d-une-cle-de-configuration" target="_blank"&gt;Invalidation de settings ou de cache à l’aide d’une clé de configuration&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.juliencorioland.net/Archives/aspnet-mvc-multicast-dans-une-application-hebergee-sur-azure-avec-appfabric-servicebus-et-neteventrelaybinding" target="_blank"&gt;Multicast dans une application hébergée sur Azure avec AppFabric ServiceBus et NetEventRelayBinding&lt;/a&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Dans ce billet, je souhaiterai vous présenter une autre manière de communiquer entres différentes instances d’un même rôle, en utilisant les Endpoints internes.&lt;/p&gt;  &lt;p&gt;Les endpoints internes sont des points d’accès sur lesquels chaque instance est en capacité, par exemple, d’hoster un service WCF. Il existe plusieurs avantages dans l’utilisation de ceux-ci :&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Ils sont internes, donc inaccessibles depuis l’extérieur &lt;/li&gt;    &lt;li&gt;Il est possible de communiquer via un binding en net.tcp &lt;/li&gt;    &lt;li&gt;Leur utilisation n’entraîne pas de surcoût sur votre facture Windows Azure &lt;/li&gt; &lt;/ul&gt;  &lt;h3&gt;&lt;u&gt;Création d’un endpoint interne&lt;/u&gt;&lt;/h3&gt;  &lt;p&gt;Pour ajouter un endpoint interne à un rôle Windows Azure, rendez-vous dans les propriétés de celui-ci dans votre projet Visual Studio, puis accédez à l’onglet “Endpoints” :&lt;/p&gt;  &lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/f4f8676f-261f-41a2-a522-cd3be6276604" rel="lightbox"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="https://juliencoriolandblog.blob.core.windows.net/medias/44682992-6862-446b-aae7-cc5d638a5c22" width="624" height="298" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Il suffit de cliquer sur le bouton Add Endpoint, de le nommer, de mettre son type à Internal, de choisir le protocole et&amp;#160; enfin de définir le port privé sur lequel le endpoint sera accessible :&lt;/p&gt;  &lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/8daa9fab-8db5-480b-b485-1e9b33c955c9" rel="lightbox"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="https://juliencoriolandblog.blob.core.windows.net/medias/4da3f44d-41e1-460b-ab8b-c101aa203242" width="625" height="151" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Toutes les instances du rôles exposeront alors ce endpoint automatiquement !&lt;/p&gt;  &lt;h3&gt;&lt;/h3&gt;  &lt;h3&gt;&lt;u&gt;Héberger un service WCF sur un endpoint interne&lt;/u&gt;&lt;/h3&gt;  &lt;p&gt;Il est possible de faire en sorte qu’un service WCF soit hébergé au démarrage de chaque instance. Celui-ci pourra être appelé par n’importe quelle autre instance du rôle. Par exemple, dans le cas d’un scénario d’invalidation de cache, une instance de rôle doit être capable de notifier toutes les autres de réinitialiser leur cache local. Pour cela, il suffit de créer un canal de communication via WCF entre les instances.&lt;/p&gt;  &lt;p&gt;Dans le cas présent, le contrat de service WCF est très simple :&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;[ServiceContract]
public interface ICacheInvalidationService
{
    [OperationContract]
    void InvalideCache();
}&lt;/pre&gt;

&lt;p&gt;Son implémentation aussi :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public class CacheInvalidationService : ICacheInvalidationService
{
    public void InvalideCache()
    {
        //invalidation du cache local
    }
}&lt;/pre&gt;

&lt;p&gt;Le démarrage du service WCF se fait dans la méthode &lt;strong&gt;OnStart&lt;/strong&gt; du WebRole. En premier lieu, il faut récupérer l’adresse du endpoint interne qui a été créé :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;if (RoleEnvironment.CurrentRoleInstance.InstanceEndpoints
    .Any(ip =&amp;gt; ip.Key == &amp;quot;CacheInvalidationEndpoint&amp;quot;))
{
    IPEndPoint endpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints
        .First(ip =&amp;gt; ip.Key == &amp;quot;CacheInvalidationEndpoint&amp;quot;).Value.IPEndpoint;


}&lt;/pre&gt;

&lt;p&gt;Ensuite, l’hébergement du service WCF se fait très simplement à l’aide d’un &lt;strong&gt;ServiceHost&lt;/strong&gt; :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public class WebRole : RoleEntryPoint
{
    private ServiceHost _serviceHost;

    public override bool OnStart()
    {
        if (RoleEnvironment.CurrentRoleInstance.InstanceEndpoints
            .Any(ip =&amp;gt; ip.Key == &amp;quot;CacheInvalidationEndpoint&amp;quot;))
        {
            IPEndPoint endpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints
                .First(ip =&amp;gt; ip.Key == &amp;quot;CacheInvalidationEndpoint&amp;quot;).Value.IPEndpoint;

            Uri baseAddress = new Uri(string.Format(&amp;quot;net.tcp://{0}&amp;quot;, endpoint));
            _serviceHost = new ServiceHost(typeof(CacheInvalidationService), baseAddress);

            NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
            _serviceHost.AddServiceEndpoint(typeof(ICacheInvalidationService), binding, &amp;quot;CacheInvalidation&amp;quot;);

            try
            {
                _serviceHost.Open();
            }
            catch
            {
                Trace.WriteLine(&amp;quot;Impossible de démarrer le service d'invalidation de cache&amp;quot;);
            }
        }

        return base.OnStart();
    }
}&lt;/pre&gt;

&lt;p&gt;Il est également possible de surcharger la méthode &lt;strong&gt;OnStop&lt;/strong&gt; du rôle pour arrêter le service WCF proprement :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public override void OnStop()
{
    if (_serviceHost != null)
    {
        try
        {
            _serviceHost.Close();
        }
        catch
        {
            _serviceHost.Abort();
        }
    }
    base.OnStop();
}&lt;/pre&gt;

&lt;p&gt;A présent, toute instance de ce rôle expose un service WCF permettant l’invalidation de son cache local !&lt;/p&gt;

&lt;h3&gt;Contacter toutes les instances&lt;/h3&gt;

&lt;p&gt;La dernière étape consiste à écrire le code qui permet d’appeler le service d’invalidation de cache sur toutes les instances du rôle.&lt;/p&gt;

&lt;p&gt;Pour cela, il est nécessaire de récupérer tous les &lt;strong&gt;IPEndPoint&lt;/strong&gt; qui portent le nom “CacheInvalidationEndpoint” :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;var ipEndpoints =
    from instance in RoleEnvironment.CurrentRoleInstance.Role.Instances
    from endpoint in instance.InstanceEndpoints
    where endpoint.Key == &amp;quot;CacheInvalidationEndpoint&amp;quot;
    select endpoint.Value.IPEndpoint;&lt;/pre&gt;

&lt;p&gt;Ensuite, il suffit de parcourir les endpoints pour créer un canal de communication vers chacun d’eux, à l’aide d’une &lt;strong&gt;ChannelFactory&lt;/strong&gt; WCF :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;foreach (var ipEndPoint in ipEndpoints)
{
    try
    {
        NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);

        ChannelFactory&amp;lt;ICacheInvalidationService&amp;gt; channelFactory =
            new ChannelFactory&amp;lt;ICacheInvalidationService&amp;gt;(binding);

        string uri = string.Format(&amp;quot;net.tcp://{0}/CacheInvalidation&amp;quot;, ipEndPoint);
        EndpointAddress address = new EndpointAddress(uri);

        ICacheInvalidationService service = channelFactory.CreateChannel(address);
        if (service != null)
        {
            service.InvalideCache();
        }
    }
    catch
    {
        //log exception
    }
}&lt;/pre&gt;

&lt;p&gt;Et voilà, toutes les instances ont maintenant été notifiée de vider leur cache local !&lt;/p&gt;

&lt;p&gt;Vraiment simple à mettre en place, et super utile !&lt;/p&gt;

&lt;p&gt;Pour tester, récupérez le projet d’exemple et mettez un point d’arrêt dans la méthode &lt;strong&gt;InvalidateCache&lt;/strong&gt; du service WCF. Lancez le rôle Azure (configuré pour exécuter deux instances) et appelez ensuite l’URL /Home/InvalidateCache sur l’application MVC. Vous verrez que Visual Studio s’arrête bien deux fois dans le service, une fois pour chaque instance du rôle &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/a3056944-f256-4f62-8d8a-04f781bfc30a" /&gt;&lt;/p&gt;

&lt;p&gt;Le code source est disponible &lt;a href="https://juliencoriolandblog.blob.core.windows.net/publicfiles/ExempleEndpointInterne.zip" target="_blank"&gt;ici&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A bientôt !&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Sat, 14 Jan 2012 14:58:00 Z</pubDate><a10:updated>2012-01-14T14:58:00Z</a10:updated></item><item><guid isPermaLink="false">2012-c-est-parti-–-meilleurs-voeux-livre-sur-aspnet-mvc-4-techdays</guid><link>http://www.juliencorioland.net/Archives/2012-c-est-parti-%E2%80%93-meilleurs-voeux-livre-sur-aspnet-mvc-4-techdays</link><author>Julien Corioland</author><title>2012 c’est parti – Meilleurs voeux, livre sur ASP.NET MVC 4, Techdays…</title><description>&lt;p&gt;Avant tout, je profite de ce post pour vous souhaiter une excellente année 2012 ! Qu’elle soit aussi riche que 2011 professionnellement et qu’elle vous apporte tout ce que vous souhaitez : santé, bonheur et réussite !&lt;/p&gt;  &lt;p&gt;Pour ma part, 2012 s’annonce déjà très riche en travail &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/c07162f3-ec37-49c5-a304-158079704f73" /&gt;&lt;/p&gt;  &lt;p&gt;Pour commencer, un livre sur ASP.NET MVC 4 actuellement en cours d’écriture et sur lequel j’aurai amplement l’occasion de revenir d’ici à sa sortie !&lt;/p&gt;  &lt;p&gt;Trois sessions aux Techdays 2012 (merci aux personnes concernées pour leur confiance !) :&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;font color="#404040"&gt;&lt;u&gt;Architecture, bonnes pratiques et recettes pour la réussite de vos projets avec ASP.NET MVC&lt;/u&gt; : je coanimerai cette session avec &lt;a href="http://www.rui.fr/" target="_blank"&gt;Rui Carvalho&lt;/a&gt;. Nous tenterons de vous fournir un ensemble de bonnes pratiques et des “trucs” que nous utilisons régulièrement chez nos clients dans la mise en place de projets ASP.NET MVC ! Plus d’info sur la session sur le &lt;a href="http://www.microsoft.com/france/mstechdays/programmes/parcours.aspx?SessionID=cfeebe6c-a5bf-4cff-9f9c-133d30501bd3" target="_blank"&gt;site des Techdays 2012…&lt;/a&gt;&lt;/font&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;font color="#404040"&gt;&lt;/font&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;font color="#404040"&gt;&lt;u&gt;La dure lutte du développeur : 10 trucs pratiques pour une application mobile bien peaufinée :&lt;/u&gt; il manque souvent peu de choses pour qu’une application soit parfaite ! Malheuresement ce petit effort est parfois négligé avant la publication d’une application. Cyril Cathala, David Poulin et moi-même vous donnerons des conseils pratiques pour peaufiner toutes vos applications Windows Phone ! Plus d’info sur la session sur le &lt;a href="http://www.microsoft.com/france/mstechdays/programmes/parcours.aspx?SessionID=8ff36860-fd09-4981-bfe9-253aaaba4736" target="_blank"&gt;site des Techdays 2012…&lt;/a&gt;&lt;/font&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;font color="#404040"&gt;&lt;/font&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;font color="#404040"&gt;&lt;u&gt;Enseigner les technologies Microsoft, un exemple avec Windows Phone :&lt;/u&gt; je co-animerai cette session avec Julie Knibbe. L’idée est de vous présenter, par l’exemple de Windows Phone, les ressources qui sont à votre disposition pour enseigner les technologies Microsoft ! Plus d’info sur la session sur le &lt;a href="http://www.microsoft.com/france/mstechdays/programmes/parcours.aspx?SessionID=b2ef1ab8-f6fd-4072-b494-e67813dba7e3" target="_blank"&gt;site des Techdays 2012…&lt;/a&gt;&lt;/font&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;font color="#404040"&gt;Rendez-vous aux Techdays donc !&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.microsoft.com/france/mstechdays/"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="bann-techdays-2012" border="0" alt="bann-techdays-2012" src="https://juliencoriolandblog.blob.core.windows.net/medias/4cc6f654-3f0c-4714-a890-1b04bcc4c1e4" width="602" height="308" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Et évidemment, en 2012 toujours, mon travail au quotidien comme consultant chez Infinite Square ! &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/c07162f3-ec37-49c5-a304-158079704f73" /&gt; Il me permettra d’animer ce blog qui sera certainement rythmé par Windows Phone, ASP.NET MVC 4 et Windows Azure !&lt;/p&gt;  &lt;p&gt;Bon 2012 !&lt;/p&gt;  &lt;p&gt;Julien&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Mon, 02 Jan 2012 08:57:00 Z</pubDate><a10:updated>2012-01-03T09:23:17Z</a10:updated></item><item><guid isPermaLink="false">aspnet-mvc-multicast-dans-une-application-hebergee-sur-azure-avec-appfabric-servicebus-et-neteventrelaybinding</guid><link>http://www.juliencorioland.net/Archives/aspnet-mvc-multicast-dans-une-application-hebergee-sur-azure-avec-appfabric-servicebus-et-neteventrelaybinding</link><author>Julien Corioland</author><title>[ASP.NET MVC] Multicast dans une application hébergée sur Azure avec AppFabric ServiceBus et NetEventRelayBinding</title><description>&lt;p&gt;Il y a quelques semaines j’ai posté une astuce pour invalider un cache mémoire sur un &lt;a href="http://www.juliencorioland.net/Archives/azure-invalidation-de-settings-ou-de-cache-a-l-aide-d-une-cle-de-configuration" target="_blank"&gt;changement de configuration d’instance Windows Azure&lt;/a&gt;. Bien que cette technique fonctionne, elle nécessite une intervention humaine, puisqu’à chaque fois que l’on souhaite invalider le cache, il faut modifier la configuration du service hébergé sur Azure.&lt;/p&gt;  &lt;p&gt;Afin d’éviter de faire cela, je me suis penché sur l’utilisation d’AppFabric ServiceBus dans Azure et notamment du &lt;strong&gt;NetEventRelayBinding&lt;/strong&gt; pour faire en sorte que lorsque je publie / supprime ou modifie un post, toutes les instances de mon blog soit notifiées et invalident leur cache mémoire.&lt;/p&gt;  &lt;p&gt;Pour faire simple, j’expose un endpoint sur le service bus Azure, sur lequel toutes les instances vont s’abonner. Dès que l’une d’entre elle contact ce endpoint, toutes les autres sont notifiées.&lt;/p&gt;  &lt;p&gt;Le bus des services permet de mettre en place un relai entre un client et un host de service WCF. Par exemple, il est possible d’avoir un service WCF qui soit hébergé sur un serveur IIS dans votre infrastructure et que ce service soit consommé depuis Azure ou une application totalement externe via le bus des services, évitant ainsi des problèmes de sécurité et de configuration de pare-feu.&lt;/p&gt;  &lt;p&gt;L’autre scénario (mis en place ici) est le multicast ou le bus des services sert également de relai vers un host WCF, mais en mode multicast.&lt;/p&gt;  &lt;p&gt;Pour mettre cela en place, il faut procéder en différentes étapes, décrites ci-après.&lt;/p&gt;  &lt;h2&gt;&lt;/h2&gt;  &lt;h3&gt;1. Activer un domaine sur le bus des services Windows Azure&lt;/h3&gt;  &lt;p&gt;Connectez-vous au portail d’administration Windows Azure sur &lt;a href="http://windows.azure.com"&gt;http://windows.azure.com&lt;/a&gt; puis rendez-vous dans la section &lt;strong&gt;Bus des services, Contrôle d’accès et Cache&lt;/strong&gt; via le menu de gauche :&lt;/p&gt;  &lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/e0ac8082-83b9-49f9-9107-1e2353a402e5" rel="lightbox"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="https://juliencoriolandblog.blob.core.windows.net/medias/f3913438-b0bf-42de-adc5-fa7433ae9962" width="205" height="284" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Sélectionnez ensuite &lt;strong&gt;Bus des services&lt;/strong&gt; dans le noeud &lt;strong&gt;AppFabric&lt;/strong&gt;, puis cliquez sur &lt;strong&gt;Nouveau&lt;/strong&gt; dans le ruban. Une popup s’ouvre alors, vous permettant d’ajouter un nouveau domaine sur lequel vous pourrez exposer vos services via AppFabric :&lt;/p&gt;  &lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/adbba4ff-15d3-4af7-9f39-f7ba042ce86b" rel="lightbox"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="https://juliencoriolandblog.blob.core.windows.net/medias/58f021ab-fc42-41ea-ae68-2d47ca3c1beb" width="520" height="223" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Choisissez votre espace de nom, la région dans laquelle exposer le bus des services, l’abonnement sur lequel vous souhaitez l’ajouter… puis valider la popup. Patientez jusqu’à ce que le Bus des services ajouté soit marqué comme “Actif” dans la liste.&lt;/p&gt;  &lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/dea831f3-d634-42c2-b9bc-4d313d36684c" rel="lightbox"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="https://juliencoriolandblog.blob.core.windows.net/medias/28a35d77-262a-4451-beb8-57b7c2d305b3" width="620" height="79" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h3&gt;2. Création du contrat de service et de l’implémentation du service&lt;/h3&gt;  &lt;p&gt;Comme AppFabric Service Bus sert de relais entre clients et host WCF, on utilise les mêmes mécanismes que WCF classique pour créer contrat de services et implémentations de services.&lt;/p&gt;  &lt;p&gt;Dans mon cas, le contrat est ultra simple :&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;[ServiceContract(Name = &amp;quot;IRelayCacheInvalidationEventContract&amp;quot;, Namespace = &amp;quot;http://www.juliencorioland.net/Services/&amp;quot;)]
public interface IRelayCacheInvalidationEventContract
{
    [OperationContract(IsOneWay = true)]
    void InvalidateCache();
}&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;Attention toutefois à bien marquer toutes les &lt;strong&gt;OperationContract&lt;/strong&gt; comme étant en mode “One-Way”, une condition nécessaire à l’utilisation du contrat sur le bus des services Azure.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;J’ai également prévu une interface pour représenter le canal de communication WCF qui sera utilisé pour invalider le cache :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public interface IRelayCacheInvalidationEventChannel : IRelayCacheInvalidationEventContract, IClientChannel
{
}&lt;/pre&gt;

&lt;p&gt;A présent l’implémentation du service, une fois encore très simple ici puisqu’il s’agit d’implémenter l’interface &lt;strong&gt;IRelayCacheInvalidationEventContract&lt;/strong&gt; :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public class CacheInvalidator : IRelayCacheInvalidationEventContract
{
    public void InvalidateCache()
    {
        ICacheService cacheService = ServiceLocator.Current.GetInstance&amp;lt;ICacheService&amp;gt;();
        if(cacheService != null)
        {
            cacheService.InvalidateCache();
        }
    }
}&lt;/pre&gt;

&lt;h3&gt;&lt;/h3&gt;

&lt;h3&gt;&amp;#160;&lt;/h3&gt;

&lt;h3&gt;3. Ajout du code pour hoster le service dans le Global.asax du rôle web ASP.NET MVC&lt;/h3&gt;

&lt;p&gt;Dans cette étape, il s’agit d’ajouter le code de création de l’host WCF qui sera exécuté par toutes les instances lors du démarrage de l’application web.&lt;/p&gt;

&lt;p&gt;Pour cela, il faut dans un premier temps récupérer 3 informations : le domaine du bus des services, le nom l’émetteur par défaut (issuer name) et la clé partagée (shared secret). Pour récupérer ces informations, sélectionnez votre bus des services dans l’interface d’administration et cliquez sur le bouton &lt;strong&gt;Affichage&lt;/strong&gt; de la zone&lt;strong&gt; Clé par défaut&lt;/strong&gt;, à droite. Une popup s’affiche alors :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/4769893e-2415-47b9-a93a-ac7e6c2e0ca5" rel="lightbox"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="https://juliencoriolandblog.blob.core.windows.net/medias/4a8f682b-1b35-47c4-818d-6f6c7f8ec326" width="478" height="176" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Personnellement, j’ai choisi de rajouter ces éléments au fichier de configuration du mon instance Azure, ce qui me permet de les récupérer de la sorte :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;string serviceNamespace = RoleEnvironment.GetConfigurationSettingValue(&amp;quot;CacheInvalidationRelayDomainNamespace&amp;quot;);
string issuerName = RoleEnvironment.GetConfigurationSettingValue(&amp;quot;CacheInvalidationRelayIssuerName&amp;quot;);
string issuerSecret = RoleEnvironment.GetConfigurationSettingValue(&amp;quot;CacheInvalidationRelayIssuerSecret&amp;quot;);&lt;/pre&gt;

&lt;p&gt;Ensuite, vous devez créer une &lt;strong&gt;EndpointBehavior&lt;/strong&gt; pour identifier l’appel sur le bus des services. Cela s’effectue à l’aide d’un &lt;strong&gt;TokenProvider&lt;/strong&gt; et d’une instance de la classe &lt;strong&gt;TransportClientEndpointBehavior&lt;/strong&gt; :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;TokenProvider tokenProvider = TokenProvider.CreateSharedSecretTokenProvider(issuerName, issuerSecret);
TransportClientEndpointBehavior relayCredentials = new TransportClientEndpointBehavior(tokenProvider);&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;Pour avoir accès à ces classes, il vous faut référencer la librairie Microsoft.ServiceBus.dll.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A présent, utilisez l’api du service bus pour créer l’Uri qui hostera le relais vers le service :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;Uri relayAddress = ServiceBusEnvironment.CreateServiceUri(&amp;quot;sb&amp;quot;, serviceNamespace, &amp;quot;/CacheInvalidation/&amp;quot;);&lt;/pre&gt;

&lt;p&gt;Puis comme vous l’auriez fait en WCF, crééez le ServiceHost et ajoutez lui l’endpoint behavior comme cela :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;_serviceHost = new ServiceHost(typeof(CacheInvalidator), relayAddress);
    foreach (var endpoint in _serviceHost.Description.Endpoints)
{
    endpoint.Behaviors.Add(relayCredentials);
}
_serviceHost.Open();&lt;/pre&gt;

&lt;h3&gt;&lt;/h3&gt;

&lt;h3&gt;&amp;#160;&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Remarquez que l’endpoint behavior est rajoutée sur tous les endpoints configurés pour le service CacheInvalidator.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A présent, toutes les instances peuvent être atteintes via du multicast et donc pourront invalider leur cache mémoire !&lt;/p&gt;



&lt;h3&gt;4. Ajout du code pour récupérer un canal WCF et lancer l’invalidation de cache&lt;/h3&gt;

&lt;p&gt;Comme en WCF, on utilise un &lt;strong&gt;ChannelFactory&lt;/strong&gt; pour récupérer un canal de communication. Pour pouvoir accéder au bus des services, il faut récupérer les mêmes informations que précédemment à savoir espace de noms, émetteur et clé partagé. Il faut également créer la behavior permettant d’authentifier l’appel&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;string serviceNamespace = RoleEnvironment.GetConfigurationSettingValue(&amp;quot;CacheInvalidationRelayDomainNamespace&amp;quot;);
string issuerName = RoleEnvironment.GetConfigurationSettingValue(&amp;quot;CacheInvalidationRelayIssuerName&amp;quot;);
string issuerSecret = RoleEnvironment.GetConfigurationSettingValue(&amp;quot;CacheInvalidationRelayIssuerSecret&amp;quot;);

TokenProvider tokenProvider = TokenProvider.CreateSharedSecretTokenProvider(issuerName, issuerSecret);
TransportClientEndpointBehavior relayCredentials = new TransportClientEndpointBehavior(tokenProvider);
Uri relayAddress = ServiceBusEnvironment.CreateServiceUri(&amp;quot;sb&amp;quot;, serviceNamespace, &amp;quot;/CacheInvalidation/&amp;quot;);&lt;/pre&gt;

&lt;p&gt;A partir de là, il est possible de créer la &lt;strong&gt;ChannelFactory&lt;/strong&gt; et de récupérer un canal de communication de type &lt;strong&gt;IRelayCacheInvalidationEventChannel&lt;/strong&gt; pour invalider le cache :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;ChannelFactory&amp;lt;IRelayCacheInvalidationEventChannel&amp;gt; channelFactory =
                new ChannelFactory&amp;lt;IRelayCacheInvalidationEventChannel&amp;gt;(&amp;quot;CacheInvalidationEndpoint&amp;quot;,
                    new EndpointAddress(relayAddress));
channelFactory.Endpoint.Behaviors.Add(relayCredentials);
_cacheInvalidationChannel = channelFactory.CreateChannel();

_cacheInvalidationChannel.Open();
_cacheInvalidationChannel.InvalidateCache();&lt;/pre&gt;

&lt;p&gt;Pensez à bien fermer le canal lorsque vous n’en n’avez plus besoin :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;_cacheInvalidationChannel.Close();&lt;/pre&gt;

&lt;p&gt;Le code étant en place, il ne reste plus qu’à configurer le service WCF, dans le Web.config du rôle web ASP.NET MVC !&lt;/p&gt;

&lt;h3&gt;&lt;/h3&gt;

&lt;h3&gt;5. Configuration du service WCF&lt;/h3&gt;

&lt;p&gt;Comme toujours la configuration d’un service WCF se passe dans la section system.serviceModel du fichier de configuration de l’application : ici le Web.config du rôle web !&lt;/p&gt;

&lt;p&gt;Tout d’abord, il faut ajouter les extensions propres à l’utilisation du bus des services :&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;&amp;lt;system.serviceModel&amp;gt;
    &amp;lt;extensions&amp;gt;
      &amp;lt;bindingExtensions&amp;gt;
        &amp;lt;add name=&amp;quot;netEventRelayBinding&amp;quot; type=&amp;quot;Microsoft.ServiceBus.Configuration.NetEventRelayBindingCollectionElement, Microsoft.ServiceBus, Version=1.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35&amp;quot; /&amp;gt;
      &amp;lt;/bindingExtensions&amp;gt;
    &amp;lt;/extensions&amp;gt;
&amp;lt;/system.serviceModel&amp;gt;&lt;/pre&gt;

&lt;p&gt;Ensuite il faut ajouter la définition du binding netRelayEventBinding (toujours dans system.serviceModel) :&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;&amp;lt;bindings&amp;gt;
  &amp;lt;netEventRelayBinding&amp;gt;
    &amp;lt;binding name=&amp;quot;default&amp;quot; /&amp;gt;
  &amp;lt;/netEventRelayBinding&amp;gt;
&amp;lt;/bindings&amp;gt;&lt;/pre&gt;

&lt;p&gt;Puis le endpoint du service :&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;&amp;lt;services&amp;gt;
  &amp;lt;service name=&amp;quot;MetroBlog.Bll.CacheInvalidator&amp;quot;&amp;gt;
    &amp;lt;endpoint name=&amp;quot;CacheInvalidationEndpoint&amp;quot;
            contract=&amp;quot;MetroBlog.Interfaces.IRelayCacheInvalidationEventContract&amp;quot;
            binding=&amp;quot;netEventRelayBinding&amp;quot;
            bindingConfiguration=&amp;quot;default&amp;quot; /&amp;gt;
  &amp;lt;/service&amp;gt;
&amp;lt;/services&amp;gt;&lt;/pre&gt;

&lt;p&gt;Pour terminer par la configuration du client :&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;&amp;lt;client&amp;gt;
  &amp;lt;endpoint name=&amp;quot;CacheInvalidationEndpoint&amp;quot;
            contract=&amp;quot;MetroBlog.Interfaces.IRelayCacheInvalidationEventContract&amp;quot;
            binding=&amp;quot;netEventRelayBinding&amp;quot;
            bindingConfiguration=&amp;quot;default&amp;quot;
            /&amp;gt;
&amp;lt;/client&amp;gt;&lt;/pre&gt;

&lt;p&gt;Et voilà ! Si vous avez bien suivi toutes les étapes, vos instances sont toutes capables d’invalider leur cache grâce au multicast de AppFabric Service Bus !&lt;/p&gt;

&lt;p&gt;A bientôt &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/bb93d462-5fc6-45b9-9bc2-2600d3c5b99f" /&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Fri, 04 Nov 2011 17:24:00 Z</pubDate><a10:updated>2011-11-04T17:24:00Z</a10:updated></item><item><guid isPermaLink="false">en-windows-phone-best-practices-for-periodic-tasks-with-background-agents</guid><link>http://www.juliencorioland.net/Archives/en-windows-phone-best-practices-for-periodic-tasks-with-background-agents</link><author>Julien Corioland</author><title>[EN] [Windows Phone] Best practices for periodic tasks with background agents</title><description>&lt;p&gt;As you probably already know Windows Phone 7.1 SDK (“Mango”) allows to create background agents to realize some operations when the application is not running. &lt;/p&gt;  &lt;p&gt;There are two types of tasks : periodic tasks and long running tasks. This post is about periodic tasks that are executed each 30 minutes by the operating system. There are some limitations with the API that may be used in background agent. For further information please go to &lt;a href="http://msdn.microsoft.com/en-us/library/hh202962(v=VS.92).aspx" target="_blank"&gt;this MSDN page&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;There are some constraints with background periodic tasks :&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;automatically executed by the OS each 30 minutes&lt;/li&gt;    &lt;li&gt;the operation can’t exceed 25 seconds per run&lt;/li&gt;    &lt;li&gt;if the phone switch to battery saver mode the background agent may not be executed&lt;/li&gt;    &lt;li&gt;on some devices only 6 background agents may be planned simultaneously &lt;/li&gt;    &lt;li&gt;agents can’t use more that 6MB of memory&lt;/li&gt;    &lt;li&gt;agents have to be re-planned each 2 weeks&lt;/li&gt;    &lt;li&gt;an agent that crashes two times is automatically disabled by the system&lt;/li&gt; &lt;/ul&gt;  &lt;h3&gt;Avoid exception propagation in background agent&lt;/h3&gt;  &lt;p&gt;Because two crashes will disable the background agent exception handling is really important in the development. For example, common errors occur when using a WebRequest&amp;#160; : the agent creates an HTTP request, the connection is lost, the callback throws an exception that is uncatched ! To avoid this, you can use the following code :&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;try
{ 
    var httpWebRequest = WebRequest.CreateHttp(&amp;quot;http://www.monsite.com/service.aspx&amp;quot;); 
    httpWebRequest.BeginGetRequestStream(asyncResult =&amp;gt; 
    { 
        try
        { 
            var responseStream = httpWebRequest.EndGetRequestStream(asyncResult); 
            //do stuff with the response
        } 
        catch (Exception exception) 
        { 
            OnError(exception); 
        } 
    }, null); 
} 
catch (Exception exception) 
{ 
    OnError(exception); 
}&lt;/pre&gt;

&lt;h3&gt;Schedule a background agent&lt;/h3&gt;

&lt;p&gt;Background agents are automatically disabled after two weeks : you have to re-schedule them. Windows Phone 7.5 has a centralized interface to manage all background agents that are available on the system so the user can disable an agent without run your application. If you try to schedule a periodic task with a disabled background agent an InvalidOperationException is thrown. You have to take care of this case and catch the exception as demonstrated bellow.&lt;/p&gt;

&lt;p&gt;The first step is to get the periodic task by its name :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;var periodicTask = ScheduledActionService.Find(&amp;quot;AgentDemo&amp;quot;) as PeriodicTask;&lt;/pre&gt;

&lt;p&gt;Next, delete the periodic task if it exists :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;if (periodicTask != null) 
{ 
    try
    { 
        ScheduledActionService.Remove(PeriodicTaskName); 
    } 
    catch (Exception) { } 
}&lt;/pre&gt;

&lt;p&gt;Re-create the periodic task and set the description (it’s mandatory) :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;periodicTask = new PeriodicTask(&amp;quot;AgentDemo&amp;quot;); 
periodicTask.Description = &amp;quot;Agent description&amp;quot;;&lt;/pre&gt;

&lt;p&gt;Try to schedule the periodic task :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;try
{ 
    ScheduledActionService.Add(periodicTask); 
} 
catch (InvalidOperationException ioe) 
{ 
    if (ioe.Message.Contains(&amp;quot;BNS Error: The action is disabled&amp;quot;)) 
    { 
        //agent is disabled for the application
    } 
}&lt;/pre&gt;

&lt;p&gt;Now the agent can be run by the system if it’s not disabled &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/626937fa-5522-4b98-93d2-52c5f2ee46fb" /&gt;&lt;/p&gt;

&lt;h3&gt;Memory usage limit&lt;/h3&gt;

&lt;p&gt;Background agent can’t use more than 6MB of memory. You can get the current memory usage using the following snippet :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;var memory = DeviceStatus.ApplicationMemoryUsageLimit  
    - DeviceStatus.ApplicationCurrentMemoryUsage;&lt;/pre&gt;

&lt;p&gt;Hope this helps &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/c9223d9f-3ae2-4e26-8309-b0c68a43a80f" /&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Tue, 25 Oct 2011 16:51:00 Z</pubDate><a10:updated>2011-10-25T16:51:00Z</a10:updated></item><item><guid isPermaLink="false">windows-phone-bonnes-pratiques-dans-une-tache-periodique-avec-un-background-agent</guid><link>http://www.juliencorioland.net/Archives/windows-phone-bonnes-pratiques-dans-une-tache-periodique-avec-un-background-agent</link><author>Julien Corioland</author><title>[Windows Phone] Bonnes pratiques dans une tâche périodique avec un background agent</title><description>&lt;p&gt;Vous le savez certainement déjà, la version 7.1 du SDK Windows Phone (“Mango”) offre la possibilité de créer des Background Agent, c’est à dire des tâches de fond exécutées par l’OS à interval régulier (en moyenne, selon certains paramètres, cf. plus bas).&lt;/p&gt; &lt;p&gt;Il existe deux types de tâches : périodique et traitement long. Dans ce post, nous nous intéresserons uniquement au premier type.&lt;/p&gt; &lt;p&gt;Dans ces tâches, il n’est pas possible de faire tout et n’importe quoi, seules certaines API sont autorisées (cf. &lt;a href="http://msdn.microsoft.com/en-us/library/hh202962(v=VS.92).aspx" target="_blank"&gt;cette page de la MSDN&lt;/a&gt;). Cependant des scénarios assez sympa sont rendus possibles, notamment la mise à jour des Live Tile. &lt;/p&gt; &lt;p&gt;Quelques éléments importants sur les backgrounds agents :&lt;/p&gt; &lt;ul&gt; &lt;li&gt;ils sont exécutés toutes les 30 minutes  &lt;li&gt;il est possible d’y exécuter du code pendant 25 secondes, pas plus!  &lt;li&gt;si le téléphone passe en mode d’économie de batterie, ils sont succeptibles de ne pas se lancer  &lt;li&gt;sur certain device, seuls 6 background agents peuvent être actif en simultanés  &lt;li&gt;ils ne doivent pas utiliser plus de 6MB de mémoire  &lt;li&gt;ils doivent être replannifié toutes les deux semaines  &lt;li&gt;un agent qui crash 2 fois est désactivé par l’OS &lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Cela fait pas mal de règles assez contraignantes mais il est vraiment possible de tirer profit de ces background agents en respectant quelques bonnes pratiques.&lt;/p&gt; &lt;h3&gt;Eviter la propagation d’exception dans un Background Agent&lt;/h3&gt; &lt;p&gt;Comme vu plus tôt, un background agent qui crash 2 fois est automatiquement désactivé par l’OS. Vous devez à tout prix être attentif à ne pas laisser passer d’exception. Un scénario de crash assez courant et la mauvaise gestion des connexions et appels réseaux.&lt;/p&gt; &lt;p&gt;Par exemple, votre background agent lance une requête HTTP vers un service, la connexion est coupée, l’exception n’est pas catchée dans la callback =&amp;gt; l’agent plante (il vous reste une vie!).&lt;/p&gt;&lt;pre class="brush: csharp;"&gt;try
{
    var httpWebRequest = WebRequest.CreateHttp("http://www.monsite.com/service.aspx");
    httpWebRequest.BeginGetRequestStream(asyncResult =&amp;gt;
    {
        try
        {
            var responseStream = httpWebRequest.EndGetRequestStream(asyncResult);
            //gestion de la réponse
        }
        catch (Exception exception)
        {
            OnError(exception);
        }
    }, null);
}
catch (Exception exception)
{
    OnError(exception);
}&lt;/pre&gt;
&lt;p&gt;Comme il est possible de le constater dans l’extrait de code ci-dessus, il est important d’intercépter les exceptions lors de la création de la requête HTTP, mais également lors de la récupération de la réponse, dans la callback ! &lt;/p&gt;
&lt;h3&gt;Plannifier un Background Agent&lt;/h3&gt;
&lt;p&gt;Comme vu précédemment, les background agents sont automatiquement désactivés au bout de 2 semaines. Il est donc nécessaire de les replannifier fréquemment.&lt;/p&gt;
&lt;p&gt;Lorsque l’on plannifie un background agent, il est possible que celui-ci ait été désactivé par l’utilisateur depuis l’interface du téléphone prévue à cet effet (dans les paramètres du téléphone). Dans ce cas une InvalidOperationException est levée lors de la tentative de plannification =&amp;gt; il faut penser à catcher cette exception et éventuellement sauvegarder le fait que le background agent soit désactivé dans un fichier de configuration, par exemple.&lt;/p&gt;
&lt;p&gt;La première étape consiste à récupérer une tâche périodique via son nom :&lt;/p&gt;&lt;pre class="brush: csharp;"&gt;var periodicTask = ScheduledActionService.Find("AgentDemo") as PeriodicTask;&lt;/pre&gt;
&lt;p&gt;Ensuite, on la supprime si elle existe (il est préférable de la supprimer et de la replannifier, afin de palier à la désactivation au bout de 2 semaines!) :&lt;/p&gt;&lt;pre class="brush: csharp;"&gt;if (periodicTask != null)
{
    try
    {
        ScheduledActionService.Remove(PeriodicTaskName);
    }
    catch (Exception) { }
}&lt;/pre&gt;
&lt;p&gt;Il faut ensuite recréer la tâche et lui passer une description (obligatoire!) :&lt;/p&gt;&lt;pre class="brush: csharp;"&gt;periodicTask = new PeriodicTask("AgentDemo");
periodicTask.Description = "Description de l'agent";&lt;/pre&gt;
&lt;p&gt;Ensuite on tente la plannification (gare à l’InvalidOperationException si l’utilisateur a désactivé le background agent dans les paramètres de Windows Phone!) :&lt;/p&gt;&lt;pre class="brush: csharp;"&gt;try
{
    ScheduledActionService.Add(periodicTask);
}
catch (InvalidOperationException ioe)
{
    if (ioe.Message.Contains("BNS Error: The action is disabled"))
    {
        //mise à jour conf
    }
}&lt;/pre&gt;
&lt;p&gt;Enfin, pour déboguer, il est possible de demander le lancement de l’agent après un temps donné (lorsque l’application est quittée via le bouton démarrer, pour concerver le débogueur attaché.)&lt;/p&gt;&lt;pre class="brush: csharp;"&gt;ScheduledActionService.LaunchForTest("DemoAgent", TimeSpan.FromSeconds(10));&lt;/pre&gt;
&lt;p&gt;Votre agent est prêt et fonctionnel !!&lt;/p&gt;
&lt;h3&gt;Eviter de dépasser la limite mémoire autorisée&lt;/h3&gt;
&lt;p&gt;Une des règles à respecter dans un background agent est de ne pas dépasser 6MB de mémoire ! Si vous dépassez cette limite, l’agent est intérrompu et il est considéré comme planté, donc désactivé au bout de 2 fois. Tout au long du traitement, il est possible de tester la mémoire consommée par l’agent à l’aide de l’instruction :&lt;/p&gt;&lt;pre class="brush: csharp;"&gt;var memory = DeviceStatus.ApplicationMemoryUsageLimit 
    - DeviceStatus.ApplicationCurrentMemoryUsage;&lt;/pre&gt;
&lt;p&gt;Voilà quelques règles à respecter pour faire des background agents qui resteront actifs !&lt;/p&gt;
&lt;p&gt;A bientôt &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/c3f8d0ab-c7b3-4f1a-9897-6df77a11f923"&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Tue, 25 Oct 2011 10:14:00 Z</pubDate><a10:updated>2011-11-04T16:34:51Z</a10:updated></item><item><guid isPermaLink="false">azure-invalidation-de-settings-ou-de-cache-a-l-aide-d-une-cle-de-configuration</guid><link>http://www.juliencorioland.net/Archives/azure-invalidation-de-settings-ou-de-cache-a-l-aide-d-une-cle-de-configuration</link><author>Julien Corioland</author><title>[Azure] Invalidation de settings ou de cache à l’aide d’une clé de configuration</title><description>&lt;p&gt;Dans ce post je vais vous expliquer une technique que j’ai utilisée pour invalider le cache / recharger les settings sur toutes les instances du rôle Web qui héberge mon blog (en vrai il n’y en a qu’une seule, mais je trouve l’astuce sympa &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/f484b684-078c-4f0d-9724-edfec42f8b06" /&gt;)&lt;/p&gt;  &lt;p&gt;&lt;u&gt;Le constat est simple :&lt;/u&gt; mes posts et les settings du blog (email, smtp, etc…) sont persistés dans le stockage table de Windows Azure. Afin d’éviter les aller-retours vers le stockage par table, je monte tout en cache mémoire (le nombre de transaction est facturé et en plus du coup ça envoi du lourd en terme de perf).&lt;/p&gt;  &lt;p&gt;&lt;u&gt;Problème :&lt;/u&gt; si je change un settings dans mon storage, comment est-ce que je demande à mon service de recharger les données ? Idem si pour une raison x ou y je souhaite vider le cache de posts, je dois avoir une solution. Si on se place dans un contexte Windows Azure multi-instances (cas très fréquent) c’est une couche de complexité supplémentaire qui se rajoute car il faut notifier chaque instance !&lt;/p&gt;  &lt;p&gt;La solution la plus simple est d’utiliser un setting Windows Azure et de faire en sorte que vos instances écoutent les changements de configuration. Dès lors, il suffit de détecter le changement de la bonne clé de settings, “ConfigVersion” dans mon cas (ou “CléBidon” si vous voulez) et de reinitialiser vos différents services (ici mon service de cache et mon service de settings).&lt;/p&gt;  &lt;p&gt;Tout cela se passe dans le fichier WebRole.cs du webRole, via l’événement Changed de RoleEnvironment :&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;public class WebRole : RoleEntryPoint
{
    public override bool OnStart()
    {
        // For information on handling configuration changes
        // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
        RoleEnvironment.Changed += OnRoleEnvironmentChanged;
        return base.OnStart();
    }

    private void OnRoleEnvironmentChanged(object sender, RoleEnvironmentChangedEventArgs e)
    {
        if (e.Changes.Any(change =&amp;gt; change is RoleEnvironmentConfigurationSettingChange)) {
            foreach (var change in e.Changes.OfType&amp;lt;RoleEnvironmentConfigurationSettingChange&amp;gt;()) {
                if (change.ConfigurationSettingName == &amp;quot;ConfigVersion&amp;quot;)
                {
                    var cacheService = UnityRoot.Container.Resolve&amp;lt;ICacheService&amp;gt;();
                    cacheService.InvalidateCache();
                    var settingsManager = UnityRoot.Container.Resolve&amp;lt;ISettingsManager&amp;gt;();
                    settingsManager.Reload();
                }
            }
        }
    }
}&lt;/pre&gt;

&lt;p&gt;A bientôt &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/0db8278d-538f-4246-b4e3-bfc950a5381e" /&gt;&lt;/p&gt;

&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:a99247b3-3e3e-448b-941e-ad29b50621c4" class="wlWriterEditableSmartContent"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/windows" rel="tag"&gt;windows&lt;/a&gt;,&lt;a href="http://technorati.com/tags/azure" rel="tag"&gt;azure&lt;/a&gt;,&lt;a href="http://technorati.com/tags/asp.net" rel="tag"&gt;asp.net&lt;/a&gt;&lt;/div&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Sat, 15 Oct 2011 11:43:00 Z</pubDate><a10:updated>2011-10-15T11:43:00Z</a10:updated></item><item><guid isPermaLink="false">livre-mvvm-de-la-decouverte-a-la-maîtrise</guid><link>http://www.juliencorioland.net/Archives/livre-mvvm-de-la-decouverte-a-la-ma%C3%AEtrise</link><author>Julien Corioland</author><title>[Livre] MVVM : De la découverte à la maîtrise</title><description>&lt;p&gt;Un billet rapide pour vous présenter un ouvrage dédié au pattern MVVM et à son utilisation dans le cadre de projets WPF/Silverlight et Silverlight pour Windows Phone.&lt;/p&gt;  &lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/eb77346c-d485-4695-b23b-98b8a4d008bd" rel="lightbox"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="9782815002097-mvvm" border="0" alt="9782815002097-mvvm" src="https://juliencoriolandblog.blob.core.windows.net/medias/8d726b13-4935-41ac-aca5-1bb0cd07596f" width="222" height="313" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Coécrit par deux collègues et amis &lt;a href="http://www.jonathanantoine.com" target="_blank"&gt;Jonathan Antoine&lt;/a&gt; et &lt;a href="http://blog.thomaslebrun.net" target="_blank"&gt;Thomas Lebrun&lt;/a&gt;, ce livre s’adresse à toutes personnes désireuses de découvrir MVVM ou d’approfondir leurs connaissances sur le sujet.&lt;/p&gt;  &lt;p&gt;Bien que je n’ai pas encore lu tout l’ouvrage (sorti aujourd’hui même), j’ai eu l’occasion de participer à la relecture de certains chapitres de celui-ci et j’ai énormément apprécié l’approche des auteurs : du pratique, du concret et la “réalité du terrain” concernant MVVM ! &lt;/p&gt;  &lt;p&gt;Pour en savoir plus c’est par là : &lt;a title="http://www.digitbooks.fr/catalogue/mvvm-antoine-lebrun.html" href="http://www.digitbooks.fr/catalogue/mvvm-antoine-lebrun.html"&gt;http://www.digitbooks.fr/catalogue/mvvm-antoine-lebrun.html&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Bravo à Jon et Tom, et surtout, bonne lecture à tous !&lt;/p&gt;  &lt;p&gt;A bientôt ! &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/3f6bc3d8-1542-4a39-90b6-8ed2b5c7128b" /&gt;&lt;/p&gt;  &lt;p&gt;--&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:19a58382-f9fa-4592-aeaf-520c87886c7f" class="wlWriterEditableSmartContent"&gt;Technorati Tags: &lt;a href="http://technorati.com/tags/livre" rel="tag"&gt;livre&lt;/a&gt;,&lt;a href="http://technorati.com/tags/mvvm" rel="tag"&gt;mvvm&lt;/a&gt;,&lt;a href="http://technorati.com/tags/model" rel="tag"&gt;model&lt;/a&gt;,&lt;a href="http://technorati.com/tags/view" rel="tag"&gt;view&lt;/a&gt;,&lt;a href="http://technorati.com/tags/viewmodel" rel="tag"&gt;viewmodel&lt;/a&gt;,&lt;a href="http://technorati.com/tags/wpf" rel="tag"&gt;wpf&lt;/a&gt;,&lt;a href="http://technorati.com/tags/silverlight" rel="tag"&gt;silverlight&lt;/a&gt;,&lt;a href="http://technorati.com/tags/windows" rel="tag"&gt;windows&lt;/a&gt;,&lt;a href="http://technorati.com/tags/phone" rel="tag"&gt;phone&lt;/a&gt;,&lt;a href="http://technorati.com/tags/wp7" rel="tag"&gt;wp7&lt;/a&gt;&lt;/div&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Thu, 29 Sep 2011 11:44:00 Z</pubDate><a10:updated>2011-10-02T16:44:52Z</a10:updated></item><item><guid isPermaLink="false">en-aspnet-mvc-4-asynchronous-controllers</guid><link>http://www.juliencorioland.net/Archives/en-aspnet-mvc-4-asynchronous-controllers</link><author>Julien Corioland</author><title>[EN] [ASP.NET MVC 4] Asynchronous controllers</title><description>&lt;p&gt;Asynchronous execution is the future of Windows development : it has been largely demonstrated during the //Build conference two weeks ago.&lt;/p&gt;  &lt;p&gt;In previous versions of ASP.NET MVC it was possible to create asynchronous controllers by inheriting the AsyncController class and using some conventions :&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;MyAction&lt;strong&gt;Async&lt;/strong&gt; : method that returns void and launches an asynchronous process &lt;/li&gt;    &lt;li&gt;MyAction&lt;strong&gt;Completed&lt;/strong&gt; : method that returns an ActionResult (the result of the MVC action “MyAction”, in this case) &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;To allow the MVC engine to manage asynchronous operations and pass the result to the view engine, developers had to use the propery AsyncManager of the AsyncController. The “completed” method parameters was passed by the MVC engine through this object.&lt;/p&gt;  &lt;p&gt;For example, the controller that is defined bellow allows to get a Json-serialized list of movies – asynchronously – from an OData service :&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;public class MoviesController : AsyncController
{
    public ActionResult Index()
    {
        return View();
    }

    public void GetJsonMoviesAsync(int? page)
    {
        const int pageSize = 20;
        int skip = pageSize * ((page ?? 1) - 1);
        string url = string.Format(&amp;quot;http://odata.netflix.com/[…]&amp;amp;$skip={0}&amp;amp;$top={1}&amp;quot;, 
            skip, pageSize);

        //the asynchronous operation is declared
        AsyncManager.OutstandingOperations.Increment();

        var webClient = new WebClient();
        webClient.DownloadStringCompleted += OnWebClientDownloadStringCompleted;
        webClient.DownloadStringAsync(new Uri(url));//the asynchronous process is launched
    }

    private void OnWebClientDownloadStringCompleted(object sender, 
        DownloadStringCompletedEventArgs e)
    {
        //the asynchronous process ends
        //&amp;quot;movies&amp;quot; result is added to the parameters of the AsyncManager
        //NB : it's the name of the parameter that is take by the 
        //GetJsonMoviesCompleted method
        List&amp;lt;Movie&amp;gt; movies = null;
        if (AsyncManager.Parameters.ContainsKey(&amp;quot;movies&amp;quot;))
        {
            movies = (List&amp;lt;Movie&amp;gt;)AsyncManager.Parameters[&amp;quot;movies&amp;quot;];
            movies.Clear();
        }
        else
        {
            movies = new List&amp;lt;Movie&amp;gt;();
            AsyncManager.Parameters[&amp;quot;movies&amp;quot;] = movies;
        }

        movies.AddRange(Movie.FromXml(e.Result));

        //the ends of the asynchronous operation (launches the call of &amp;quot;Action&amp;quot;Completed)
        AsyncManager.OutstandingOperations.Decrement();
    }

    public ActionResult GetJsonMoviesCompleted(List&amp;lt;Movie&amp;gt; movies)
    {
        //on retourne le résultat Json
        return Json(movies, JsonRequestBehavior.AllowGet);
    }
}&lt;/pre&gt;

&lt;p&gt;It’s not really complicated to create an asynchronous controller but ASP.NET MVC 4 and C# 5 with the new &lt;strong&gt;async&lt;/strong&gt; and &lt;strong&gt;await&lt;/strong&gt; keywords will make it easier !&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public class MoviesController : AsyncController
{
	public ActionResult Index()
	{
		return View();
	}

	public async Task&amp;lt;ActionResult&amp;gt; GetJsonMovies(int? page)
	{
		const int pageSize = 20;
		int skip = pageSize * ((page ?? 1) - 1);
		string.Format(&amp;quot;http://odata.netflix.com/[…]&amp;amp;$skip={0}&amp;amp;$top={1}&amp;quot;, 
            		skip, pageSize);

		var webClient = new WebClient();
		string xmlResult = await webClient.DownloadStringTaskAsync(url);
		return Json(Movie.FromXml(xmlResult), JsonRequestBehavior.AllowGet);
	}
}&lt;/pre&gt;

&lt;p&gt;As you can see in the previous code snippet, in ASP.NET MVC 4 you always should inherits from AsyncController but there is no more naming conventions, no more Async/Completed methods, no more AsyncManager and the action returns a Task&lt;actionresult&gt; instead of an ActionResult !&lt;/p&gt;

&lt;p&gt;It’s awesome !&lt;/p&gt;

&lt;p&gt;Hope this helps &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/234129fb-07df-470d-b429-ee7472ebc124" /&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Thu, 29 Sep 2011 11:03:00 Z</pubDate><a10:updated>2011-09-29T11:03:00Z</a10:updated></item><item><guid isPermaLink="false">aspnet-mvc-4-controleurs-asynchrone</guid><link>http://www.juliencorioland.net/Archives/aspnet-mvc-4-controleurs-asynchrone</link><author>Julien Corioland</author><title>[ASP.NET MVC 4] Contrôleurs asynchrones !</title><description>&lt;p&gt;On le sait, cela a été largement abordé il y a deux semaines lors de la //Build conférence : le futur du développement est dans l’asynchrone.&lt;/p&gt;  &lt;p&gt;Bien entendu, il était déjà possible de réaliser des contrôleur asynchrone dans les versions précédente d’ASP.NET MVC et ce en dérivant nos contrôleurs de la classe AsyncController. Le moteur MVC se basait alors sur des conventions :&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;MonAction&lt;strong&gt;Async&lt;/strong&gt; : méthode qui retourne void et qui démarre un traitement asynchrone &lt;/li&gt;    &lt;li&gt;MonAction&lt;strong&gt;Completed&lt;/strong&gt; : méthode qui retourne un ActionResult (le résultat de l’action MVC “MonAction”, dans ce cas). &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Afin de synchroniser le tout, l’AsyncController fournissait une propriété AsyncManager permettant notamment au moteur MVC de connaître le nombre d’opération asynchrone en cours, de gérer celles-ci et de passer les paramètres à la méthode de retour d’action (MonActionCompleted) à la fin du traitement asynchrone, et ce pour retourner le résultat.&lt;/p&gt;  &lt;p&gt;Par exemple, le contrôleur ci-dessous expose une action GetJSonMovies asynchrone, qui se charge d’aller récupérer une liste de films via un flux OData :&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;public class MoviesController : AsyncController
{
    public ActionResult Index()
    {
        return View();
    }

    public void GetJsonMoviesAsync(int? page)
    {
        const int pageSize = 20;
        int skip = pageSize * ((page ?? 1) - 1);
        string url = string.Format(&amp;quot;http://odata.netflix.com/[…]&amp;amp;$skip={0}&amp;amp;$top={1}&amp;quot;, 
            skip, pageSize);

        //on &amp;quot;déclare&amp;quot; l'opération asynchrone
        AsyncManager.OutstandingOperations.Increment();

        var webClient = new WebClient();
        webClient.DownloadStringCompleted += OnWebClientDownloadStringCompleted;
        webClient.DownloadStringAsync(new Uri(url));//on lance le traitement asynchrone
    }

    private void OnWebClientDownloadStringCompleted(object sender, 
        DownloadStringCompletedEventArgs e)
    {
        //le traitement asynchrone se termine
        //on rajoute le résultat &amp;quot;movies&amp;quot; au paramètre du AsyncManager
        //NB : il s'agit du nom du paramètre de la méthode GetJsonMoviesCompleted !!
        List&amp;lt;Movie&amp;gt; movies = null;
        if (AsyncManager.Parameters.ContainsKey(&amp;quot;movies&amp;quot;))
        {
            movies = (List&amp;lt;Movie&amp;gt;)AsyncManager.Parameters[&amp;quot;movies&amp;quot;];
            movies.Clear();
        }
        else
        {
            movies = new List&amp;lt;Movie&amp;gt;();
            AsyncManager.Parameters[&amp;quot;movies&amp;quot;] = movies;
        }

        movies.AddRange(Movie.FromXml(e.Result));

        //on indique que l'opération se termine (déclenche l'appel du &amp;quot;Action&amp;quot;Completed)
        AsyncManager.OutstandingOperations.Decrement();
    }

    public ActionResult GetJsonMoviesCompleted(List&amp;lt;Movie&amp;gt; movies)
    {
        //on retourne le résultat Json
        return Json(movies, JsonRequestBehavior.AllowGet);
    }
}&lt;/pre&gt;

&lt;p&gt;Nous sommes d’accord, ce code n’est pas extrêment compliqué, mais ASP.NET MVC 4, par le biais des nouveautés de C# 5 et de l’utilisation des mots clés &lt;strong&gt;async&lt;/strong&gt; et &lt;strong&gt;await&lt;/strong&gt; va grandement le simplifier, comme il est possible de le constater ci-dessous :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public class MoviesController : AsyncController
{
	public ActionResult Index()
	{
		return View();
	}

	public async Task&amp;lt;ActionResult&amp;gt; GetJsonMovies(int? page)
	{
		const int pageSize = 20;
		int skip = pageSize * ((page ?? 1) - 1);
		string.Format(&amp;quot;http://odata.netflix.com/[…]&amp;amp;$skip={0}&amp;amp;$top={1}&amp;quot;, 
            		skip, pageSize);

		var webClient = new WebClient();
		string xmlResult = await webClient.DownloadStringTaskAsync(url);
		return Json(Movie.FromXml(xmlResult), JsonRequestBehavior.AllowGet);
	}
}&lt;/pre&gt;

&lt;p&gt;Comme vous pouvez le voir, en ASP.NET MVC 4 on continue à dériver &lt;strong&gt;AsyncController&lt;/strong&gt;, en revanche plus besoin de faire appelle à l’AsyncManager ou même d’utiliser des conventions “Action&lt;strong&gt;Async&lt;/strong&gt;/Action&lt;strong&gt;Completed&lt;/strong&gt;” ! Notez également que la méthode ne retourne plus un ActionResult mais un Task&amp;lt;ActionResult&amp;gt; (induit par l’utilisation de async et await).&lt;/p&gt;

&lt;p&gt;Franchement, ça poutre !&lt;/p&gt;

&lt;p&gt;A bientôt &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/a9dd7904-9ac0-4a5a-be99-fe411038ab4b" /&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Thu, 29 Sep 2011 10:42:00 Z</pubDate><a10:updated>2011-09-29T10:51:25Z</a10:updated></item><item><guid isPermaLink="false">en-aspnet-mvc-handle-http-errors</guid><link>http://www.juliencorioland.net/Archives/en-aspnet-mvc-handle-http-errors</link><author>Julien Corioland</author><title>[EN] [ASP.NET MVC] Handle HTTP errors</title><description>&lt;p&gt;This subject seems to be really basic when developing a web application but it seems that it’s very discussed on the web too. In a lot of forum threads or blog posts we can find very various methods to handle HTTP exception within an ASP.NET MVC application. This post exposes a way to do that. I’m not pretending that it’s the best way but I think that it’s an elegant one &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/82ea65f8-bfc3-4ca3-a380-27907ed6cd55" /&gt; (so don’t hesitate to comment on this post if you’ve remarks or suggestions).&lt;/p&gt;  &lt;h3&gt;What’s the real need ?&lt;/h3&gt;  &lt;p&gt;I think that developers should manage HttpException in an other way than other exceptions for 3 main reasons :&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Respect of web-standards (a not found error returns a 404 http code, that’s it !)&lt;/li&gt;    &lt;li&gt;SEO : search engines use http codes (because it’s standards) to build their indexes.&lt;/li&gt;    &lt;li&gt;The user : a user-friendly page is already better than a stack trace…&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;I think that the most important is that the error code is well returned by the server : for example the URL http://www.mywebsite.com/Pages/some-page-that-does-not-exists is called the server should return a 404 status code and not a 302 (found) followed by a 200 (or 404) from a specific error page like http://www.mywebsite.com/Errors/Error404).&lt;/p&gt;  &lt;h3&gt; When HttpException are thrown in an ASP.NET MVC application ?&lt;/h3&gt;  &lt;p&gt;HttpException are thrown by the ASP.NET MVC engine or by the application code, directly in the asp.net controllers of your apps :&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;throw new HttpException(404, &amp;quot;Not found&amp;quot;);&lt;/pre&gt;

&lt;p&gt;In some cases, they can also be thrown by the server (for internal errors, for example). But all HttpException can be handled in the application.&lt;/p&gt;

&lt;h3&gt;How to handle these exceptions ?&lt;/h3&gt;

&lt;p&gt;After a lot of researches and some tests I think that http exceptions should be handled in two places :&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;In a base controller inherited by all the controllers of the application&lt;/li&gt;

  &lt;li&gt;In the Global.asax file&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;&lt;u&gt;Handle http exception in the controller :&lt;/u&gt;&lt;/h4&gt;

&lt;p&gt;Exceptions that are thrown by the developer directly in the application code can be handle in a base controller. For example :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public ActionResult Product(int id)
{
    var product = _unitOfWork.GetProduct(id);
    if(product == null)
        throw new HttpException(404, &amp;quot;Le produit est introuvable&amp;quot;);
 
    return View(product);
}&lt;/pre&gt;

&lt;p&gt;By inheriting the System.Web.Mvc.Controller class it’s possible to override an “OnException” method. This method is called when an exception occurs in a controller method (action) :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;protected override void OnException(ExceptionContext filterContext) {
    base.OnException(filterContext);
 
    if (filterContext.Exception != null) {
        filterContext.ExceptionHandled = true;
 
        if (filterContext.Exception is HttpException) {
            if (!ControllerContext.RouteData.Values.ContainsKey(&amp;quot;error&amp;quot;)) {
                ControllerContext.RouteData.Values.Add(&amp;quot;error&amp;quot;, filterContext.Exception);
            }
 
            var httpException = (HttpException) filterContext.Exception;
 
            switch (httpException.GetHttpCode()) {
                case 404:
                    filterContext.HttpContext.Response.StatusCode = 404;
                    filterContext.HttpContext.Response.StatusDescription = httpException.Message;
                    View(&amp;quot;Error404&amp;quot;, null).ExecuteResult(ControllerContext);
                    break;
                case 500:
                    filterContext.HttpContext.Response.StatusCode = 500;
                    filterContext.HttpContext.Response.StatusDescription = httpException.Message;
                    View(&amp;quot;Error500&amp;quot;, null).ExecuteResult(ControllerContext);
                    break;
                default:
                    filterContext.HttpContext.Response.StatusDescription = httpException.Message;
                    View(&amp;quot;GenericError&amp;quot;, null).ExecuteResult(ControllerContext);
                    break;
            }
        }
 
        //autre traitement si pas HttpException (log par exemple...)
    }
}&lt;/pre&gt;

&lt;p&gt;As you can see in this code snippet the first step is to check that the exception is an HttpException. If yes, a redirection to an appropriate error page is done just after setting the status code and status description of the http response. The views are in the “Shared” folder of the application.&lt;/p&gt;

&lt;p&gt;Now http exceptions that are thrown in a controller return the good status code and redirect to a user friendly page.&lt;/p&gt;

&lt;h4&gt;&lt;u&gt;Handle HTTP exceptions in the Global.asax :&lt;/u&gt;&lt;/h4&gt;

&lt;p&gt;It’s in the Global.asax file that all other http exception are handled. To to that you should subscribe to the Error event of the mvc application (in the Init method override) :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public override void Init()
{
    base.Init();
    this.Error += new EventHandler(MvcApplication_Error);
}&lt;/pre&gt;

&lt;p&gt;In the Error event handler, initialize an errors-dedicated controller and create appropriate route data to work with the error :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;var routeData = new RouteData();
routeData.Values.Add(&amp;quot;controller&amp;quot;, &amp;quot;Errors&amp;quot;); 
 
var lastException = Server.GetLastError();
 
if (lastException is HttpException) {
    var httpException = (HttpException) lastException;
    switch(httpException.GetHttpCode()) {
        case 404:
            routeData.Values.Add(&amp;quot;action&amp;quot;, &amp;quot;Error404&amp;quot;);
            break;
        case 500:
            routeData.Values.Add(&amp;quot;action&amp;quot;, &amp;quot;Error500&amp;quot;);
            break;
        default:
            routeData.Values.Add(&amp;quot;action&amp;quot;, &amp;quot;GenericError&amp;quot;);
            break;
    }
}
else
{
    routeData.Values.Add(&amp;quot;action&amp;quot;, &amp;quot;GenericError&amp;quot;);
}
 
routeData.Values.Add(&amp;quot;exception&amp;quot;, lastException);
 
Server.ClearError();
 
IController errorController = _unityContainer.Resolve&amp;lt;ErrorsController&amp;gt;();
errorController.Execute(new RequestContext(
new HttpContextWrapper(Context), routeData));&lt;/pre&gt;

&lt;p&gt;The ErrorsController :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public class ErrorsController : Controller
{
    public ActionResult Error404() {
        Response.StatusCode = 404;
        Exception exception = null;
        if(RouteData.Values.ContainsKey(&amp;quot;exception&amp;quot;)) {
            exception = (Exception) RouteData.Values[&amp;quot;exception&amp;quot;];
        }
        return View(exception);
    }
 
    public ActionResult Error500() {
        Response.StatusCode = 500;
        Exception exception = null;
        if (RouteData.Values.ContainsKey(&amp;quot;exception&amp;quot;))
        {
            exception = (Exception)RouteData.Values[&amp;quot;exception&amp;quot;];
        }
        return View(exception);
    }
 
    public ActionResult GenericError() {
        Exception exception = null;
        if (RouteData.Values.ContainsKey(&amp;quot;exception&amp;quot;))
        {
            exception = (Exception)RouteData.Values[&amp;quot;exception&amp;quot;];
        }
        return View(exception);
    }
}&lt;/pre&gt;

&lt;p&gt;As you can see each action defines itself the http status code of the http response.&lt;/p&gt;

&lt;p&gt;Now all http exceptions are handled in a user and SEO friendly way in your ASP.NET MVC applications !&lt;/p&gt;

&lt;p&gt;Hope this helps &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/e89176b7-5f2f-448f-9823-134e65753d2f" /&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Tue, 27 Sep 2011 11:57:00 Z</pubDate><a10:updated>2011-09-27T11:57:00Z</a10:updated></item><item><guid isPermaLink="false">aspnet-mvc-gestion-des-erreurs-http</guid><link>http://www.juliencorioland.net/Archives/aspnet-mvc-gestion-des-erreurs-http</link><author>Julien Corioland</author><title>[ASP.NET MVC] Gestion des erreurs HTTP</title><description>&lt;p&gt;Bien que cela soit un sujet qui paraissent essentiel et surtout “basic” lorsque l’on développe une application web (avec ASP.NET MVC ou pas d’ailleurs), ce sujet semble avoir fait couler beaucoup d’encre sur le web ! (si je puis dire…). En effet, il semble qu’il règne une sorte de “flou” autour de ce sujet ou chacun utilise une méthode différente pour gérer les erreurs HTTP. Je ne sais pas si on peut parler ici de “bonne pratique” mais en tout cas, ce post présente un moyen que je juge efficace et suffisament générique pour gérer les Http Exception dans une application ASP.NET MVC ! (n’hésitez pas à me faire par de vos remarques / suggestions en commentaire &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/3ce1b146-665b-440a-b9be-fd76de65ecbb" /&gt;)&lt;/p&gt;  &lt;h3&gt;Quel est le réel besoin ?&lt;/h3&gt;  &lt;p&gt;Au final, pourquoi souhaite-t-on traiter les erreurs HTTP de manière différente des autres erreurs ? A mon humble avis, pour 3 raisons principalement :&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Le respect des standards (une page qui n’existe pas c’est un 404, c’est tout !) &lt;/li&gt;    &lt;li&gt;La SEO : les moteurs de recherche utilisent tous les codes HTTP (comme c’est standard) pour construire leurs index. &lt;/li&gt;    &lt;li&gt;L’utilisateur : on souhaite en général personnaliser la page d’erreur sur laquel arrive l’utilisateur ! &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;Ce qui importe donc, c’est avant tout que le code d’erreur soit bien renvoyé : si j’appelle une URL “http://www.monsite.com/Pages/Page-qui-n-existe-pas” je souhaite que ce soit bien cette page là qui me retourne un code de status 404 et non pas un code 302 (trouvé) suivi d’un 200 (ou 404, à la limite, mais non) sur une URL de type “http://www.monsite.com/Errors/Error404”. &lt;/p&gt;  &lt;h3&gt;Quand est-ce que des HttpException sont levées en ASP.NET MVC ?&lt;/h3&gt;  &lt;p&gt;Les exceptions sont levées soit par le moteur ASP.NET MVC, soit dans votre code :&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;throw new HttpException(404, &amp;quot;Not found&amp;quot;);&lt;/pre&gt;

&lt;p&gt;Elles peuvent également être levées au niveau du serveur (erreur interne 500, par exemple), mais dans tous les cas, il est possible de les gérer !&lt;/p&gt;

&lt;h3&gt;Comment gérer ces exceptions ?&lt;/h3&gt;

&lt;p&gt;Après pas mal de recherches et de tests, je pense qu’il y a deux endroits où il faut gérer les exceptions :&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Dans un contrôleur de base dérivé par tous les contrôleurs d’une application ASP.NET MVC &lt;/li&gt;

  &lt;li&gt;Dans le fichier Global.asax &lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;&lt;u&gt;Gestion des erreurs dans un contrôleur de base :&lt;/u&gt;&lt;/h4&gt;

&lt;p&gt;Au sein du contrôleur, on gèrera les exceptions qui ont été levées intentionnellement par le développeur. Par exemple :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public ActionResult Product(int id)
{
    var product = _unitOfWork.GetProduct(id);
    if(product == null)
        throw new HttpException(404, &amp;quot;Le produit est introuvable&amp;quot;);

    return View(product);
}&lt;/pre&gt;

&lt;p&gt;Lorsque l’on dérive la classe System.Web.Mvc.Controller, il est possible de surcharger une méthode “OnException” : celle-ci est appelée lorsqu’une exception est levée dans le contrôleur.&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;protected override void OnException(ExceptionContext filterContext) {
    base.OnException(filterContext);

    if (filterContext.Exception != null) {
        filterContext.ExceptionHandled = true;

        if (filterContext.Exception is HttpException) {
            if (!ControllerContext.RouteData.Values.ContainsKey(&amp;quot;error&amp;quot;)) {
                ControllerContext.RouteData.Values.Add(&amp;quot;error&amp;quot;, filterContext.Exception);
            }

            var httpException = (HttpException) filterContext.Exception;

            switch (httpException.GetHttpCode()) {
                case 404:
                    filterContext.HttpContext.Response.StatusCode = 404;
                    filterContext.HttpContext.Response.StatusDescription = httpException.Message;
                    View(&amp;quot;Error404&amp;quot;, null).ExecuteResult(ControllerContext);
                    break;
                case 500:
                    filterContext.HttpContext.Response.StatusCode = 500;
                    filterContext.HttpContext.Response.StatusDescription = httpException.Message;
                    View(&amp;quot;Error500&amp;quot;, null).ExecuteResult(ControllerContext);
                    break;
                default:
                    filterContext.HttpContext.Response.StatusDescription = httpException.Message;
                    View(&amp;quot;GenericError&amp;quot;, null).ExecuteResult(ControllerContext);
                    break;
            }
        }

        //autre traitement si pas HttpException (log par exemple...)
    }
}&lt;/pre&gt;

&lt;p&gt;Comme vous pouvez le constater, ce code vérifie si l’exception est de type HttpException, si c’est le cas, elle redirige vers une vue d’erreur après avoir renseigner un status code et un status description au niveau de la réponse.&lt;/p&gt;

&lt;p&gt;NB : ces vues sont placées dans le dossier “Shared” des vues partagées.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;&lt;strong&gt;Premier objectif atteind :&lt;/strong&gt;&lt;/u&gt; une erreur HTTP levée dans un contrôleur se traduit par une page d’erreur “user friendly” et une réponse HTTP “SEO/Standards friendly” puisque retournant le bon code HTTP sur la bonne URL !&lt;/p&gt;

&lt;h4&gt;&lt;u&gt;Gestion des erreurs HTTP dans le Global.asax :&lt;/u&gt;&lt;/h4&gt;

&lt;p&gt;Dans le Global.asax, on va gérer toutes les autres erreurs HTTP qui pourraient être levées dans l’application. &lt;/p&gt;

&lt;p&gt;Pour se faire, on surcharge d’abord la méthode &lt;strong&gt;Init&lt;/strong&gt; afin de s’abonner à l’événement &lt;strong&gt;Error&lt;/strong&gt; :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public override void Init()
{
    base.Init();
    this.Error += new EventHandler(MvcApplication_Error);
}&lt;/pre&gt;

&lt;p&gt;Dans la méthode &lt;strong&gt;MvcApplication_Error&lt;/strong&gt;, on récupère la dernière erreur du serveur, on vérifie si celle-ci est de type HttpException. Si oui, on instancie un contrôleur spécialisé dans la gestion des erreurs (ici ErrorsController, détaillé plus bas) on exécute le rendu de la vue en lui passant les bonnes RouteData :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;var routeData = new RouteData();
routeData.Values.Add(&amp;quot;controller&amp;quot;, &amp;quot;Errors&amp;quot;); 

var lastException = Server.GetLastError();

if (lastException is HttpException) {
    var httpException = (HttpException) lastException;
    switch(httpException.GetHttpCode()) {
        case 404:
            routeData.Values.Add(&amp;quot;action&amp;quot;, &amp;quot;Error404&amp;quot;);
            break;
        case 500:
            routeData.Values.Add(&amp;quot;action&amp;quot;, &amp;quot;Error500&amp;quot;);
            break;
        default:
            routeData.Values.Add(&amp;quot;action&amp;quot;, &amp;quot;GenericError&amp;quot;);
            break;
    }
}
else
{
    routeData.Values.Add(&amp;quot;action&amp;quot;, &amp;quot;GenericError&amp;quot;);
}

routeData.Values.Add(&amp;quot;exception&amp;quot;, lastException);

Server.ClearError();

IController errorController = _unityContainer.Resolve&amp;lt;ErrorsController&amp;gt;();
errorController.Execute(new RequestContext(
new HttpContextWrapper(Context), routeData));&lt;/pre&gt;

&lt;p&gt;Voilà le code du contrôleur &lt;strong&gt;ErrorsController&lt;/strong&gt; :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public class ErrorsController : Controller
{
    public ActionResult Error404() {
        Response.StatusCode = 404;
        Exception exception = null;
        if(RouteData.Values.ContainsKey(&amp;quot;exception&amp;quot;)) {
            exception = (Exception) RouteData.Values[&amp;quot;exception&amp;quot;];
        }
        return View(exception);
    }

    public ActionResult Error500() {
        Response.StatusCode = 500;
        Exception exception = null;
        if (RouteData.Values.ContainsKey(&amp;quot;exception&amp;quot;))
        {
            exception = (Exception)RouteData.Values[&amp;quot;exception&amp;quot;];
        }
        return View(exception);
    }

    public ActionResult GenericError() {
        Exception exception = null;
        if (RouteData.Values.ContainsKey(&amp;quot;exception&amp;quot;))
        {
            exception = (Exception)RouteData.Values[&amp;quot;exception&amp;quot;];
        }
        return View(exception);
    }
}&lt;/pre&gt;

&lt;p&gt;NB : notez que c’est dans chaque action que l’on défini le status code de la réponse HTTP !&lt;/p&gt;

&lt;p&gt;Je vous passe le code des vues qui n’a pas grand intérêt (mise à part que l’on peut réutiliser ici les vues du dossier “Shared” évidemment)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;Deuxième objectif atteind :&lt;/u&gt;&lt;/strong&gt; une erreur HTTP levée autre part que dans un contrôleur se traduit par une page d’erreur “user friendly” et une réponse HTTP “SEO/Standards friendly” puisque retournant le bon code HTTP sur la bonne URL !&lt;/p&gt;

&lt;p&gt;A bientôt ! &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/e66b5970-7a8a-4db8-ad7b-abcf794a1208" /&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Tue, 27 Sep 2011 11:19:00 Z</pubDate><a10:updated>2011-09-27T11:45:11Z</a10:updated></item><item><guid isPermaLink="false">enentity-framework-database-first-approach-with-ef-41</guid><link>http://www.juliencorioland.net/Archives/enentity-framework-database-first-approach-with-ef-41</link><author>Julien Corioland</author><title>[EN][Entity Framework] Database first approach with EF 4.1</title><description>&lt;p&gt;As you probably already know the last version of Entity Framework (4.1) supports a new development scenario called “Code first” (also called code only in some cases). It consists to develop the .NET entities (POCOs) before the SQL relational model, create the database, map these entities to table and columns at runtime according the class definitions. To do that, there is no need of an EDMX file, metadata and mapping are based on conventions and attributes.&lt;/p&gt;  &lt;p&gt;Entity Framework 4.1 provides a lot of new functionalities that may be used even if the “code first” approach is not applied for many reasons (existing database, database created and maintained by a DBA that doesn’t know .NET or Entity Framework…). To be able to use the new features of Entity Framework 4.1 it’s possible to continue with the database first approach and use an EDMX file to do the mapping between .NET objects (POCOs) and SQL tables and columns. This post explains how to do that !&lt;/p&gt;  &lt;h3&gt;Import the Entity Framework 4.1 library&lt;/h3&gt;  &lt;p&gt;To use EF 4.1 it’s recommended to reference the related NuGet package using the NuGet management console in Visual Studio. Right click on the project that should reference the library and choose Manage NuGet packages…&lt;/p&gt;  &lt;p&gt;&lt;img style="display: block; float: none; margin-left: auto; margin-right: auto" src="https://juliencoriolandblog.blob.core.windows.net/medias/8140a49c-8268-4797-a240-543039f14abd" /&gt;&lt;/p&gt;  &lt;p&gt;In the NuGet management console, search for “Entity Framework” and click Install to download and reference the EntityFramework.dll library.&lt;/p&gt;  &lt;p&gt;&lt;img style="display: block; float: none; margin-left: auto; margin-right: auto" src="https://juliencoriolandblog.blob.core.windows.net/medias/aa0a2412-5933-4612-86e8-8de2cd6c5b92" width="600" height="338" /&gt;&lt;/p&gt;  &lt;h3&gt;Download the T4 code generation templates for EF 4.1&lt;/h3&gt;  &lt;p&gt;To generate the DbContext and the .NET entities from the EDMX definition it’s possible to download two code generation templates from the Visual Studio Extensions manager. Open it and search for “DbContext” in the online gallery :&lt;/p&gt;  &lt;p&gt;&lt;img style="display: block; float: none; margin-left: auto; margin-right: auto" src="https://juliencoriolandblog.blob.core.windows.net/medias/0c9bd5f7-54dd-4c6f-8d8f-efd5ae66f619" width="600" height="326" /&gt;&lt;/p&gt;  &lt;p&gt;Install the “ADO.NET C# DbContext Generator” item (or VB.NET).&lt;/p&gt;  &lt;h3&gt;Delete the code generation tool from EDMX properties&lt;/h3&gt;  &lt;p&gt;When an ADO.NET entity data model is added to a project, it uses the default code generation template. To avoid this, you have to remove this tool in the property window of the EDMX file :&lt;/p&gt;  &lt;p&gt;&lt;img style="display: block; float: none; margin-left: auto; margin-right: auto" src="https://juliencoriolandblog.blob.core.windows.net/medias/31e06249-66bd-419b-951b-366ece363d4d" width="400" height="215" /&gt;&lt;/p&gt;  &lt;p&gt;Be careful if you’re working on an existing project where generated code may be in use !&lt;/p&gt;  &lt;h3&gt;Add the templates to the project&lt;/h3&gt;  &lt;p&gt;Now you’ve to add the two generation code templates to the project. To do that add a new item of type “ADO.NET C# DbContext Generator” from the add item menu :&lt;/p&gt;  &lt;p&gt;&lt;img style="display: block; float: none; margin-left: auto; margin-right: auto" src="https://juliencoriolandblog.blob.core.windows.net/medias/9ab72d8f-27c5-4a79-8f36-a60e47518c30" width="600" height="313" /&gt;&lt;/p&gt;  &lt;p&gt;In these two files, find the text $edmxInputFile and replace it by the relative path to the EDMX file. &lt;/p&gt;  &lt;p&gt;&lt;img style="display: block; float: none; margin-left: auto; margin-right: auto" src="https://juliencoriolandblog.blob.core.windows.net/medias/6d088850-685e-40d8-9165-aab0eaed284f" width="400" height="97" /&gt;&lt;/p&gt;  &lt;p&gt;Save the files. Automatically the DbContext and the entities are generated :&lt;/p&gt;  &lt;p&gt;&lt;img style="display: block; float: none; margin-left: auto; margin-right: auto" src="https://juliencoriolandblog.blob.core.windows.net/medias/247b9026-a277-4d5e-b405-6a320aec9f9c" /&gt;&lt;/p&gt;  &lt;p&gt;Now you are ready to use the DbContext and query the database through LINQ to Entites !&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;using (var dbContext = new BlogContainer()) {
    var posts = dbContext.Posts.Where(p =&amp;gt; p.IsPublished).ToList();

    return View(posts);
}&lt;/pre&gt;

&lt;p&gt;After each change on the edmx file just run the custom tool on the two T4 templates to regenerate the DbContext and the entities.&lt;/p&gt;

&lt;p&gt;Hope this helps &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/dddea8f5-5156-45bc-bbb4-62d90d6d517b" /&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Thu, 22 Sep 2011 11:48:00 Z</pubDate><a10:updated>2011-09-22T11:48:00Z</a10:updated></item><item><guid isPermaLink="false">entity-framework-continuer-a-utiliser-un-edmx-avec-ef-41</guid><link>http://www.juliencorioland.net/Archives/entity-framework-continuer-a-utiliser-un-edmx-avec-ef-41</link><author>Julien Corioland</author><title>[Entity Framework] Continuer à utiliser un EDMX avec EF 4.1</title><description>&lt;p&gt;Le titre de ce post vous a peut-être suppris puisque l’une des nouveautés la plus plébicitée de Entity Framework 4.1 est la possibilité de s’affranchir d’un modèle EDMX et d’adopter une stratégie Code First : les entités .NET permettent de générer le modèle de données SQL.&lt;/p&gt;  &lt;p&gt;Pourtant, Entity Framework 4.1 apporte de nombreuses autres nouveautés, notamment en terme de gestion de l’état des entités, la mise à jour d’entités dans un contexte state-less (notamment), de requêtage… via des méthodes beaucoup plus simples que précédement.&lt;/p&gt;  &lt;p&gt;Ce post à pour but d’expliquer comment utiliser Entity Framework 4.1 tout en continuant d’utiliser un EDMX et une stratégie Database First !&lt;/p&gt;  &lt;h3&gt;Importer la librairie Entity Framework 4.1&lt;/h3&gt;  &lt;p&gt;Pour pouvoir utiliser EF 4.1, le plus simple est de référencer le package NuGet associé. Faites un clic droit sur votre projet Visual Studio puis cliqué sur “Manage NuGet packages…” :&lt;/p&gt;  &lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/8140a49c-8268-4797-a240-543039f14abd" rel="lightbox"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="EF41_01" border="0" alt="EF41_01" src="https://juliencoriolandblog.blob.core.windows.net/medias/447cc3fb-3433-4d21-897b-94f433a9ff47" width="445" height="81" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Dans la fenêtre qui s’affiche, recherchez Entity Framework :&lt;/p&gt;  &lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/aa0a2412-5933-4612-86e8-8de2cd6c5b92" rel="lightbox"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="EF41_02" border="0" alt="EF41_02" src="https://juliencoriolandblog.blob.core.windows.net/medias/2446feb7-80d6-44fb-89a4-479ba373a3fe" width="604" height="342" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Cliquez sur “Install”, acceptez les conditions. La librairie EntityFramework.dll va être téléchargée et référencée dans le projet.&lt;/p&gt;  &lt;h3&gt;Télécharger les templates de génération de code (T4) pour EF 4.1&lt;/h3&gt;  &lt;p&gt;Afin de générer le code propre à EF 4.1 (entités et DbContext) il est possible de télécharger un template d’item via le gestionnaire d’extension de Visual Studio. Placez-vous dans la galerie en ligne et recherchez DbContext :&lt;/p&gt;  &lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/0c9bd5f7-54dd-4c6f-8d8f-efd5ae66f619" rel="lightbox"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="EF41_03" border="0" alt="EF41_03" src="https://juliencoriolandblog.blob.core.windows.net/medias/e34f820b-62d5-4de1-8a69-49069d114533" width="604" height="332" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Installez l’élément “ADO.NET C# DbContext Generator” (ou VB si vous le souhaitez &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/c404a2ee-ac89-4d06-a38b-a49f6dcf656c" /&gt;).&lt;/p&gt;  &lt;h5&gt;&lt;/h5&gt;  &lt;h3&gt;Supprimer l’outil de génération de code de votre EDMX&lt;/h3&gt;  &lt;p&gt;Afin que le code ne soit plus généré par l’EDMX, éditez les propriétés de ce dernier et supprimer l’outil personnalisé :&lt;/p&gt;  &lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/31e06249-66bd-419b-951b-366ece363d4d" rel="lightbox"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="EF41_04" border="0" alt="EF41_04" src="https://juliencoriolandblog.blob.core.windows.net/medias/14a9c266-5a17-4ed7-bbc1-126d0037da7d" width="404" height="219" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Attention :&lt;/u&gt;&lt;/strong&gt; ne faites pas celà dans un projet qui utilise les éléments de code généré par l’EDMX Entity Framework “classique”.&lt;/p&gt;  &lt;h3&gt;Ajouter les templates au projet&lt;/h3&gt;  &lt;p&gt;Maintenant, ajoutez un nouvel item de type “ADO.NET C# DbContext Generator” au projet.&lt;/p&gt;  &lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/9ab72d8f-27c5-4a79-8f36-a60e47518c30" rel="lightbox"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="EF41_05" border="0" alt="EF41_05" src="https://juliencoriolandblog.blob.core.windows.net/medias/9788f498-47da-4d8c-b7bb-dc0cc4b7a8cb" width="604" height="316" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Identifiez la ligne de code suivante dans chacun des templates et modifiez la chaine $edmxInputFile$ par le chemin relatif vers votre fichier EDMX.&lt;/p&gt;  &lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/6d088850-685e-40d8-9165-aab0eaed284f" rel="lightbox"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="EF41_06" border="0" alt="EF41_06" src="https://juliencoriolandblog.blob.core.windows.net/medias/afbee5e4-5ad4-49f8-86ac-c9f88bc9e4e1" width="354" height="89" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Dès lors, lorsque vous enregistrez les templates, du code devrait être généré : les entités et le DbContext.&lt;/p&gt;  &lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/247b9026-a277-4d5e-b405-6a320aec9f9c" rel="lightbox"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="EF41_07" border="0" alt="EF41_07" src="https://juliencoriolandblog.blob.core.windows.net/medias/5b7bb9b6-c3d3-418f-b107-7404fed92edf" width="304" height="336" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Et voilà, vous pouvez utilisez le contexte généré pour attaquer votre base de données ! &lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;using (var dbContext = new BlogContainer()) {
    var posts = dbContext.Posts.Where(p =&amp;gt; p.IsPublished).ToList();

    return View(posts);
}&lt;/pre&gt;

&lt;p&gt;Après modification de l’EDMX, il suffit de regénérer les templates T4 pour les mettre à jour et de recompiler le tout &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/c404a2ee-ac89-4d06-a38b-a49f6dcf656c" /&gt;&lt;/p&gt;

&lt;p&gt;A bientôt &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/24d6543a-2ef6-4171-925d-1cd87f55ed7c" /&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Mon, 19 Sep 2011 10:50:00 Z</pubDate><a10:updated>2011-09-19T10:31:54Z</a10:updated></item><item><guid isPermaLink="false">en-aspnet-mvc-custom-output-cache-provider</guid><link>http://www.juliencorioland.net/Archives/en-aspnet-mvc-custom-output-cache-provider</link><author>Julien Corioland</author><title>[EN] [ASP.NET MVC] Custom output cache provider</title><description>&lt;p&gt;Since ASP.NET 4 was released it’s possible to write custom output cache providers to change the way data are cached. For example, one of my customer wanted that some actions were cached on the file system to avoid loss of cache during an application pool reboot.&lt;/p&gt;  &lt;p&gt;Be careful by using the following code because in many scenarios it can’t be used “as it is” (in a web farm, for example). &lt;/p&gt;  &lt;p&gt;A custom output cache provider is a simple class that inherits from &lt;strong&gt;OutputCacheProvider&lt;/strong&gt; and overrides these methods :&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;Add&lt;/strong&gt; : adds an entry in the cache &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Get&lt;/strong&gt; : gets an entry from the cache &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Remove&lt;/strong&gt; : removes an entry from the cache &lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Set&lt;/strong&gt; : updates an entry in the cache &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Cached items must be serializable. In this sample, the following class will be used :&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;[Serializable]
public class CachedItem
{
    public object Item { get; set; }
    public DateTime UtcExpiry { get; set; }
}&lt;/pre&gt;

&lt;p&gt;In order to save an item on the file system the two following methods will be used. The first allows to get a file path from the cache key (generated by asp.net) and the second allows to save a CachedItem on the disk :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;private string GetFilePathFromKey(string key)
{
    foreach (var invalidChar in Path.GetInvalidFileNameChars())
        key = key.Replace(invalidChar, '_');

    return Path.Combine(CacheDirectory, key);
}

private void SaveCachedItem(CachedItem cachedItem, string filePath)
{
    if (File.Exists(filePath))
        File.Delete(filePath);

    using (var stream = File.OpenWrite(filePath))
    {
        var binaryFormatter = new BinaryFormatter();
        binaryFormatter.Serialize(stream, cachedItem);
    }
}&lt;/pre&gt;

&lt;p&gt;&lt;u&gt;The Add method :&lt;/u&gt;&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public override object Add(string key, object entry, DateTime utcExpiry)
{
    string filePath = GetFilePathFromKey(key);
    var cachedItem = GetCachedItem(filePath);

    if (cachedItem != null &amp;amp;&amp;amp; cachedItem.UtcExpiry.ToUniversalTime() &amp;lt;= DateTime.UtcNow)
    {
        Remove(key);
    }
    else if (cachedItem != null)
    {
        return cachedItem.Item;
    }

    SaveCachedItem(new CachedItem()
    {
        Item = entry,
        UtcExpiry = utcExpiry
    }, filePath);

    return entry;
}&lt;/pre&gt;

&lt;p&gt;&lt;u&gt;The Get method :&lt;/u&gt;&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public override object Get(string key)
{
    string filePath = GetFilePathFromKey(key);
    var cachedItem = GetCachedItem(filePath);
    if (cachedItem != null)
    {
        if (cachedItem.UtcExpiry.ToUniversalTime() &amp;lt;= DateTime.UtcNow)
        {
            Remove(key);
        }
        else
        {
            return cachedItem.Item;
        }
    }

    return null;
}&lt;/pre&gt;

&lt;p&gt;&lt;u&gt;The Remove method :&lt;/u&gt;&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public override void Remove(string key)
{
    string filePath = GetFilePathFromKey(key);
    if (File.Exists(filePath))
        File.Delete(filePath);
}&lt;/pre&gt;

&lt;p&gt;&lt;u&gt;The Set Method :&lt;/u&gt;&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public override void Set(string key, object entry, DateTime utcExpiry) {
    string filePath = GetFilePathFromKey(key);
    var cachedItem = new CachedItem() {Item = entry, UtcExpiry = utcExpiry};
    SaveCachedItem(cachedItem, filePath);
}&lt;/pre&gt;

&lt;p&gt;Now that the output cache provider is created, it can be registered in the web.config file of the application, in the &lt;strong&gt;caching&lt;/strong&gt; section :&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;&amp;lt;system.web&amp;gt;
&amp;lt;caching&amp;gt;
  &amp;lt;outputCache&amp;gt;
    &amp;lt;providers&amp;gt;
      &amp;lt;add name=&amp;quot;FileSystemOutputCacheProvider&amp;quot;
           type=&amp;quot;Samples.FileSystemOutputCacheProvider, Samples&amp;quot;/&amp;gt;
    &amp;lt;/providers&amp;gt;
  &amp;lt;/outputCache&amp;gt;
&amp;lt;/caching&amp;gt;&lt;/pre&gt;

&lt;p&gt;The last step consists to override the &lt;strong&gt;GetOutputCacheProviderName&lt;/strong&gt; method in the Global.asax file. Actually this method is called by the ASP.NET Framework when an OutputCache attribute (or an OutputCache directive) is used. The method returns the name of the output cache provider to use in the current context. For example, if you want to use the FileSystemOutputCacheProvider for the Details action of the Products controller, you can use this code :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public override string GetOutputCacheProviderName(HttpContext context)
{
    if (context.Request.RawUrl.ToUpper().Contains(&amp;quot;PRODUCTS/DETAILS&amp;quot;)) {
        return &amp;quot;FileSystemOutputCacheProvider&amp;quot;;
    }
    return base.GetOutputCacheProviderName(context);
}&lt;/pre&gt;

&lt;p&gt;As mentioned previously you just have to set an OutputCache attribute on the action Details of the Products controller to use the custom output cache provider :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public class ProductsController : Controller
{
    [OutputCache(Duration = 3600, VaryByParam = &amp;quot;id&amp;quot;)]
    public ActionResult Details(int id)
    {
        //long operation
        Thread.Sleep(2000);

        ViewBag.ProductId = id;
        return View();
    }
}&lt;/pre&gt;

&lt;p&gt;&lt;a href="http://juliencoriolandblog.blob.core.windows.net/publicfiles/SampleOutputCacheProvider.zip" target="_blank"&gt;Download the source code here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope this helps &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/50cfe4ee-2887-4f52-8f11-e17f9521a233" /&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Tue, 13 Sep 2011 11:42:00 Z</pubDate><a10:updated>2011-09-13T11:44:17Z</a10:updated></item><item><guid isPermaLink="false">aspnet-mvc-fournisseur-de-cache-de-sortie-personnalise</guid><link>http://www.juliencorioland.net/Archives/aspnet-mvc-fournisseur-de-cache-de-sortie-personnalise</link><author>Julien Corioland</author><title>[ASP.NET MVC] Fournisseur de cache de sortie personnalisé</title><description>&lt;p&gt;ASP.NET 4.0 apporte de nombreuses nouveautés et notamment la possibilité de développer son propre fournisseur de cache de sortie.&lt;/p&gt;  &lt;p&gt;Pour des besoins bien particulier, un de mes clients avait besoin de générer un grand nombre de page dynamique : le problème étant que ces pages faisaient appel à énormément de ressources et donc la génération prenait un temps fou.&lt;/p&gt;  &lt;p&gt;Pour résoudre le problème, j’ai proposé de faire un fournisseur de cache de sortie personnalisé permettant de stocker la réponse HTTP sur le FileSystem afin de pouvoir la renvoyer plus rapidement. Cette solution est assez intéressante car le cache reste disponible même si le pool d’application ASP.NET est coupé. En revanche, elle n’est pas optimale dans un cas multi-serveur avec un répartiteur de charge en amont. &lt;u&gt;Bref, à ne pas utiliser “les yeux fermés”&lt;/u&gt; &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/666d8c66-c848-4282-bca8-606428d382cb" /&gt;&lt;/p&gt;  &lt;h3&gt;Création du fournisseur de cache de sortie personnalisé&lt;/h3&gt;  &lt;p&gt;Pour créer un fournisseur de cache de sortie personnalisé, il suffit de dériver de la classe abstraite OutputCacheProvider et de redéfinir les méthodes suivantes :&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;public class FileSystemOutputCacheProvider : OutputCacheProvider
{
    public override object Add(string key, object entry, DateTime utcExpiry)
    {
        throw new NotImplementedException();
    }

    public override object Get(string key)
    {
        throw new NotImplementedException();
    }

    public override void Remove(string key)
    {
        throw new NotImplementedException();
    }

    public override void Set(string key, object entry, DateTime utcExpiry)
    {
        throw new NotImplementedException();
    }
}&lt;/pre&gt;

&lt;p&gt;Voilà un bref descriptif des méthodes :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Add&lt;/strong&gt; : permet d’ajouter un élément identifié par une clé unique dans le cache &lt;/li&gt;

  &lt;li&gt;&lt;strong&gt;Get&lt;/strong&gt; : permet de récupérer un élément dans le cache &lt;/li&gt;

  &lt;li&gt;&lt;strong&gt;Remove&lt;/strong&gt; : permet de supprimer un élément du cache &lt;/li&gt;

  &lt;li&gt;&lt;strong&gt;Set&lt;/strong&gt; : permet de mettre à jour un élément dans le cache &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Les éléments mis en cache seront de type “CachedItem” :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;[Serializable]
public class CachedItem
{
    public object Item { get; set; }
    public DateTime UtcExpiry { get; set; }
}&lt;/pre&gt;

&lt;p&gt;Nous utiliserons également deux méthodes permettant de générer un chemin de fichier à partir de la clé passé par le moteur ASP.NET MVC et de sauvegarder un CachedItem sur le disque :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;private void SaveCachedItem(CachedItem cachedItem, string filePath)
{
    if (File.Exists(filePath))
        File.Delete(filePath);

    using (var stream = File.OpenWrite(filePath))
    {
        var binaryFormatter = new BinaryFormatter();
        binaryFormatter.Serialize(stream, cachedItem);
    }
}

private string GetFilePathFromKey(string key)
{
    foreach (var invalidChar in Path.GetInvalidFileNameChars())
        key = key.Replace(invalidChar, '_');

    return Path.Combine(CacheDirectory, key);
}&lt;/pre&gt;

&lt;p&gt;&lt;u&gt;Méthode Add :&lt;/u&gt;&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public override object Add(string key, object entry, DateTime utcExpiry)
{
    string filePath = GetFilePathFromKey(key);
    var cachedItem = GetCachedItem(filePath);

    if (cachedItem != null &amp;amp;&amp;amp; cachedItem.UtcExpiry.ToUniversalTime() &amp;lt;= DateTime.UtcNow)
    {
        Remove(key);
    }
    else if (cachedItem != null)
    {
        return cachedItem.Item;
    }

    SaveCachedItem(new CachedItem()
    {
        Item = entry,
        UtcExpiry = utcExpiry
    }, filePath);

    return entry;
}&lt;/pre&gt;

&lt;p&gt;&lt;u&gt;Méthode Get :&lt;/u&gt;&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public override object Get(string key)
{
    string filePath = GetFilePathFromKey(key);
    var cachedItem = GetCachedItem(filePath);
    if (cachedItem != null)
    {
        if (cachedItem.UtcExpiry.ToUniversalTime() &amp;lt;= DateTime.UtcNow)
        {
            Remove(key);
        }
        else
        {
            return cachedItem.Item;
        }
    }

    return null;
}&lt;/pre&gt;

&lt;p&gt;&lt;u&gt;Méthode Remove :&lt;/u&gt;&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public override void Remove(string key)
{
    string filePath = GetFilePathFromKey(key);
    if (File.Exists(filePath))
        File.Delete(filePath);
}&lt;/pre&gt;

&lt;p&gt;&lt;u&gt;Méthode Set :&lt;/u&gt;&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public override void Set(string key, object entry, DateTime utcExpiry) {
    string filePath = GetFilePathFromKey(key);
    var cachedItem = new CachedItem() {Item = entry, UtcExpiry = utcExpiry};
    SaveCachedItem(cachedItem, filePath);
}&lt;/pre&gt;

&lt;h3&gt;Enregistrement du fournisseur de cache de sortie personnalisé&lt;/h3&gt;

&lt;p&gt;L’enregistrement d’un fournisseur de cache de sortie se fait en deux étapes : dans le Web.config et dans le Global.asax.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;Fichier Web.config :&lt;/u&gt;&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;&amp;lt;system.web&amp;gt;

&amp;lt;caching&amp;gt;
  &amp;lt;outputCache&amp;gt;
    &amp;lt;providers&amp;gt;
      &amp;lt;add name=&amp;quot;FileSystemOutputCacheProvider&amp;quot; 
	type=&amp;quot;Samples.FileSystemOutputCacheProvider, Samples&amp;quot;/&amp;gt;
    &amp;lt;/providers&amp;gt;
  &amp;lt;/outputCache&amp;gt;
&amp;lt;/caching&amp;gt;&lt;/pre&gt;

&lt;p&gt;&lt;u&gt;Global.asax :&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;&lt;font size="2"&gt;Il faut surcharger la méthode &lt;strong&gt;GetOutputCacheProviderName&lt;/strong&gt; qui est en charge de retourner le fournisseur contextuel à la requête. Par exemple, si l’on souhaite utiliser le FileSystemCacheProvider que sur le contrôleur “Products”, on écrira :&lt;/font&gt;&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public override string GetOutputCacheProviderName(HttpContext context)
{
    if (context.Request.RawUrl.ToUpper().Contains(&amp;quot;PRODUCTS/DETAILS&amp;quot;)) {
        return &amp;quot;FileSystemOutputCacheProvider&amp;quot;;
    }
    return base.GetOutputCacheProviderName(context);
}&lt;/pre&gt;

&lt;h5&gt;
  &lt;br /&gt;&lt;/h5&gt;

&lt;h3&gt;Utilisation du fournisseur de cache de sortie personnalisé en ASP.NET MVC&lt;/h3&gt;

&lt;p&gt;Pour utiliser le nouveau fournisseur de cache, il suffit d’utiliser l’attribut &amp;quot;OutputCache” comme à son habitude. C’est le Framework ASP.NET qui se chargera d’instancier le bon fournisseur de cache au bon moment &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/666d8c66-c848-4282-bca8-606428d382cb" /&gt;&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public class ProductsController : Controller
{
    [OutputCache(Duration = 3600, VaryByParam = &amp;quot;id&amp;quot;)]
    public ActionResult Details(int id)
    {
        //long operation
        Thread.Sleep(2000);

        ViewBag.ProductId = id;
        return View();
    }
}&lt;/pre&gt;

&lt;p&gt;NB : le fait de préciser ici le paramètre VaryByParam = “id” permet de faire en sorte que le moteur MVC génère une clé différente par id, est donc implicitement que le FileSystemOutputCacheProvider génère un fichier par id !&lt;/p&gt;

&lt;p&gt;&lt;a href="http://juliencoriolandblog.blob.core.windows.net/publicfiles/SampleOutputCacheProvider.zip" target="_blank"&gt;Sources de l'exemple&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A bientôt &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/ded6ef76-614f-4147-b652-66bd87ce42a6" /&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Tue, 13 Sep 2011 07:18:00 Z</pubDate><a10:updated>2011-09-13T07:21:03Z</a10:updated></item><item><guid isPermaLink="false">en-enable-or-disable-ie-proxy-using-powershell</guid><link>http://www.juliencorioland.net/Archives/en-enable-or-disable-ie-proxy-using-powershell</link><author>Julien Corioland</author><title>[En] Enable or disable IE proxy using Powershell</title><description>&lt;p&gt;I’m currently working in a company where I have to set up a proxy to connect my laptop to the Internet. Because I'm fed up to enable it each morning and disable it each evening, I made two Powershell scripts to do these operations :&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;EnableProxy.ps1 :&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;pre class="brush: ps;"&gt;set-itemproperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' 
-name ProxyEnable -value 1&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;DisableProxy.ps1 :&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;pre class="brush: ps;"&gt;set-itemproperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' 
-name ProxyEnable -value 0 &lt;/pre&gt;

&lt;p&gt;Et voilà !&lt;/p&gt;

&lt;p&gt;Hope this helps &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/e5a900f5-7560-4497-8c39-06708d65f0a2" /&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Tue, 06 Sep 2011 07:46:00 Z</pubDate><a10:updated>2011-09-07T18:42:21Z</a10:updated></item><item><guid isPermaLink="false">activation-et-desactivation-du-proxy-ie-via-powershell</guid><link>http://www.juliencorioland.net/Archives/activation-et-desactivation-du-proxy-ie-via-powershell</link><author>Julien Corioland</author><title>Activation et désactivation du proxy IE via Powershell</title><description>&lt;p&gt;En ce moment je suis chez un client où je dois activer un proxy pour me connecter à Internet. Comme j’en avais assez de devoir reconfigurer mon IE tous les soirs chez moi et tous les matins chez mon client, voilà deux scripts Powershell permettant de faire celà en deux clics :&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;EnableProxy.ps1 :&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;pre class="brush: ps;"&gt;set-itemproperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' 
-name ProxyEnable -value 1&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;DisableProxy.ps1&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;pre class="brush: ps;"&gt;set-itemproperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' 
-name ProxyEnable -value 0&lt;/pre&gt;

&lt;p&gt;Simple et efficace !&lt;/p&gt;

&lt;p&gt;A bientôt &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/04dced6b-31a0-4857-bb09-873169d48712" /&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Tue, 06 Sep 2011 07:28:00 Z</pubDate><a10:updated>2011-09-07T18:42:59Z</a10:updated></item><item><guid isPermaLink="false">aspnet-mvc-remote-attribute-and-asynchronous-validation</guid><link>http://www.juliencorioland.net/Archives/aspnet-mvc-remote-attribute-and-asynchronous-validation</link><author>Julien Corioland</author><title>[En][ASP.NET MVC] Remote attribute and asynchronous validation</title><description>&lt;p&gt;As you probably know ASP.NET MVC supports .NET Framework 4 Data Annotations to validate user’s inputs (Required, StringLength, Range, RegularExpression…)&lt;/p&gt;  &lt;p&gt;Another attribute exists and allows asynchronous client side validation. It’s the &lt;strong&gt;Remote&lt;/strong&gt; attribute. For example, it can be used to validate e-mail and username in a registration form and alert the user before the form is posted.&lt;/p&gt;  &lt;p&gt;Here is a sample model for a registration form :&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;public class UserModel
{
    [Required]
    [Remote(&amp;quot;CheckUsername&amp;quot;, &amp;quot;RemoteValidation&amp;quot;, ErrorMessage = &amp;quot;This username is already used.&amp;quot;)]
    public string Username { get; set; }
 
    [Required]
    [Remote(&amp;quot;CheckEmail&amp;quot;, &amp;quot;RemoteValidation&amp;quot;, ErrorMessage = &amp;quot;This e-mail is already used.&amp;quot;)]
    public string Email { get; set; }
     
    [StringLength(80)]
    public string FirstName { get; set; }
 
    [StringLength(80)]
    public string LastName { get; set; }
     
    public DateTime BirthDate { get; set; }
}&lt;/pre&gt;

&lt;p&gt;As you can see in the previous code snippet, the &lt;strong&gt;Remote&lt;/strong&gt; attribute takes two parameters :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;the MVC action that should validate the input &lt;/li&gt;

  &lt;li&gt;the controller that defines the action &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;u&gt;The controller :&lt;/u&gt;&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public class RemoteValidationController : Controller 
{
    private readonly string[] _existingUsernames = {&amp;quot;beedoo&amp;quot;, &amp;quot;julien&amp;quot;, &amp;quot;jcorioland&amp;quot;};
    private readonly string[] _existingEmails = { &amp;quot;contact@juliencorioland.net&amp;quot; };
 
    public JsonResult CheckUsername(string username) {
        bool userIsAvailable = !_existingUsers.Contains(username);
        return Json(userIsAvailable, JsonRequestBehavior.AllowGet);
    }
 
    public JsonResult CheckEmail(string email)
    {
        bool emailIsAvailable = !_existingEmails.Contains(email);
        return Json(emailIsAvailable, JsonRequestBehavior.AllowGet);
    }
}&lt;/pre&gt;

&lt;p&gt;These actions are very simple : they take the input to validate as a string parameter and return a Json-serialized Boolean that indicates if the input is valid or not. For this article I’ve used two arrays to represent users and e-mails databases but it’s possible to execute any code here.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;The view :&lt;/u&gt;&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;@using (Html.BeginForm())
{
    &amp;lt;div class=&amp;quot;editor-label&amp;quot;&amp;gt;@Html.LabelFor(m =&amp;gt; m.Username)&amp;lt;/div&amp;gt;
    &amp;lt;div class=&amp;quot;editor-field&amp;quot;&amp;gt;
        @Html.TextBoxFor(m =&amp;gt; m.Username)
        @Html.ValidationMessageFor(m =&amp;gt; m.Username)
    &amp;lt;/div&amp;gt;
 
    &amp;lt;div class=&amp;quot;editor-label&amp;quot;&amp;gt;@Html.LabelFor(m =&amp;gt; m.Email)&amp;lt;/div&amp;gt;
    &amp;lt;div class=&amp;quot;editor-field&amp;quot;&amp;gt;
        @Html.TextBoxFor(m =&amp;gt; m.Email)
        @Html.ValidationMessageFor(m =&amp;gt; m.Email)
    &amp;lt;/div&amp;gt;
 
    &amp;lt;div class=&amp;quot;editor-label&amp;quot;&amp;gt;@Html.LabelFor(m =&amp;gt; m.FirstName)&amp;lt;/div&amp;gt;
    &amp;lt;div class=&amp;quot;editor-field&amp;quot;&amp;gt;
        @Html.TextBoxFor(m =&amp;gt; m.FirstName)
        @Html.ValidationMessageFor(m =&amp;gt; m.FirstName)
    &amp;lt;/div&amp;gt;
 
    &amp;lt;div class=&amp;quot;editor-label&amp;quot;&amp;gt;@Html.LabelFor(m =&amp;gt; m.LastName)&amp;lt;/div&amp;gt;
    &amp;lt;div class=&amp;quot;editor-field&amp;quot;&amp;gt;
        @Html.TextBoxFor(m =&amp;gt; m.LastName)
        @Html.ValidationMessageFor(m =&amp;gt; m.LastName)
    &amp;lt;/div&amp;gt;
 
    &amp;lt;div class=&amp;quot;editor-label&amp;quot;&amp;gt;@Html.LabelFor(m =&amp;gt; m.BirthDate)&amp;lt;/div&amp;gt;
    &amp;lt;div class=&amp;quot;editor-field&amp;quot;&amp;gt;
        @Html.TextBoxFor(m =&amp;gt; m.BirthDate)
        @Html.ValidationMessageFor(m =&amp;gt; m.BirthDate)
    &amp;lt;/div&amp;gt;
     
    &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;S'enregistrer&amp;quot; /&amp;gt;
}&lt;/pre&gt;

&lt;p&gt;To activate the client side validation the following jQuery plugins should be loaded :&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;@ViewBag.Title&amp;lt;/title&amp;gt;
    &amp;lt;link href=&amp;quot;@Url.Content(&amp;quot;~/Content/Site.css&amp;quot;)&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;
    &amp;lt;script src=&amp;quot;@Url.Content(&amp;quot;~/Scripts/jquery-1.5.1.min.js&amp;quot;)&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;@Url.Content(&amp;quot;~/Scripts/jquery-ui-1.8.11.min.js&amp;quot;)&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;@Url.Content(&amp;quot;~/Scripts/jquery.unobtrusive-ajax.min.js&amp;quot;)&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;@Url.Content(&amp;quot;~/Scripts/jquery.validate.min.js&amp;quot;)&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;@Url.Content(&amp;quot;~/Scripts/jquery.validate.unobtrusive.min.js&amp;quot;)&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;&lt;/pre&gt;

&lt;p&gt;The validation actions are automatically called (asynchronously of course) when the user is completing the register form :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/4f231217-6729-4233-81ea-fb4da3539a26" rel="lightbox"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="image" border="0" alt="image" src="https://juliencoriolandblog.blob.core.windows.net/medias/e7cde45f-ce9f-43dd-b18b-7c75f50d2365" width="668" height="526" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope this helps &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/aa6b240f-4829-4081-b8ea-7a62900604c6" /&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Mon, 05 Sep 2011 16:26:00 Z</pubDate><a10:updated>2011-09-06T08:04:09Z</a10:updated></item><item><guid isPermaLink="false">aspnet-mvc-attribut-remote-et-validation-asynchrone</guid><link>http://www.juliencorioland.net/Archives/aspnet-mvc-attribut-remote-et-validation-asynchrone</link><author>Julien Corioland</author><title>[ASP.NET MVC] Attribut Remote et validation asynchrone</title><description>&lt;p&gt;Comme vous le savez peut-être déjà, ASP.NET MVC 3 supporte les DataAnnotations du .NET Framework 4 pour tout ce qui est validation des entrées utilisateurs : Required, StringLength, Range, RegularExpression…&lt;/p&gt;  &lt;p&gt;Il existe un attribut un peu moins connu : &lt;strong&gt;Remote&lt;/strong&gt;. Celui-ci permet de lancer une validation asynchrone côté client. Par exemple, sur un formulaire d’inscription il est fréquent de vouloir valider de manière asynchrone qu’un nom d’utilisateur / e-mail n’existe pas déjà en base utilisateur.&lt;/p&gt;  &lt;p&gt;Voilà un exemple de modèle pour parvenir à cela :&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;public class UserModel
{
    [Required]
    [Remote(&amp;quot;CheckUsername&amp;quot;, &amp;quot;RemoteValidation&amp;quot;, ErrorMessage = &amp;quot;Ce nom d'utilisateur est déjà utilisé&amp;quot;)]
    public string Username { get; set; }

    [Required]
    [Remote(&amp;quot;CheckEmail&amp;quot;, &amp;quot;RemoteValidation&amp;quot;, ErrorMessage = &amp;quot;Cet e-mail est déjà utilisé&amp;quot;)]
    public string Email { get; set; }
    
    [StringLength(80)]
    public string FirstName { get; set; }

    [StringLength(80)]
    public string LastName { get; set; }
    
    public DateTime BirthDate { get; set; }
}&lt;/pre&gt;

&lt;p&gt;Comme il est possible de le constater dans l’extrait de code ci-dessus, l’attribut remote prend deux paramètres :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;L’action MVC en charge de la validation &lt;/li&gt;

  &lt;li&gt;Le contrôlleur dans lequel l’action est défini (ici, RemoteValidationController) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Voilà le code de ce contrôlleur :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public class RemoteValidationController : Controller 
{
    private readonly string[] _existingUsernames = {&amp;quot;beedoo&amp;quot;, &amp;quot;julien&amp;quot;, &amp;quot;jcorioland&amp;quot;};
    private readonly string[] _existingEmails = { &amp;quot;contact@juliencorioland.net&amp;quot; };

    public JsonResult CheckUsername(string username) {
        bool userIsAvailable = !_existingUsers.Contains(username);
        return Json(userIsAvailable, JsonRequestBehavior.AllowGet);
    }

    public JsonResult CheckEmail(string email)
    {
        bool emailIsAvailable = !_existingEmails.Contains(email);
        return Json(emailIsAvailable, JsonRequestBehavior.AllowGet);
    }
}&lt;/pre&gt;

&lt;p&gt;Les actions sont ultra simples : elles prennent en paramètre le terme à valider et retourne un booléean, sérialisé en Json.&lt;/p&gt;

&lt;p&gt;NB : ici la source d’emails/usernames existants a été simplifiée, mais un appel à une base de données est tout à fait possible.&lt;/p&gt;

&lt;p&gt;La vue pour tester :&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;@using (Html.BeginForm())
{
    &amp;lt;div class=&amp;quot;editor-label&amp;quot;&amp;gt;@Html.LabelFor(m =&amp;gt; m.Username)&amp;lt;/div&amp;gt;
    &amp;lt;div class=&amp;quot;editor-field&amp;quot;&amp;gt;
        @Html.TextBoxFor(m =&amp;gt; m.Username)
        @Html.ValidationMessageFor(m =&amp;gt; m.Username)
    &amp;lt;/div&amp;gt;

    &amp;lt;div class=&amp;quot;editor-label&amp;quot;&amp;gt;@Html.LabelFor(m =&amp;gt; m.Email)&amp;lt;/div&amp;gt;
    &amp;lt;div class=&amp;quot;editor-field&amp;quot;&amp;gt;
        @Html.TextBoxFor(m =&amp;gt; m.Email)
        @Html.ValidationMessageFor(m =&amp;gt; m.Email)
    &amp;lt;/div&amp;gt;

    &amp;lt;div class=&amp;quot;editor-label&amp;quot;&amp;gt;@Html.LabelFor(m =&amp;gt; m.FirstName)&amp;lt;/div&amp;gt;
    &amp;lt;div class=&amp;quot;editor-field&amp;quot;&amp;gt;
        @Html.TextBoxFor(m =&amp;gt; m.FirstName)
        @Html.ValidationMessageFor(m =&amp;gt; m.FirstName)
    &amp;lt;/div&amp;gt;

    &amp;lt;div class=&amp;quot;editor-label&amp;quot;&amp;gt;@Html.LabelFor(m =&amp;gt; m.LastName)&amp;lt;/div&amp;gt;
    &amp;lt;div class=&amp;quot;editor-field&amp;quot;&amp;gt;
        @Html.TextBoxFor(m =&amp;gt; m.LastName)
        @Html.ValidationMessageFor(m =&amp;gt; m.LastName)
    &amp;lt;/div&amp;gt;

    &amp;lt;div class=&amp;quot;editor-label&amp;quot;&amp;gt;@Html.LabelFor(m =&amp;gt; m.BirthDate)&amp;lt;/div&amp;gt;
    &amp;lt;div class=&amp;quot;editor-field&amp;quot;&amp;gt;
        @Html.TextBoxFor(m =&amp;gt; m.BirthDate)
        @Html.ValidationMessageFor(m =&amp;gt; m.BirthDate)
    &amp;lt;/div&amp;gt;
    
    &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;S'enregistrer&amp;quot; /&amp;gt;
}&lt;/pre&gt;

&lt;p&gt;Enfin, assurez vous de charger les plugins jQuery UI et jQuery validate pour que cela fonctionne :&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;@ViewBag.Title&amp;lt;/title&amp;gt;
    &amp;lt;link href=&amp;quot;@Url.Content(&amp;quot;~/Content/Site.css&amp;quot;)&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;
    &amp;lt;script src=&amp;quot;@Url.Content(&amp;quot;~/Scripts/jquery-1.5.1.min.js&amp;quot;)&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;@Url.Content(&amp;quot;~/Scripts/jquery-ui-1.8.11.min.js&amp;quot;)&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;@Url.Content(&amp;quot;~/Scripts/jquery.unobtrusive-ajax.min.js&amp;quot;)&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;@Url.Content(&amp;quot;~/Scripts/jquery.validate.min.js&amp;quot;)&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;@Url.Content(&amp;quot;~/Scripts/jquery.validate.unobtrusive.min.js&amp;quot;)&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;&lt;/pre&gt;

&lt;p&gt;Dès lors, les actions de validation sont appelées en asynchrone lors de la saisie du formulaire, permettant ainsi une aide à la saisie avancée pour l’utilisateur !&lt;/p&gt;

&lt;p&gt;&lt;a href="https://juliencoriolandblog.blob.core.windows.net/medias/f5563f95-e071-431a-894d-31e4099d6554" rel="lightbox"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="image" border="0" alt="image" src="https://juliencoriolandblog.blob.core.windows.net/medias/bd24678a-d5e2-4f1b-8033-670de4704f20" width="683" height="539" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;NB : et comme toujours, la validation côté client ne doit absolument pas remplacer la validation côté serveur !&lt;/p&gt;

&lt;p&gt;A bientôt &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/77f00b6e-819d-41f5-b67e-4992baf52650" /&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Mon, 05 Sep 2011 11:35:00 Z</pubDate><a10:updated>2011-09-05T11:35:00Z</a10:updated></item><item><guid isPermaLink="false">aspnet-mvc-authentification-et-utilisateurs-anonymes</guid><link>http://www.juliencorioland.net/Archives/aspnet-mvc-authentification-et-utilisateurs-anonymes</link><author>Julien Corioland</author><title>[ASP.NET MVC] Authentification et utilisateurs anonymes</title><description>&lt;p&gt;Lorsque l’on développe une application web nécessitant de l’authentification, plusieurs stratégies sont possibles, par exemple :&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;On autorise l’accès anonyme partout, sauf sur les pages nécessitant que l’utilisateur soit authentifié &lt;/li&gt;    &lt;li&gt;On refuse l’accès anonyme partout sauf sur les pages ne nécessitant pas que l’utilisateur soit authentifié &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Depuis ASP.NET MVC 2 il est possible de réaliser la première option très facilement. En effet, il suffit de placer un filtre d’authorisation sur les méthodes des contrôleurs (entendre action) pour lesquelles il faut que l’utilisateur soit authentifié. Il s’agit de l’attribut &lt;strong&gt;AuthorizeAttribute&lt;/strong&gt; :&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;[Authorize]
public ActionResult Admin()
{
    //code
    return View();
}&lt;/pre&gt;

&lt;p&gt;Sur certains projets, seules quelques pages ne nécessitent pas que l’utilisateur soit activée, il devient donc contraigant d’avoir à placer l’attribut Authorize sur TOUTES les méthodes de contrôleur. ASP.NET MVC 3 introduit un nouveau concept : les filtres globaux. Il s’agit en fait de la possibilité d’appliquer des filtres sur toutes les actions de tous les contrôleurs, par exemple.&lt;/p&gt;

&lt;p&gt;Pour cela, il suffit de venir ajouter le filtre en question dans la liste des filtres globaux. Ceci se fait dans le fichier Global.asax. Depuis la version 3 d’ASP.NET MVC, une nouvelle méthode est générée : &lt;strong&gt;RegisterGlobalFilters &lt;/strong&gt;:&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
    filters.Add(new AuthorizeAttribute());
}&lt;/pre&gt;

&lt;p&gt;Cette méthode est ensuite appelée dans le &lt;strong&gt;Application_Start&lt;/strong&gt; du même fichier :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
}&lt;/pre&gt;

&lt;p&gt;Si vous testez ce code, vous verrez que plus rien ne s’affiche, même pas la page d’authentification puisque vous demandez que l’utilisateur soit authentifié partout.&lt;/p&gt;

&lt;p&gt;Pour rendre le process un peu plus permissif, il suffit de créer un nouvel attribut AnonymousAttribute qui servira à marquer les actions ne nécessitant pas que l’utilisateur soit authentifié :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class AnonymousAttribute : Attribute
{
}&lt;/pre&gt;

&lt;p&gt;A présent, il est possible de dériver le filtre AuthorizeAttribute et de surcharger la méthode OnAuthorization de celui-ci. Dans cette surcharge, il est possible de vérifier sur le contexte du filtre si l’action possède ou non l’attribut Anonymous décrit ci-dessus :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public class AuthenticationRequiredAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        bool anonymousAllowed = filterContext.ActionDescriptor.IsDefined(typeof (AnonymousAttribute), false);

        if (!anonymousAllowed)
            base.OnAuthorization(filterContext);
    }
}&lt;/pre&gt;
Du coup, il suffit d’enregister ce filtre plutôt que le AuthorizeAttribute dans le Global.asax 

&lt;pre class="brush: csharp;"&gt;public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
    filters.Add(new AuthenticationRequiredAttribute());
}&lt;/pre&gt;

&lt;p&gt;Puis de placer l’attribut &lt;strong&gt;Anonymous&lt;/strong&gt; partout ou vous souhaitez conserver une accès anonyme (par exemple les actions LogOn) :&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;[Anonymous]
public ActionResult LogOn()
{
    return View();
}

[HttpPost]
[Anonymous]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
    //code
}&lt;/pre&gt;

&lt;p&gt;Du coup, le comportement désiré est bien obtenu : toutes les actions nécessitent que l’utilisateur soit authentifié, sauf celles où pour lesquelles l’accès anonyme est autorisé explicitement.&lt;/p&gt;

&lt;p&gt;En espérant que cela serve à certains d’entre vous !&lt;/p&gt;

&lt;p&gt;A bientôt &lt;img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-winkingsmile" alt="Winking smile" src="https://juliencoriolandblog.blob.core.windows.net/medias/ad93fd32-97b6-4147-b081-9089aec7b4c9" /&gt;&lt;/p&gt;</description><source>Blog de Julien Corioland (MVP)</source><pubDate>Mon, 22 Aug 2011 15:37:00 Z</pubDate><a10:updated>2011-08-22T15:45:37Z</a10:updated></item></channel></rss>
