mardi 14 juin 2011

Premiers pas en HTML 5 – Canvas

L’heure est venu de s’intéresser progressivement à HTML 5, dans la mesure où la spécification semble pouvoir prochainement dépasser le stade du Working Draft.

Donc, par où commencer? HTML 5 propose tout un tas de fonctionnalités hétérogènes. Certaines sont déjà supportées par la plupart des navigateurs, d’autres pas. Elles ne sont d’ailleurs visiblement pas toutes au même niveau d’avancement. Certaines portent sur l’aspect graphique, d’autres sur la communication ou encore sur la validation des formulaires par exemple. De même, les tutoriaux disponibles sont nombreux, mais dispersés. Donc pour démarrer, la façon la plus logique de procéder est de se concentrer sur les parties relativement stables et déjà implémentées par les navigateurs courants :

  • Les fonctionnalités multimédia (audio, vidéo, dessin, etc.)
  • Les fonctions de drag&drop
  • Le storage

Plus précisément, l’idée est ici de s’intéresser en premier lieu au Canvas, une nouvelle balise introduite par HTML 5 qui permet de définir une région dans laquelle on peut dessiner. Après avoir fastidieusement récupéré des bouts de code pour constituer un exemple simple et fonctionnel quelque soit le navigateur client, voici un premier résultat :

<!DOCTYPE html>
<html>
<head>
<title>Bouncing balls demo</title>
</head>
<body>
<section id="wrapper">
<header>
<h1>Bouncing balls</h1>
</header>
<article></article>
</section>
<script>
// global variables
var vSpeedInc = 1;
var hSpeed = 5.0;
var vSpeed = 1.0;
var circleRadius = 20;
var circleXPos = 100.0;
var circleYPos = 100.0;
var width = 800;
var height = 600;
var framerate = 50;

// start animation at startup
startAnimation();

