Archive pour la catégorie ‘ASP.NET’

IIS Media Pack 1.0

22 novembre 2008

Il y a 2 semaines est sorti un pack d’extension au serveur Web IIS 7.0. Comme son nom l’indique, ce pack ajoute 2 fonctionnalités médias :

  • Bit Throttling (contrôle de flux) : cela permet de contrôler le débit de différents types de médias (WMV, MP3, etc.) en fonction du client, du paramétrage, etc. Ayant eu, dans une vie antérieure, à développer des sites web manipulant de nombreux médias (vidéos, mp3), je peux vous dire que cette fonctionnalité est très intéressante. Elle permet de gérer son contenu plus sereinement, je parle surtout en terme de bande passante, sans avoir à recourir à un serveur spécialisé (streaming, Windows Media Services, etc.) ou à un CDN (Content Delivery Network comme akamai).
  • Web playlists : création de playlists éventuellement dynamiques avec pour principal but de ne pas exposer en direct vos médias

De nouvelles extensions sont prévues pour 2009, notamment concernant la gestion des vidéos HD avec Smooth Streaming (je vous renvoie d’ailleurs sur le blog d’un collègue qui en parle : SmoothHD chez Patrice Lamarche)

Plus d’infos sur le site de IIS : http://www.iis.net/media

Logs IIS – champ time-taken

23 octobre 2008

Le champ étendu des logs IIS time-taken est bien pratique pour avoir une bonne vision des performances d’un site web ou de services. Il reflète le temps, en millisecondes, qu’a pris la requête. Mais attention, qu’inclut-il ?

Le champ time-taken correspond au temps entre le premier octet reçu et le dernier octet envoyé par HTTP.sys (la sous-couche HTTP de IIS). Il inclut donc le temps de transfert réseau.

En conséquence, si vous avez des pages lourdes (ou des ViewState énormes), le résultat peut être faussé et votre analyse moins précise. Pensez donc à jetez un oeil au champ sc-bytes et cs-bytes (sc=server-to-client / cs=client-to-server) pour vérifier que le poids des transferts n’est pas anormal. Evidemment, sur un intranet local, l’impact sera souvent négligeable.

 

Tous les détails sur ce lien : http://support.microsoft.com/kb/944884/en-us

PS : ces informations conviennent à IIS 6.0 et IIS 7.0

Identité d’un pool d’application IIS – Droits minimums

12 octobre 2008

Par défaut, dans IIS 6.0, un Worker Process (le processus représentant un pool d’applications au runtime), tourne avec le compte Network Service (Service Réseau). C’est largement suffisant dans la majorité des cas mais il arrive que l’on souhaite utiliser un compte particulier, pour faire de la sécurité intégrée avec SQL Server, pour accéder à un répertoire sensible, etc.

Dans ce cas, on modifie les paramètres du Pool, section Identity (Identité), pour préciser un compte d’exécution. Bien sûr, vous vous doutez bien qu’il va falloir des droits à ce compte pour accéder au minimum requis ASP.NET (fichiers du sites, GAC, répertoire de compilation temporaire, etc.).

Vous trouverez la liste à ce lien :  http://msdn.microsoft.com/en-us/library/kwzs111e.aspx

Normalement, vous n’avez pas à configurer tout cela, il suffit juste d’ajouter le compte utilisé dans le groupe IIS_WPG, local à la machine. Mais il est possible que cela ne fonctionne pas, surtout si vous avez copié le site d’une machine à une autre (problème de SID).

Attention : tout ceci ne fonctionne que pour IIS 6.0, pour IIS 7.0, c’est un petit peu différent.

 

Ajax: UpdatePanel Injection

16 janvier 2008