function startAnimation(data) {
// canvas initialization
var canvas = document.createElement('canvas');
canvas.height = height;
canvas.width = width;
document.getElementsByTagName('article')[0].appendChild(canvas);
var ctx = canvas.getContext("2d");

// lauch timer
window.canvasTimer = setInterval(draw, 1000 / framerate);

function draw() {
// draw
ctx.clearRect(0, 0, width, height);
ctx.strokeStyle = "#000000";
ctx.fillStyle = "#FF0000";
ctx.beginPath();
ctx.arc(circleXPos, circleYPos, circleRadius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.stroke();
ctx.fill();

// compute
if (circleXPos + circleRadius >= width || circleXPos - circleRadius <= 0)
hSpeed = -hSpeed;


if (circleYPos + circleRadius + vSpeed >= height) {
vSpeed = -vSpeed;
}
else {
vSpeed = vSpeed + vSpeedInc;
}

circleXPos = circleXPos + hSpeed;
circleYPos = circleYPos + vSpeed;
}
}
</script>
</body>
</html>



Ce code anime une balle qui rebondit et se cogne sur les coins du Canvas. Il fonctionne seul, par simple copier/coller dans une page HTML vierge. Il représente donc un bon point de départ pour des expérimentations diverses, ce qui manque visiblement dans les différents tutoriaux disponibles.

vendredi 3 juin 2011

DbConnectionStringBuilder

Il y a plus de 10000 types définis dans le Framework .NET. Il est donc logique que quelques uns d’entre eux soient à la fois très utiles et méconnus. DbConnectionStringBuilder (et ses classes spécialisées comme SqlConnectionStringBuilder pour SQL Server) est sans doute l’un d’entre eux. Il permet de manipuler des chaines de connexion pour les providers communs par code (propriétés et accesseurs) sans avoir à traiter directement la chaine de caractères. Le code est alors plus lisible et il y a moins de risque d’erreur.

Dans la plupart des cas, les chaines de connexions définies habituellement dans le fichier de configuration n’ont pas besoin d’être modifiées à la volée. Néanmoins, certaines situations peuvent justifier l’utilisation de ces classes :

  • L’application accède à de multiples bases de données et il n’est pas possible de déterminer à la compilation quelle(s) base(s) de données sont sollicitées.
  • L’application accède à une base de données pour laquelle les informations nécessaires à la connexion ne sont pas connues lors de la compilation, par exemple parce qu’elles sont fournies par l’utilisateur.
  • L’application utilise des logins/passwords différents pour la connexion à la base de données en fonction des droits accordés à l’utilisateur connecté.
  • Ecriture de code pour l’industrialisation d’un logiciel automatisant la configuration en fonction des plateformes développement/intégration/préproduction/production.

En l’occurrence, l’utilisation de SqlConnectionStringBuilder s’est imposée pour basculer dynamiquement entre SQL Server et SQL Azure, à l’aide d’un code de ce type :

private string connectionString = null;

private string userIdAzure = "*****";
private string serverAzure = "*****";
bool azureContext = true;

public DataAccess()
{
connectionString = ConfigurationManager.ConnectionStrings["Connexion"].ConnectionString;
if (azureContext)
{
SqlConnectionStringBuilder scsb = new SqlConnectionStringBuilder(connectionString);
scsb.DataSource = serverAzure;
scsb.UserID = userIdAzure;
connectionString = scsb.ConnectionString;
}
}



On voit que le champ azureContext permet de basculer dynamiquement entre SQL Server et SQL Azure, ce qui est parfaitement envisageable tant que les limitations inhérentes à SQL Azure sont respectées.


Note : Dans mon cas, le mot de passe et le nom de la base de données sont identiques pour la base SQL Server et la base SQL Azure ; seuls l’User Id et le nom du serveur ont eu besoin d’être modifiés.

mardi 31 mai 2011

Microsoft Days 2011 à Strasbourg

Les Microsoft Days 2011 de Strasbourg auront lieu le 8 Juin prochain. Vous pouvez vous inscrire .

Pour ma part, j’y animerai 2 sessions pendant la matinée :

  • “Du Framework .NET 2.0 au Framework .NET 4.0 - Tout ce que vous avez manqué” avec Stéphanie Hertrich de Microsoft.
  • “Windows Azure - Approche pragmatique d'une révolution en marche”, avec mon collègue Philippe Geiger.

Je donne rendez-vous avec grand plaisir à tout ceux qui souhaitent participer à cet évènement.

Fonction d’exportation simple

Beaucoup d’applications proposent des fonctionnalités d’exportation de grilles de données vers des formats tels que CSV ou Excel. L’objectif de cet article est de proposer la fonction la plus simple et la plus générique possible pour répondre à cette problématique courante.

Ainsi, pour rendre la fonction la plus générique possible :

  • La réflexion est utilisée pour détecter dynamiquement les propriétés
  • Des paramètres optionnels (avec des valeurs par défaut) permettent d’utiliser la même fonction en faisant varier les détails d’implémentation
  • La fonction est une méthode d’extension sur IEnumerable<T>, ce qui la rend applicable dans quasiment tous les cas d’utilisation.

La fonction

Voici la fonction principale d’export :

public static class ExportationExtensions
{
public static string Export<TSource>(this IEnumerable<TSource> items,
bool includeTitleRow = true,
string header = "", string footer = "",
string rowPrefix = "", string rowSuffix = "",
string rowSeparator = "\r\n", string itemSeparator = ",",
Type ignoreFieldHavingAttributeType = null)
{
// serializable properties
var serializableProperties = typeof(TSource).GetProperties().AsEnumerable();
if (ignoreFieldHavingAttributeType != null)
serializableProperties = serializableProperties
.Where(pi => pi.GetCustomAttributes(false)
.All(o => o.GetType().FullName != ignoreFieldHavingAttributeType.FullName));

// result
return items.Aggregate(
string.Concat(header, serializableProperties
.Aggregate(rowPrefix,
(str, sp) => string.Concat(str, sp.Name, itemSeparator),
res => string.Concat(res, rowSuffix, rowSeparator))),
(row, item) => string.Concat(row, serializableProperties
.Aggregate(rowPrefix,
(str, sp) => string.Concat(str, sp.GetValue(item, null).ToString(), itemSeparator),
res => string.Concat(res, rowSuffix)), rowSeparator),
rows => string.Concat(rows, footer));
}
}



L’exportation produit par défaut des données au format CSV.


Utilisation


La fonction prévoit l’écriture éventuel de la ligne de titre, le préfixe et le suffixe de la chaine produite et de chaque ligne, ainsi que le séparateur entre chaque élément. En outre, afin de ne pas sélectionner certaines propriétés à exporter, la fonction élimine les propriétés sur lesquelles sont appliquées un attribut particulier (de la même manière que l’attribut XmlIgnore empêche la sérialisation XML du champ sur lequel l’attribut porte). Voici un exemple d’export d’une liste au format CSV puis sous forme de tableau HTML (ce qui permet l’ouverture directe par Excel par exemple) :

class Person
{
[DebuggerDisplay("")]
public string Nom { get; set; }
public string Prenom { get; set; }
public int Age { get; set; }
}

class Program
{
static void Main(string[] args)
{
List<Person> personnes = new List<Person>() {
new Person(){ Age = 55, Nom = "Gates", Prenom = "Bill"},
new Person(){ Age = 48, Nom = "Obama", Prenom = "Barack"},
new Person(){ Age = 26, Nom = "Damaj", Prenom = "Dany"}
};

Console.WriteLine(personnes.Export());

Console.WriteLine();

Console.WriteLine(personnes.Export(header: "<table>", footer: "</table>",
rowPrefix: "<tr><td>", rowSuffix: "</td></tr>",
itemSeparator: "</td><td>",
ignoreFieldHavingAttributeType: typeof(DebuggerDisplayAttribute)));

Console.ReadLine();
}
}



Champ d’application et perspectives


La fonction présentée ci-dessus a été écrite à l’occasion de la recherche d’une fonction d’Export CSV pour les tables Azure. Comme la plupart des fonctions très (trop?) génériques, ses performances ne sont pas excellentes en raison de l’utilisation de la réflexion et du type String en retour de la méthode (un fonctionnement par buffer serait préférable). D’autre part, le choix d’un séparateur entre éléments et d’un préfixe/suffixe pour chaque entité est celui qui semble être le plus logique, mais il ne convient sans doute pas à toutes les implémentations.

jeudi 19 mai 2011

Utilisation de MsBuild pour automatiser les déploiements

Le déploiement est une étape critique de la vie d’un projet. Cependant, la plupart du temps, il n’existe aucune procédure automatisée, et parfois même aucune documentation. Il existe pourtant de nombreux outils permettant d’automatiser la tâche de déploiement, parmi lesquels figurent NAnt et MSBuild par exemple.

Qu’est-ce que MSBuild?

MSBuild est le système de Build de Visual Studio, ce qui veut notamment dire que tous les fichiers projet (.csproj par exemple) utilisent ce système. Par exemple, si on crée une application console par Visual Studio et qu’on ouvre directement le fichier .csproj avec un éditeur de texte, on obtient :

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>04c1eb93-daac-4486-8781-7c0e8dd5967c</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ConsoleApplication1</RootNamespace>
<AssemblyName>ConsoleApplication1</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>



On voit que MSBuild est en fait basé sur XML, et que les fichiers MSBuild respectent un schéma bien précis. Ce dernier permet de définir notamment :



  • Des Target identifiés par leur nom, permettant de regrouper des tâches et d’organiser leur exécution.

  • Des Task représentant une action unitaire pour le système MSBuild. La liste des tâches disponible permet de donner rapidement une vue d’ensemble de ce qu’on peut faire avec MSBuild (on y retrouve les tâches associées à la compilation notamment). Le système prévoit une extensibilité par l’implémentation d’une interface ITask et par l’utilisation de la balise Import.

  • Des ItemGroup contenant des Item représentant des fichiers, la plupart du temps en vue d’une compilation.

  • Des PropertyGroup représentant de définir des propriétés personnalisées.

Un véritable mini-langage pour le déploiement


MSBuild ne s’arrête pas là. A l’aide des éléments When, Choose et Otherwise et des attributs Condition et DependsOn (pour les Target), on peut très rapidement développer une procédure de déploiement complexe qui répond aux problématiques courantes :



  • Création automatique de backups estampillés à la date du jour.

  • Notification automatique de l’équipe projet lorsqu’un nouveau déploiement a lieu.

  • Prise en compte des différences d’environnement entre déploiements en intégration/préproduction/production (notamment pour prendre les bons fichiers de configuration et les bons identifiants pour les serveurs de destination).

  • Lancement automatique de tests unitaires avant tout déploiement.

  • Tout autre traitement automatisé à l’aide de la tâche Exec.

En outre, de nombreuses tâches récurrentes (envoi de mails, compression, archivage sur le contrôleur de code source, copie sur serveur FTP, etc.) sont déjà développées dans le MSBuild Community Tasks Project et viennent largement consolider les tâches MSBuild de base.


On dispose finalement d’un véritable mini-langage compréhensible et extensible pour la réalisation de tâches de déploiement. Pour exécuter le fichier MSBuild et lancer un déploiement, il suffit d’utiliser l’outil msbuild du Framework .NET. Par exemple, en créant un .bat avec le code suivant, où BuildAll.xml est le fichier MSBuild et “Package” un Target à l’intérieur du fichier MSBuild qu’on souhaite exécuter explicitement :

call "%VS100COMNTOOLS%vsvars32.bat"
msbuild BuildAll.xml /t:Package



Un exemple concret


Pour finir, voici un exemple concret d’utilisation d’MSBuild, où la cible Package permet de :



  • Compiler un projet en utilisant comme répertoire de sortie Build\

  • D’exécuter des tests unitaires

  • De créer un fichier Release.txt contenant la date et l’heure courante dans le répertoire Build\

  • De compresser le répertoire Build\

  • De supprimer le répertoire temporaire Build\
<Project DefaultTargets="Package" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<MSBuildCommunityTasksPath>chemin de la dll…</MSBuildCommunityTasksPath>
</PropertyGroup>
<Import Project="Dependencies\MSBuild.Community.Tasks.Targets"/>

<PropertyGroup>
<SourceDir>répertoire contenant les sources…</SourceDir>
<BuildDir>Build\</BuildDir>
</PropertyGroup>

<Target Name="Build">
<RemoveDir Directories="$(BuildDir)" ContinueOnError="true" />
<MakeDir Directories="$(BuildDir)" ContinueOnError="false" />

<MSBuild Projects="$(SourceDir)\ExempleDeDossier\ExempleDeProjet.csproj"
Properties="Configuration=Release;DebugSymbols=false;DebugType=none;OutDir=$(MSBuildProjectDirectory)\$(BuildDir)"
BuildInParallel="True">
</MSBuild>

<Prompt Text="Build Terminated. Hit [ENTER] ..." />
</Target>


<Target Name="Test" DependsOnTargets="Build">
<Exec Command="MsTest /noresults /testmetadata:&quot;$(SourceDir)\ExempleDeFichierDeTests.vsmdi&quot;" />

<Prompt Text="Tests Terminated. Hit [ENTER] ..." />
</Target>

<Target Name="Package" DependsOnTargets="Build;Test">
<Time Format="yyyyMMddHHmmss">
<Output TaskParameter="FormattedTime" PropertyName="CurrentDate" />
</Time>

<WriteLinesToFile File="$(BuildDir)\Release.txt" Lines="$(CurrentDate)" Overwrite="true" />

<ItemGroup>
<ZipFiles Include="$(BuildDir)\**\*.*" />
</ItemGroup>

<Zip Files="@(ZipFiles)"
ZipFileName="$(MSBuildProjectDirectory)\LeFichierDeBackup.zip" ZipLevel="9" />

<RemoveDir Directories="$(BuildDir)" ContinueOnError="false" />

<Prompt Text="Package Terminated. Hit [ENTER] ..." />
</Target>
</Project>

jeudi 21 avril 2011

Enumération conditionnelle sur les collections

Après l’écriture du post précédent concernant la surveillance de l’énumération d’une collection au moyen de délégués, il apparait que la méthode (simple) mise en œuvre peut largement être étendue jusqu’à construire un ensemble complet de méthodes d’extension de IEnumerable<T> agissant non pas sur la collection elle-même comme les méthodes de System.Linq.Enumerable, mais sur son énumération. Par exemple, on peut parfaitement créer une nouvelle méthode EnumerateIf permettant de spécifier une condition lors de l’énumération. Si la condition n’est pas satisfaite, l’énumération sur l’élément courant de la collection est ignorée. Pour implémenter EnumerateIf, il suffit de déclarer la méthode d’extension suivante :

public static IEnumerable<TSource> EnumerateIf<TSource>(this IEnumerable<TSource> source,
Predicate<TSource> predicate)
{
EEnumerable<TSource> eenumerable = new EEnumerable<TSource>(source,
predicate);
return eenumerable;
}



Le nouveau constructeur d’EEnumerable<T> transmet la condition à son enumerator :

public EEnumerable(IEnumerable<TSource> source,
Predicate<TSource> enumerationCondition)
{
Source = source;
Enumerator = new EEnumerator<TSource>(source.GetEnumerator(), enumerationCondition, this);
}



Et le nouveau constructeur d’EEnumerator<T> affecte simplement le prédicat :

public Predicate<TSource> EnumerationCondition { get; set; }
public IEnumerator<TSource> Source { get; set; }
private EEnumerable<TSource> parent;

public EEnumerator(IEnumerator<TSource> source,
Predicate<TSource> enumerationCondition,
EEnumerable<TSource> parent)
{
Source = source;
EnumerationCondition = enumerationCondition;
this.parent = parent;
}



Comme pour le post précédent, la vraie logique se trouve en fait dans la méthode MoveNext, appelée effectivement au fur et à mesure de l’énumération. Ici, les lignes 6 à 8 réalisent le comportement souhaité en “sautant” les éléments de la Source interne qui ne satisfont pas la condition :


   1:  public bool MoveNext()
   2:  {
   3:      if (OnItemEnumerating != null)
   4:          OnItemEnumerating(Current);
   5:   
   6:      bool result = Source.MoveNext();
   7:      while (EnumerationCondition != null && !EnumerationCondition(Current) && result)
   8:          result = Source.MoveNext();
   9:   
  10:      if (OnItemEnumerated != null)
  11:          OnItemEnumerated(Current);
  12:      if (!result && parent.OnEnumerated != null)
  13:          parent.OnEnumerated(parent);
  14:      return result;
  15:  }



Ainsi, avec le code d’exemple suivant :

List<int> ints = new List<int>();
ints.Add(1);
ints.Add(2);
ints.Add(3);
ints.Add(4);

ints.EnumerateIf(i => i >= 3)
.Monitor(i => Console.WriteLine("Monitoring collection"),
i => Console.WriteLine("Collection monitored"),
i => Console.WriteLine("Current item is " + i),
i => Console.WriteLine("Next item will be " + i))
.ToList();



Le résultat affiché dans la console est bien le résultat attendu :


image


La méthode EnumerateIf n’est qu’un premier exemple. D’autres méthodes du même style sont envisageables :



  • EnumerateWhile

  • EnumerateRange (en spécifiant l’index minimal et maximal, en vue d’une pagination)

  • EnumerateWithDelay (ajout d’un Timer pour insérer une délai entre chaque appel à MoveNext)

  • EnumerateDuring (énumérer autant d’éléments que possible en un temps donné)

  • etc.

Evidemment, il reste à trouver des cas d’utilisation concrets pour ces différentes méthodes, mais d’une façon générale, il devient possible d’insérer du comportement pendant l’énumération des collections pour réaliser un traitement “ultime” au moment de la récupération des données.


Ainsi, à partir d’un ensemble de données disponibles (stockées par exemple dans un cache), on peut réaliser le filtrage (EnumerateIf) et la pagination (EnumerateRange) sans avoir besoin de créer une nouvelle collection (habituellement par les méthodes Where, Skip et Take), mais en modifiant uniquement la façon dont les données sont énumérées.

mardi 19 avril 2011

Surveiller l’énumération des valeurs d’une collection

Dans le Framework .NET, il n’existe pas de mécanisme pour surveiller l’évaluation des éléments dans une collection. Néanmoins, une telle fonctionnalité peut être intéressante dans le cas de requêtes LINQ complexes, ou pour mettre à jour un affichage au fur et à mesure du parcours de la collection. Un tel mécanisme peut être réalisé au moyen de nouvelles classes implémentant IEnumerable<T> et IEnumerator<T>. En effet, lorsqu’une collection est parcourue (par exemple par un foreach, ou en appelant la méthode ToList sur la collection), la méthode IEnumerable<T>.GetEnumerator est exécutée. Pendant le parcours, chaque élément de la collection est atteint par un appel à la méthode IEnumerator<T>.MoveNext. Il suffit donc d’insérer du comportement dans ces 2 méthodes. Par exemple, pour implémenter IEnumerable<T> :

class EEnumerable<TSource> : IEnumerable<TSource>
{
public IEnumerable<TSource> Source { get; set; }
public IEnumerator<TSource> Enumerator { get; set; }
public Action<IEnumerable<TSource>> OnEnumerating { get; set; }
public Action<IEnumerable<TSource>> OnEnumerated { get; set; }

public EEnumerable(IEnumerable<TSource> source,
Action<IEnumerable<TSource>> OnEnumerating,
Action<IEnumerable<TSource>> OnEnumerated,
Action<TSource> OnItemEnumerating,
Action<TSource> OnItemEnumerated)
{
Source = source;
this.OnEnumerating = OnEnumerating;
this.OnEnumerated = OnEnumerated;
Enumerator = new EEnumerator<TSource>(Source.GetEnumerator(),
OnItemEnumerating, OnItemEnumerated, this);
}

public IEnumerator<TSource> GetEnumerator()
{
if (OnEnumerating != null)
OnEnumerating(this);
return Enumerator;
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
if (OnEnumerating != null)
OnEnumerating(this);
return Enumerator;
}
}

Et pour implémenter IEnumerator<T> :

class EEnumerator<TSource> : IEnumerator<TSource>
{
public Action<TSource> OnItemEnumerating { get; set; }
public Action<TSource> OnItemEnumerated { get; set; }
public IEnumerator<TSource> Source { get; set; }

private EEnumerable<TSource> parent;

public TSource Current
{
get { return Source.Current; }
}

public EEnumerator(IEnumerator<TSource> source,
Action<TSource> OnItemEnumerating,
Action<TSource> OnItemEnumerated,
EEnumerable<TSource> parent)
{
Source = source;
this.OnItemEnumerating = OnItemEnumerating;
this.OnItemEnumerated = OnItemEnumerated;
this.parent = parent;
}

public void Dispose()
{
Source.Dispose();
}

object System.Collections.IEnumerator.Current
{
get { return Source.Current; }
}

public bool MoveNext()
{
if (OnItemEnumerating != null)
OnItemEnumerating(Current);
bool result = Source.MoveNext();
if (OnItemEnumerated != null)
OnItemEnumerated(Current);
if (!result && parent.OnEnumerated != null)
parent.OnEnumerated(parent);
return result;
}

public void Reset()
{
Source.Reset();
}
}



Enfin, on peut ajouter une méthode d’extension à IEnumerable<T> pour masquer l’utilisation de ces classes :

public static class Enumerable
{
public static IEnumerable<TSource> Monitor<TSource>(this IEnumerable<TSource> source,
Action<IEnumerable<TSource>> OnEnumerating, Action<IEnumerable<TSource>> OnEnumerated,
Action<TSource> OnItemEnumerating, Action<TSource> OnItemEnumerated)
{
EEnumerable<TSource> eenumerable = new EEnumerable<TSource>(source,
OnEnumerating, OnEnumerated, OnItemEnumerating, OnItemEnumerated);
return eenumerable;
}
}



Globalement, on peut dire que EEnumerable<T> agit comme un décorateur de IEnumerable<T>, en lui ajoutant la fonctionnalité de surveillance de la collection lors du parcours. La méthode Monitor nouvellement créée est accessible pour n’importe quelle collection et retourne elle-même un IEnumerable<T>, ce qui permet de ne pas briser les chaines d’appels aux autres méthodes d’extension de IEnumerable<T>. Voici un exemple d’utilisation de la méthode Monitor :

List<int> ints = new List<int>();
ints.Add(1);
ints.Add(2);
ints.Add(3);
ints.Add(4);

ints.Monitor(i => Console.WriteLine("Monitoring"),
i => Console.WriteLine("Monitored"),
i => Console.WriteLine("Monitoring " + i),
i => Console.WriteLine("Monitored " + i))
.ToList();



Ce code permet d’obtenir le résultat suivant :


image


On voit que le parcours de la collection, nécessaire lors de l’appel à ToList, effectue en fait 5 appels à MoveNext alors que la collection ne contient que 4 éléments. Le premier appel permet de se placer au niveau du premier élément de la collection. La collection est ensuite parcourue, élément par élément. Enfin, le dernier appel à MoveNext renvoie false, indiquant qu’il n’y a plus d’élément dans la collection.


On peut complexifier légèrement l’exemple pour voir qu’en fonction de la position de l’appel à Monitor par rapport aux autres méthodes d’extension, on obtient un résultat différent dans la console. Par exemple le code suivant :

ints.Where(i => i > 2)
.OrderByDescending(i => i)
.Monitor(i => Console.WriteLine("Monitoring collection"),
i => Console.WriteLine("Collection monitored"),
i => Console.WriteLine("Current item is " + i),
i => Console.WriteLine("Next item will be " + i))
.ToList();



Donne le résultat suivant :


image


On dispose donc d’un moyen simple pour surveiller l’énumération des valeurs d’une collection, tout en ayant la possibilité de réagir en insérant du code quelconque.

mercredi 13 avril 2011

Offre de découverte Azure

L’offre de découverte de la plateforme Azure vient d’être reconduite jusqu’au 30 Septembre. Certaines limites, comme la bande passante entrante et sortante, sont relevées (20Go entrants et 20Go sortants inclus par mois). Attention néanmoins aux éléments suivants :

  • Pour y souscrire, il faut obligatoirement indiquer un numéro de carte bancaire, comme pour l’offre de découverte précédente. Ceci implique de vérifier régulièrement sa consommation pour éviter les surprises en cas de dépassement.
  • Seule une seule instance extra-small peut tourner en permanence (750 Heures par mois). Une instance small est incluse également, mais seulement pour 25 Heures par mois. Le fait de multiplier le nombre d’instances ou d’augmenter la taille des instances entrainera donc systématiquement un dépassement si elles doivent tourner en permanence. En outre, cela veut dire qu’on peut difficilement expérimenter une combinaison “classique” Web Role/Worker Role gratuitement.
  • La base SQL Azure d’un Go n’est incluse que pendant les 3 premiers mois. Ceci veut dire qu’en s’inscrivant maintenant, elle ne sera pas incluse jusqu’à la fin de l’offre (30 Septembre).

L’offre permet néanmoins de monter un environnement raisonnable en utilisant toutes les briques d’Azure (Compute, Storage, Service Bus & Access Control, SQL Azure).

samedi 26 mars 2011

Intégrer un composant Silverlight en WPF

Intégration

Il est parfois nécessaire d’intégrer un composant Silverlight dans une application WPF, étant donné que certains contrôles Silverlight n’existent pas en WPF. La méthode la plus courante consiste à placer un WebBrowser dans l’application WPF, et d’utiliser la méthode Navigate en indiquant l’URL d’une page contenant le composant Silverlight à intégrer. Ainsi, dans le XAML côté WPF :

<WebBrowser Name="wb"  />



Dans le constructeur correspondant côté .xaml.cs :

public MainWindow()
{
InitializeComponent();
wb.Navigate("http://aaa.bbb.com/toto.aspx");
}



Le résultat est visuellement excellent et remarquablement fluide. En plus de l’unification des développements (Xaml + C#), on dispose donc d’un moyen très simple et efficace pour tirer profit de développements réalisés en Silverlight a priori incompatibles avec WPF sans recompilation du code source.


Une fois l’intégration réalisée, le problème qui se pose immédiatement est celui de la communication entre le composant Silverlight et le reste de l’application WPF. Certains articles expliquent comment réaliser cette communication en passant par le document HTML, seul point de liaison disponible depuis l’application Silverlight et le WebBrowser WPF. Néanmoins, le moins que l’on puisse dire est que cette méthode n’est pas maintenable et qu’elle réduit l’évolutivité de l’architecture. Un véritable bricolage.


Une méthode de communication élégante


Pour faire communiquer l’application WPF avec le composant Silverlight, le mieux est encore d’utiliser la technologie prévue à cet effet : WCF. Plus précisément, il s’agit de concevoir un service WCF hébergé sur le serveur Web de l’application hébergeant le composant Silverlight. L’application WPF doit pouvoir envoyer des informations au composant Silverlight (et inversement), en passant systématiquement par le service WCF. Par conséquent, l’application WPF et l’application Silverlight doivent pouvoir :



  • Envoyer des messages au service WCF
  • Recevoir des messages provenant du service WCF.

Le service WCF agit comme un relai et doit pouvoir :



  • Recevoir des messages de l’application WPF et les relayer à l’application Silverlight
  • Recevoir des messages de l’application Silverlight et les relayer à l’application WPF.

Le service WCF a donc besoin de pouvoir recevoir ET envoyer des messages : un CallbackContract va être utilisé. D’autre part, les communications bidirectionnelles à permettre correspondent à un binding pollingDuplexHttpBinding avec Silverlight et à un binding wsDualHttpBinding avec WPF. Pour pouvoir appliquer ces 2 binding au service WCF, il faut les endpoints correspondants :

<services>
<service name="WpfSilverlightCommunication.CommunicationService">
<endpoint
address="silverlight"
binding="pollingDuplexHttpBinding"
bindingConfiguration="multipleMessagesPerPollPollingDuplexHttpBinding"
contract="WpfSilverlightCommunication.ICommunicationService">
</endpoint>
<endpoint
address="wpf"
binding="wsDualHttpBinding"
contract="WpfSilverlightCommunication.ICommunicationService">
</endpoint>
<endpoint
address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange"/>
</service>
</services>



Le reste de la configuration WCF (system.serviceModel) peut être configuré de façon classique :

<extensions>
<bindingExtensions>
<add name="pollingDuplexHttpBinding"
type="System.ServiceModel.Configuration.PollingDuplexHttpBindingCollectionElement,System.ServiceModel.PollingDuplex, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</bindingExtensions>
</extensions>
<bindings>
<wsDualHttpBinding>
<binding sendTimeout="00:00:05" />
</wsDualHttpBinding>
<pollingDuplexHttpBinding>
<binding name="multipleMessagesPerPollPollingDuplexHttpBinding"
duplexMode="SingleMessagePerPoll" sendTimeout="00:00:05"/>
</pollingDuplexHttpBinding>
</bindings>



Ainsi, l’application Silverlight pourra accéder au service WCF par l’adresse <adresseService>.svc/silverlight et l’application WPF par l’adresse <adresseService>.svc/wpf. Le service WCF fonctionne de la façon suivante :



  • Le mode d’instance est InstanceContextMode.Single, parce que l’application WPF et l’application Silverlight ne se connectent pas avec le même identifiant de session. Par conséquent, avec la configuration par défaut (PerSession), faire communiquer les 2 instances du service serait plus compliqué.
  • Le service contient une méthode Ping qui enregistre en premier lieu les canaux de communication vers l’application WPF et vers l’application Silverlight.
  • Chaque couple WPF/Silverlight est identifié par un Guid que partagent les 2 applications.

Le code du contrat de service WCF est le suivant :

[ServiceContract(CallbackContract=typeof(ICommunicationServiceHandler))]
public interface ICommunicationService
{
[OperationContract]
bool Ping(string id);

[OperationContract(IsOneWay=true)]
void Order(string appId, List<string> values);
}

[ServiceContract]
public interface ICommunicationServiceHandler
{
[OperationContract(IsOneWay = true)]
void Receive(List<string> values);
}



Et le service :

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single, ConcurrencyMode=ConcurrencyMode.Multiple)]
public class CommunicationService : ICommunicationService
{
Dictionary<string, ICommunicationServiceHandler> silverlightAppChannels = new Dictionary<string, ICommunicationServiceHandler>();
Dictionary<string, ICommunicationServiceHandler> wpfAppChannels = new Dictionary<string, ICommunicationServiceHandler>();

public void Order(string appId, List<string> values)
{
string endpointExtension = OperationContext.Current.EndpointDispatcher.EndpointAddress.Uri.Segments.Last();
if (silverlightAppChannels[appId] != null && wpfAppChannels[appId] != null)
{
switch (endpointExtension)
{
case "silverlight":
wpfAppChannels[appId].Receive(parcelleIds);
break;
case "wpf":
silverlightAppChannels[appId].Receive(parcelleIds);
break;
default:
break;
}
}
Ping(appId);
}

public bool Ping(string id)
{
string endpointExtension = OperationContext.Current.EndpointDispatcher.EndpointAddress.Uri.Segments.Last();
switch (endpointExtension)
{
case "silverlight":
silverlightAppChannels[id] = OperationContext.Current.GetCallbackChannel<ICommunicationServiceHandler>();
break;
case "wpf":
wpfAppChannels[id] = OperationContext.Current.GetCallbackChannel<ICommunicationServiceHandler>();
break;
default:
break;
}
return true;
}
}



La méthode principale, Order, effectue le relai en appelant la méthode Receive de l’autre canal de communication précédemment enregistré par la méthode Ping.


Pour faire en sorte que l’application WPF et l’application Silverlight partagent le même Guid, l’application WPF peut le créer et le transmettre en paramètre d’URL :

private static Guid appId = Guid.NewGuid();

public MainWindow()
{
InitializeComponent();
wb.Navigate("<adressePage>?guid=" + appId.ToString());
}



Pour le reste, l’application WPF agit de façon tout à fait classique comme client de l’endpoint avec le binding wsDualHttpBinding : après ajout de la référence de service, la fenêtre WPF peut implémenter le contrat de callback WCF et contenir une instance du proxy. Il faut simplement penser à appeler une fois la méthode Ping en passant le Guid au chargement de la fenêtre.


De même, l’application Silverlight agit comme un client tout à fait classique de l’endpoint ayant le binding PollingDuplexHttpBinding. Pour récupérer le Guid initialisé par l’application WPF, l’application Silverlight peut procéder de la manière suivante, par exemple pour l’appel initial à Ping :

proxy.PingAsync(HtmlPage.Document.QueryString["guid"]);



Conclusion


Une méthode originale et élégante a été conçue pour faire communiquer une application WPF avec une application Silverlight qui serait rendue dans un WebBrowser. On pourrait facilement étendre cette méthode selon plusieurs axes :



  • Réalisation d’autres types de relais, de type hub (broadcast) ou routeur par exemple.
  • Communication entre une application Winforms et un composant Silverlight avec le composant WebBrowser de WinForms.
  • Synchronisation des 2 applications qui pourraient s’échanger périodiquement leurs données.
  • Introduction d’une queue de messages et maintien des messages en attente dans le service WCF, en cas de perte de connexion.