Je vais vous présenter une technique utile pour optimiser la gestion des UpdatePanel dans ASP.NET Ajax : l’injection d’UpdatePanel.
[Le code source est disponible en fin d'article]

La problématique :

Dans une page se trouve une liste d’éléments présentés dans un contrôle. Prenons un contrôle template comme le Repeater par exemple (bien que je préfère créer ma propre liste templatée). Une action sur un élément doit changer son état et l’affichage doit refléter ce changement en conséquence. L’image ci-dessous montre l’exemple utilisé.

Avec un PostBack classique, on doit reconstruire tout l’arbre de contrôle pour pouvoir rendre la liste modifiée. Une optimisation (même si on ne gagne que de la bande passante) consisterait à placer le template de l’élément dans un UpdatePanel mais on ne gagne pas le temps de Render car la page reconstruit quand même l’arbre de contrôle (CreateChildControl) comme présenté dans la trace ASP.NET suivante.

arbre de controle du repeater

En plus, à moins que vous ne l’ayez prévu dans votre code, il y aura un nouvel appel à la base de données pour recharger la liste.

L’objectif :

Ne pas reconstruire l’ensemble de l’arbre (le Repeater) mais construire juste le template de l’élément modifié. On envoie le HTML généré au client via AJAX pour mettre à jour le DOM.

La solution :

On va utiliser un faux UpdatePanel qui représente la partie à modifier et qui sera plus haut dans la hiérarchie de contrôles (à la racine par exemple). On intercepte l’événement dans la page, on effectue la modification visuelle (après avoir fait son traitement métier bien évidemment) dans ce faux UpdatePanel et on va le réinjecter côté client à la place du vrai UpdatePanel à modifier. Bien entendu, on ne recharge pas la liste d’éléments.

Comment faire ? :

Premièrement, il faut placer la partie à mettre à jour selon cette technique dans un UserControl (ASCX) pour factoriser; je rappelle qu’il y a les instances dans le Repeater et la fausse instance à la racine. Dans notre cas c’est le bloc jaune (l’élément) que j’exporte. Cet export nous oblige à réifier l’affichage d’un élément en lui créant par exemple des propriétés publiques correspondant aux infos à afficher. On peut aussi le rendre autonome afin qu’il sache les retrouver à partir de l’identifiant de l’élément mais là je rentre dans le détail. Il faudra aussi faire de même avec les événements que l’élément peut lancer.

Deuxièmement, on place un instance de ce contrôle dans l’ItemTemplate du Repeater et à la racine de la page, dans un UpdatePanel. Pour le second, on veillera à mettre Visible à False afin de ne pas perturber l’affichage normal. Je vous rappelle que ce second élément n’est là que pour simuler un autre UpdatePanel, c’est un contrôle “technique“.

<ItemTemplate>
 <Custom:Department runat="server" ID="DeptBlock" IsSelected='<%#Eval("DepartmentID").ToString()==ElemID %>'
   ElemGroup='<%#Eval("GroupName") %>' ElemName='<%#Eval("Name") %>' ElemID='<%#Eval("DepartmentID") %>' />
</ItemTemplate>

Troisièment, désactiver les raffraichissements automatiques des UpdatePanel pour prendre la main avec les attributs suivants :

ChildrenAsTriggers="false" UpdateMode="Conditional"

Quatrièment, il ne reste plus qu’à modifier l’événement déclencheur (j’ai choisi ici ItemCommand du Repeater). Les étapes sont :

  • désactiver le Repeater pour prendre la main
  • remplir le faux UpdatePanel
  • faire croire au système que c’est le bon UpdatePanel que l’on envoie
  • L’envoyer côté client

protected void DepartmentList_ItemCommand(object source, RepeaterCommandEventArgs e)
{
 //on ne rebind PLUS la liste
 // DepartmentList.DataBind();
 //et on la "désactive" sinon, elle a tendance à se construire toute seule
 DepartmentList.DataSource = null;
 DepartmentList.DataSourceID = null;

 //en revanche, on configure le FakeUpdatePanel
 //...ici, je mets des données bidon mais je devrais charge mon élément individuel
 FakeDepElem.ElemGroup = "FAKE MIS A JOUR";
 FakeDepElem.ElemName = "Fake Dept";
 FakeDepElem.ElemID = e.CommandArgument.ToString();
 FakeDepElem.IsSelected = true;
 //on rend l'élément visible sinon la méthode Render ne retournera rien
 FakeDepElem.Visible = true;

 //ICI, c'est le point technique, on change l'ID du faux UpdatePanel
 //le fait de changer l'identifiant de l'update panel va permettre de cibler un autre updatepanel coté client.
 FakeElementUP.ID = e.CommandName;
 //on envoie le panel au client
 FakeElementUP.Update();
}

Le point le plus important est le remplacement de l’identifiant de mon faux UpdatePanel. Une fois renvoyé au client, la partie JavaScript d’Ajax cherche l’UpdatePanel spécifié et le remplace, sans se rendre compte de l’altération. La capture faite avec Fiddler ci-dessous montre ce que renvoie Ajax au client. Une entête identifie le bloc (l’UpdatePanel) à mettre à jour suivi du code HTML à remplacer. C’est cette entête finalement que nous avons altéré.

632|updatePanel|DepartmentList_ctl03_DeptBlock_ElementUP|

Verification avec Fiddler

Dans ce code, vous voyez que j’utilise e.CommandName. Ce choix est arbitraire. Pour fonctionner, le handler de l’événement a besoin à minima des informations suivantes :

  • l’identifiant de l’élément (ou un moyen de le retrouver)
  • l’identifiant de l’UpdatePanel à mettre à jour

SelectBtn.CommandArgument = ElemID;
SelectBtn.CommandName = ElementUP.UniqueID;

Moi je les passe explicitement dans la commande mais on peut imaginer de les retrouver avec le paramètre source ou par un autre moyen.

J’ai pu mettre en oeuvre cette technique massivement dans un site web grand public de courses en ligne que nous (Bewise) avons réalisé pour un client. Je peux donc vous dire qu’elle a été validée à tous les niveaux : performances, maintenabilité, fiabilité, sécurité. Bien évidement, elle est beaucoup plus complexe que cela car beaucoup plus riche fonctionnellement (je dis cela pour ceux qui peuvent rester sur leur faim).

Effets de bord :

Si la Trace est active, vous risquez de voir apparaître une erreur précisant qu’il y a des doublons dans les ID des contrôles et le module de Trace ne sait pas le gérer (et donc lève une exception). Bizarrement, l’erreur n’est plus levée si la pile est pleine (requestLimit). Si vous avez plus d’infos sur ce comportement non déterministe ou un moyen de contournement…

Code Source :

Le code source utilise juste une connexion à la base AdventureWorks de Microsoft. Vous aurez juste à changer la chaîne de connexion dans le web.config et le nom du serveur web dans les propriétés du projet. Le code est téléchargeable ici

Architecte ASP.NET du site Express Drive

2 septembre 2007

Un petit peu de pub pour un client, pour Bewise (et un peu pour moi :-)  avec la présentation du site Express Drive.

Express Drive est un site de courses en ligne tout ce qu’il y a de plus classique sauf que derrière se cache un concept excellent : le drive. Vous faîtes vos courses sur le web et vous passez les prendre au magasinExpress Drive de E. Leclerc Roques. Même pas besoin de se garer, vous allez dans les voies réservées et n’avez qu’à ouvrir votre coffre.

Pas besoin de citer le nom du client, c’est écrit sur la photo. Actuellement, il n’y a qu’un seul magasin pilote en france (et c’est ici, près de Toulouse) mais nul doute que les prochains ouvriront bientôt près de chez vous.

Je suis fan de ce concept mais ce qui me rend fier c’est que j’ai été l’architecte technique du site web Express Drive et Bewise est en grande partie responsable de la mise en oeuvre (merci Jocelyn). Nous sommes heureux d’avoir pu accompagner notre client à développer son site Web en un temps record.

Et comme un blog de geek ne peut pas se contenter de cette simple annonce, ouvrons un peu le capot du site…

Sans entrer dans le détail, ce site est 100% XHTML + CSS 2.0 (bon d’accord, on a dû prendre quelques raccourcis de temps en temps). Le site tire aussi énormément partie des extensions AJAX de ASP.NET. En particulier la page de courses qui est 100% en AJAX et où nous avons poussé à fond l’utilisation et la maîtrise des UpdatePanel.

Ce site n’est qu’un premier jet et va s’enrichir de nombreuses fonctionnalités pour atteindre le niveau de ces concurrents Houra, Ooshop, AuchanDirect, etc.

Je n’aime pas les FormView

3 août 2007

Même si je fais du web de façon intensive depuis plusieurs années, je n’ai jamais creusé les contrôles de databinding tout prêt de ASP.NET (ni 1.1, ni 2.0). Je peux même dire que je suis novice avec les GridView, FormView et autres DetailView.

Je suis confronté à ces contrôles depuis quelques mois maintenant dans le cadre de projets qui en tirent parti. Je dois dire que je commence à être séduit par ce mode de fonctionnement même si je reste accro aux “custom templated controls“.

Sauf que la semaine dernière j’ai découvert que la FormView a un fonctionnement que je n’aime pas du tout et qu’il est important de connaître pour bien maîtriser ce que l’on fait. Admettons que dans la FormView, vous placiez un LinkButton et un CustomValidator pour vérifier une condition. Le LinkButton a la propriété CausesValidation à true. Vous branchez l’événement click sur votre méthode et là, tout bascule, en déboguant, vous vous apercevez que la validation (votre méthode OnServerValidate) est appelée 2 fois.

La validation est garantie par la propriété CausesValidation du bouton comme on peut le voir ci-dessous dans la méthode RaisePostBackEvent du LinkButton via Reflector.

protected virtual void RaisePostBackEvent(string eventArgument)
{
    base.ValidateEvent(this.UniqueID, eventArgument);
    if (this.CausesValidation)
    {
        this.Page.Validate(this.ValidationGroup);
    }
    this.OnClick(EventArgs.Empty);
    this.OnCommand(new CommandEventArgs(this.CommandName, this.CommandArgument));
}

Mais qui appelle donc la validation une seconde fois ? On peut voir aussi dans cette méthode qu’un LinkButton lève l’événement Click mais aussi l’événement Command, même si on n’a pas précisé de CommandName. Le problème, c’est qu’un événement Command utilise le mécanisme de Bubbling (méthode OnBubbleEvent) et remonte dans la hierarchie de contrôle jusqu’à ce que quelqu’un l’intercepte. Et devinez qui le prend en charge ? La FormView !!

Sans vérifier si la FormView est à même de prendre en charge la commande (représentée par le paramètre CommandName), elle lance la validation comme on peut le voir dans le code de la méthode OnBubbleEvent().

if (causesValidation && (this.Page != null))
{
   this.Page.Validate(validationGroup);
}

Ce qui fait que si vous préférez utiliser l’événement Click, vous aurez 2 validations de la page (ou du ValidationGroup).

La solution :

  • utiliser l’événement Command de la FormView pour faire vos traitements dans les boutons se trouvant en son sein.
  • mettre la propriété CausesValidation à false et appeler manuellement Page.Validate() dans la méthode qui gère l’événement Click

Moralité : FormView stinks !

unable to build project output group ‘content files from WebApplication’

1 août 2007

Dans un projet de setup web, quand vous souhaitez copier les ‘Content Files’ des sorties d’un projet de type Web Application, si vous rencontrez un jour cette erreur, je vous propose la solution :

unable to build project output group 'content files from WebApplication'

Le problème vient du fait qu’un fichier du site Web n’est pas trouvé sur le disque (le petit icône avec un triangle jaune). La compilation du projet web ne génère aucun warning mais un projet de setup lève une erreur car lui a besoin du fichier manquant.

Plutôt que de vous arracher les cheveux, naviguez dans tous les répertoires du site dans le Solution Explorer (je sais ça peut être long) à la recherche du triangle jaune (ça ferait un bon titre pour un film) et supprimer cette référence.

Tips : cherchez en priorité dans le répertoire des images, c’est souvent là qu’il y a le plus de boxon fichiers.

 

Ajouter une CSS dans une page avec une MasterPage

28 juin 2007

Quand on utilise les MasterPages, il devient impossible d’ajouter un lien vers une feuille de style directement dans une page. En effet, on ne peut mettre la référence à la CSS que dans la section <head/> et les <asp:Content> des pages enfants doivent se trouver obligatoirement dans le <body/> (la <form/> plus exactement).

On a tout de même accès à la section <head/> depuis le code pour ajouter manuellement un lien vers une CSS avec un contrôle de type HtmlLink.

HtmlLink cssLink = new HtmlLink();
cssLink.Href = "../mapage.css";
cssLink.Attributes.Add("rel", "stylesheet");
cssLink.Attributes.Add("type", "text/css");
Page.Header.Controls.Add(cssLink);

Afin de rendre cette utilisation plus souple, on peut encapsuler ce code dans un contrôle afin de l’ajouter graphiquement dans le designer de page :

public class StyleSheetLink : Control
{
  private string _href;
  [UrlProperty]
  public string Href
  {
    get { return _href; }
    set { _href = value; }
  }
  protected override void OnPreRender(EventArgs e)
  {
    HtmlLink cssLink = new HtmlLink();
    cssLink.Href = _href;
    cssLink.Attributes.Add("rel", "stylesheet");
    cssLink.Attributes.Add("type", "text/css");
    Page.Header.Controls.Add(cssLink);
  }

Ainsi, il suffit d'ajouter dans une page le tag suivant :

<Bewise:StyleSheetLink ID="css" runat="server" Href="~/_css/default.css" />

Pour améliorer ce contrôle, on pourrait ajouter une vérification dans la liste des contrôles du header pour savoir si la CSS n'est pas déjà référencée.

Afficher un PNG avec un channel alpha sur IE6

12 juin 2007

La problématique

Elle est très simple, IE6 ne gère pas le channel alpha sur une image au format PNG. Je ne parle pas de simple transparence (ça c’est géré) mais d’une transparence en dégradé. (définition wikipedia)

La base de travail

Nous allons faire fonctionner le channel alpha pour une image classique et pour image en background. Le code HTML est le suivant :

<img src="images/logoalpha.png" />

<h1><span>Les passionnés du malt</span></h1>

Le code CSS est le suivant :

#headings h1 {
  height:241px;
  width:512px;
  background: url( '../images/logoalpha.png' ) no-repeat;
}
#headings h1 span {
  display: none;
}