mardi 22 mars 2011

Suivre l’activité d’une classe

Dans de nombreux cas, il peut être intéressant de suivre l’activité d’une classe quelconque, en particulier pour logger tous les appels à toutes ses méthodes. Quelques exemples :

  • Suivre l’activité d’un service Web.
  • Enregistrer tous les appels à une couche d’accès aux données.
  • Enregistrer tous les appels sur une classe réalisant un accès à une ressource externe, comme une classe réalisant les accès au disque, à un FTP, une classe encapsulant l’envoi de mails, etc.

A priori, il n’existe aucun mécanisme permettant de logger automatiquement tous les appels à une classe quelconque, de préférence sans modifier le code appelant ni le code appelé. Parmi les 4 sections suivantes, les 3 premières présentent des méthodes approchantes. La dernière présente une méthode originale basée sur les Code Contracts permettant d’atteindre le résultat recherché.

IntelliTrace & Rapports de performance

Il s’agit des méthodes les plus génériques. Elles permettent de tout savoir sur l’exécution d’un programme, mais sont limitées à la production de données de trace. Il ne sera donc pas possible de configurer totalement les données produites. De plus, seule la version Ultimate de Visual Studio 2010 possèdent ces fonctionnalités.

Concernant l’utilisation d’IntelliTrace, il est nécessaire d’activer l’option Evènements IntelliTrace et information sur les appels pour obtenir des informations sur les appels de méthodes :

image

Avec le bout de code suivant en exemple :

static void Main(string[] args)
{
Toto toto = new Toto();
toto.Operation();
}



La fenêtre IntelliTrace affiche à l’exécution :


image


On a bien une trace de tous les appels à la classe Toto. Bien entendu, dans le cas d’une application professionnelle complexe, le nombre d’appels devient tel que cette méthode n’est pas envisageable.


Concernant l’utilisation des rapports de performance, il suffit de lancer l’assistant performance :


image


Et de choisir Instrumentation :


image


Après exécution du programme, le profileur produit un fichier .vsp qui présente un grand nombre d’informations statistiques sur l’exécution. Par exemple, en basculant en mode d’affichage Fonctions, on voit entre autres que la méthode Operation a été appelée une seule fois :


image


L’IntelliTrace et les rapports de performance ne permettent cependant pas de logger explicitement les appels à une classe en particulier, et il n’est pas raisonnable de les utiliser sur une application de production pendant une durée trop longue. Il s’agit donc seulement de solutions approchantes au problème énoncé.


Méthodes existantes


Bien souvent, s’il y a besoin de suivre l’activité d’une classe en particulier, c’est que le contexte applicatif est propice à ce besoin. Dans ces cas, les technologies existantes anticipent le besoin en proposant les mécanismes nécessaires. Par exemple, pour logger les appels à un Service WCF, il est possible de créer un EndpointBehavior personnalisé dans lequel on passera avant/après chaque requête (methodes AfterReceiveRequest et BeforeSendReply). Autre exemple, en WebForms ASP.NET, tout accès à une page ASPX peut être détecté au moyen de l’évènement Page_BeginRequest. Enfin, pour logger tous les appels aux bases de données, le problème est souvent contourné par l’utilisation de SQL Server Profiler ou de tout autre outil similaire avec d’autres SGBDR.