La solution

Internet Explorer dispose d’un système de filtres pour effectuer des opérations de rendu sur des éléments de l’arbre DOM. Notamment, il existe un filtre appelé AlphaImageLoader qui permet de gérer le channel alpha. Pour appliquer un filtre, il suffit d’insérer une directive CSS filter. Pour l’image background, c’est assez simple, on applique directement la directive à la place de background :

filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='images/logoalpha.png', sizingMethod='scale');

Cependant, pour rester compatible avec IE7 et Firefox, on doit mettre la directive dans un hack pour IE et on garde un background classique pour les autres navigateurs. Le hack qui est interprété uniquement par IE (inférieur à la version 7) est “* html” :

* html #headings h1 {
  /* on annule le background et on le red‚finit avec le filtre */
  background:none;
  filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='images/logoalpha.png', sizingMethod='scale');
}

Pour l’image classique, on ne peut appliquer directement la directive filter en CSS. On doit recourir à du Javascript pour appliquer le filtre. Il existe un moyen d’appliquer un script sur un élément avec IE6, c’est l’utilisation des behaviours. Un behaviour est un bout de Javascript encapsulé dans un fichier .HTC formaté d’une certaine façon :

<public:component>
  <public:attach event="onpropertychange" onevent="propertyChanged()" />
  <script>