Toutes ces méthodes sont efficaces, mais elles ne répondent au problème énoncé que dans des cas particuliers.


Design Patterns


Certains design patterns, comme l’Observer ou le Decorator, peuvent être utilisés pour ajouter une fonction de Log à une classe existante. Néanmoins, pour les mettre en place, il faut soit modifier le code de la classe à logger, soit modifier le code appelant. Par exemple, dans le cas du pattern Decorator, il faut :



  • extraire une interface ou une classe abstraite de la classe à surveiller,
  • implémenter une autre classe de log qui implémente la classe abstraite ou l’interface
  • modifier le code appelant la méthode pour ajouter la fonction de log.

On peut bien entendu utiliser le pattern Factory pour ajouter systématiquement la fonction de log, mais cela ne fait que déplacer le problème, puisque le code appelant doit alors solliciter la Factory. En outre, ceci oblige à appliquer le pattern pour chaque classe à surveiller et à “doubler” les méthodes, donc on augmente la complexité et on ajoute un niveau d’abstraction. Tout ceci est disproportionné par rapport à un problème simple a priori.


Utilisation détournée des Code Contracts


Inclus dans mscorlib à partir du Framework .NET 4.0, les Code Contracts proposent des fonctionnalités de validation de code activables par un certain nombre d’options de compilation. A la base des Code Contracts, il y a 3 mécanismes permettant de réaliser cette validation de code :



  • La méthode Contract.Requires permettant de spécifier une précondition sur une méthode
  • La méthode Contract.Ensures permettant de spécifier une postcondition sur une méthode
  • L’attribut ContractInvariantMethod appliqué à une méthode, activant une validation effectuée “à la fin de chaque méthode publique de la classe”.