[MON SCRIPT ICI]
  /script>
</public:component>

On voit ici que le script sera exécuté au changement d’une propriété du tag sur lequel il est appliqué. Je ne vais pas décrire le script ici et je vous propose plutôt de le télécharger ici. Retenez juste qu’il a besoin d’un gif transparent pour appliquer le filtre (je n’ai pas cherché à comprendre ;-) .

Mais alors, comment appliqué le behaviour ? En CSS, il suffit d’ajouter :

.alphaPNG {
  /* compris uniquement par IE --> javascript executé sur l'élément */
  behavior:url('png.htc');
}

Vous aurez compris que j’ai juste à appliquer la classe CSS sur mon image PNG :

<img src="logoalpha.png" width="512" height="241" class="alphaPNG" />

Vous noterez que je fixe la taille sinon, le rendu ne se fait pas.

Pour voir ce que cela donne, rendez-vous sur cette page : http://blog.djeepy1.net/public/labs/png-fix/SampleLogo.htm
Pour télécharger tout le projet :  http://blog.djeepy1.net/public/labs/png-fix/sample.zip

Merci à Mitch pour le logo.

 

Compilation conditionnelle en ASP.NET 2.0

10 juin 2007

Je me suis retrouvé récemment face à un doute technique. Après avoir longuement expliqué l’intérêt de mettre des traces dans les différents événements d’une page ou d’un contrôle ASP.NET et l’importance de conditionner ces traces pour ne pas les retrouver dans l’environnement de production, je me suis retrouvé coincé car la technique a changé entre ASP.NET 1.x et ASP.NET 2.0. Explication…