C’est bien sûr l’attribut ContractInvariantMethod qui va être utilisé comme support pour réaliser la fonction de log. En effet, l’idée est de profiter de ce mécanisme pour réaliser un log plutôt qu’une validation. Pour comprendre comment fonctionnent réellement les Code Contracts, la première étape consiste à activer l’option correspondante dans l’onglet “Code Contracts” (des paramètres du projet) disponible si la fonctionnalité est installée :


image


Dans la classe Toto à surveiller, il suffit d’ajouter une méthode ayant l’attribut ContractInvariantMethod de la façon suivante :

class Toto
{
public void Operation()
{
}

[ContractInvariantMethod]
void aaa()
{
Contract.Invariant(false);
}
}



Après compilation et utilisation de Reflector sur l’assembly produite, on voit que l’activation des Code Contracts modifie la compilation du programme en ajoutant automatiquement, à la fin de chaque méthode publique, un appel à la méthode aaa du bout de code précédent, rebaptisée $InvariantMethod$. Par exemple, pour la méthode Operation qui ne faisait rien, on a en fait :


image


Par l’utilisation des Code Contracts, on a donc un mécanisme automatisé pour appeler une méthode après n’importe quel appel d’une méthode publique d’une classe. Ceci correspond bien au but recherché, encore faut-il arriver à injecter du code pour effectuer la surveillance de la classe. En effet, si on essaie de modifier la méthode aaa pour y réaliser autre chose que des appels successifs à Contract.Invariant, alors une erreur de compilation survient. Cependant, si on retourne dans les propriétés du projet, on voit qu’il est possible de définir une classe pour Custom Rewriter Methods dans laquelle on va pouvoir injecter du code quelconque :