Dans ma page, je mets les traces souhaitées :

#if TRACE
    Context.Trace.Write("MaPage", "Début de l'événement OnLoad");
#endif

Les pages et les contrôles ASCX étant compilés dynamiquement à l’exécution, il est inutile de mettre une directive de compilation dans votre projet (sauf si vous utilisez la fonctionnalité de publication avec les pages précompilées et non updateable).

En 1.x, j’avais l’habitude de configurer la directive dans la section <compilation/> du web.config :

<compilation debug="true" compilerOptions="/d:TRACE">

Là, l’intellisense ne me propose pas l’attribut CompilerOptions ce qui m’est confirmé par une erreur au runtime. Un petit tour sur la toile pour comprendre que la compilation est plus complexe (fine) en ASP.NET 2.0.
Heureusement, il existe toujours cette configuration dans la directive de page, ce qui n’est pas plus mal d’ailleurs pour restreindre le scope de la directive de compilation :

<%@Page Language="C#" CompilerOptions="/d:TRACE" %>

Pour ceux qui souhaitent utiliser la directive pour l’ensemble du site, il faut vous tourner vers la section de configuration <system.codeDom> où on doit déclarer des <compilers> avec leurs paramètres pour des jeux de fichiers du site. dans l’exemple ci-dessous, on définit que les fichier .cs seront compilés via un CSharpCodeProvider (csc.exe) avec la directive TRACE.

<system.codedom>
   <compilers>
      <compiler            
         language="c#;cs;csharp" extension=".cs"
         compilerOptions="/d:TRACE"
         type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />          
   </compilers>
</system.codedom>

Attention toutefois avec cette méthode car le site doit être exécuté en FullTrust pour avoir accès aux options de compilation.

Merci à Scott Allen pour les explications.


Suivre

Get every new post delivered to your Inbox.

Joignez-vous à 203 followers