image


Les méthodes de cette classe seront appelées uniquement si une validation par les Code Contracts échoue, c’est pourquoi il y a cette instruction dans la méthode aaa permettant de faire échouer systématiquement la validation :

Contract.Invariant(false);



Dans la classe indiquée pour Custom Rewriter Methods sont attendues des méthodes statiques ayant des signatures particulières. Pour celle qui nous intéresse ici, c’est-à-dire Invariant, on pourrait définir la méthode suivante :

public static class RuntimeFailureMethods
{
public static void Invariant(bool cond, string userMsg, string condText)
{
StackFrame[] sf = new StackTrace().GetFrames();
if (sf.Length >= 3)
{
MethodBase mb = sf[2].GetMethod();
Console.WriteLine("Appel de {0}.{1}", mb.DeclaringType.Name, mb.Name);
}
}
}



Ce code détermine quelle méthode a abouti à l’appel à Invariant en accédant au 3e élément de la pile des appels. En effet, le premier élément est toujours la méthode courante, le deuxième est la méthode aaa, et le troisième est bien la méthode d’origine appelée sur notre classe à surveiller. En exécutant toujours notre même Main, on obtient :


image



Les Code Contracts permettent donc bien de suivre automatiquement l’activité d’une classe quelconque, sans modification du code appelant ni du code appelé.