
Vendredi, je suis tombé sur un article de Julien Dollon qui m'a tout simplement mis une claque... Je vous recommande fortement d'aller le lire d'ailleurs...
Depuis quelques mois, comme vous l'avez probablement vu si vous suivez mes articles et mes liens, je ne cesse de me plaindre des choix faits par Microsoft. Qu'il s'agisse de Windows 8, du fait d'avoir laissé tomber Silverlight et WPF au profit de HTML5/JavaScript, de leur Windows App Store, ...
Seulement voilà. Ce n'est pas à moi de remettre tout ça en cause. J'ai beau dire que Visual Studio est un IDE génial, que Silverlight est plus sympa que HTML5/JavaScript et que WP7 est le meilleur OS mobile, ça ne changera rien à l'avenir. Microsoft a un train de retard sur ses concurrents. Que ce soit au niveau des tablettes ou des smartphones. J'ai même de plus en plus l'impression que Windows 8 sera mal accueilli, car trop dépaysant et pas pratique d'utilisation pour un PC (ce qui est un comble pour un Windows !
).
Même si je développais aujourd'hui une super application sur Windows Phone 7, combien de personnes l'utiliseraient ? 50 ? 100 ? Et combien de personnes l'utiliseraient si je la développais pour iPhone ou Android ? 1000 ? 2000 ?
Le marché a évolué. A changé. Et moi, comme tous ceux dont je me moquais il y a 2/3 ans, je suis resté agrippé à mes technos préférées sans vouloir en changer. Bouh HTML5 ! Bouh JavaScript ! Bouh Ruby ! Moi je suis un développeur .Net namého ! 
Microsoft a rendu le développement tellement simple et tellement intuitif que je suis devenu accro. Et j'ai arrêté de regarder ce qui se passait ailleurs. Je suis devenu un dinosaure de l'informatique. A même pas 25 ans, c'est triste non ? 
Sauf que non. Il est temps pour moi de réagir. Je suis un ingénieur logiciel. J'aime le .Net, mais il n'y a pas que ça. Il y a même probablement mieux aujourd'hui ! Je dois développer pour me faire plaisir, mais aussi pour offrir des services et des applications aux gens et pour apprendre de nouvelles choses ! Je dois m'adapter au marché d'aujourd'hui, et au marché de demain. Même si ce marché est celui des smartphones et des tablettes ! Et puis, peut être que j'aimerais ça au final... Qui sait ?
Alors reprenons : "Bonjour, je m'appelle Tommy, je suis ingénieur développeur, et vous ?"
image modifiée de Rego - d4u.hu, sous licence CC

Windows 7, c'est has been... Microsoft ils ont fait un truc pour ça : Windows 8.
Comme vous le savez tous, la bêta de Windows 8 devrait sortir d'ici une douzaine de jours, et Microsoft a commencé de dévoiler ses nouvelles fonctionnalités : le système de copie avancé, la disparition du bouton démarrer, l'inclusion de Office dans la version tablette, la fin du bandeau, les images comme mots de passe, ...
Bref, quelques excellentes choses, mais le principal défaut de ce système d'exploitation est toujours le même pour moi : il se veut hybride PC / Tablettes.
Je ne vais pas vous resservir mon discours sur le fait que les PC et les tablettes sont des outils à vocations totalement différentes. Non. Je vais juste essayer de comprendre avec vous pourquoi Microsoft a fait ces choix, et ce que j'aurais tenté à leur place...
Déjà, cet OS est pour skyzophrènes. Deux bureaux totalement différents, l'un orienté tactile / applications simples, l'autre orienté bureautique / applications sérieuses. L'idée de départ n'est pas si mauvaise (pouvoir habituer les utilisateurs à autre chose sans les déstabiliser au point de les perdre), mais vous vous voyez jongler sans arrêt entre les deux interfaces ? Vous vous voyez utiliser l'interface Metro à la souris ? Vous vous voyez avoir un navigateur n'acceptant ni Flash ni Silverlight ?
Mais pourquoi ce choix ? Pourquoi cette évolution ? D'après moi, pour 2 raisons :
- Que leur OS soit parfaitement compatible avec les tablettes, pour être les premiers à proposer un OS "avancé" pour tablettes.
- Pouvoir instaurer un magasin d'applications "Windows Store" (et donc profiter des 30% de "royalties" sur chaque application vendue).
Bon, le premier point est une aberration, j'en ai déjà assez parlé. Réfléchissons, si vous le voulez bien, au deuxième point.
Windows est le système d'exploitation connu par la majorité des gens comme "buggué", "lent", "lourd", "plein de virus", ... Intéressant. Bizarrement, je n'ai jamais eu le moindre problème sérieux avec Windows. Mon PC, qui a déjà plus de 3 ans, a toujours été rapide, puissant, n'a jamais planté, et je n'ai jamais eu de virus. Suis-je un surhomme ? Non, je fais juste attention à ce que j'installe.
Plutôt que de créer son bandeau "tactile" rempli d'applications, notre éditeur d'OS préféré n'aurait-il pas pu réfléchir sur comment révolutionner Windows 7 ? Le système d'exploitation très agréable auquel les gens sont habitués ?
Car il ne faut pas se leurrer. La majorité des gens restent sous Windows par habitude. Mais dès qu'on va commencer à casser leurs habitudes, ils vont de plus en plus remettre leur choix en cause, et peut être même envisager de partir chez Mac. (Après tout, Apple c'est la classe aujourd'hui. Hum !)
Mais qu'est ce que Microsoft aurait pu faire ? Creuser le véritable problème. Le principal problème des PC d'aujourd'hui est que les gens installent un peu tout et n'importe quoi, sans jamais faire attention à ce qu'ils cochent ou acceptent. Le second problème est qu'un PC neuf possède déjà des dizaines d'applications du constructeur qui ralentissent énormément le PC. A tel point que j'ai déjà vu des PC neufs bien plus lents que mon PC vieux de 3 ans.
A la place de Microsoft (oui, je n'ai pas froid aux yeux, mais c'est mon blog après tout, je dis ce que je veux), j'aurais tenté autre chose :
- Interdire aux fabricants de PC de préinstaller des logiciels, à l'exception des pilotes nécessaires au bon fonctionnement de la machine.
- Garder le système d'exploitation tel qu'il est actuellement, en ne faisant que quelques améliorations techniques (mettre une option "dock d'applications" pour remplacer la barre des tâches, améliorer le bureau et le menu démarrer, ...) et graphiques.
- Mettre en place une hiérarchisation des applications et de leurs dépendances claire et obligatoire. Ainsi, pour chaque application installée ou à installer, on devrait voir immédiatement : ce qu'elle installe, ce dont elle dépend et les outils secondaires qu'elle proposer d'installer (décochés par défaut !).
- Intégrer un catalogue d'applications, comme ce qu'ils veulent faire sous Windows 8, mais d'applications Windows classiques (pouvant donc être développées dans n'importe quel langage...). Ces applications devraient remplir certaines normes pour être acceptées : pas de toolbars ou autres spywares intégrés, hiérarchie renseignée, respect de la vie privée, ... Permettre également la connexion à un dépôt d'applications autre que l'officiel (redondance, applications d'entreprise, ...).
- Simplifier le processus de désinstallation d'applications : un tri par installation et par date. Ainsi, en supprimant par exemple Daemon Tools, on supprime automatiquement TOUT ce que ce logiciel a installé. Pas uniquement l'application : ses sous programmes, ses librairies et ses dépendances inutilisées ailleurs.
- Bloquer, par défaut, l'installation d'applications sans passer par ce Store, blocage pouvant être désactivé facilement par mot de passe.
- Garder l'UAC, mais le rendre plus pertinent (demande du mot de passe, affichage uniquement hors processus Store, ...). Après tout, qui a déjà vu l'UAC sauver son PC ?
- Continuer d'encourager le développement WPF / Silverlight (compatibilité Windows et Windows Phone, et même Mac et Linux à travers Silverlight et Moonlight !).
- Sortir une version modifiée de WP7 spécialement conçue pour les tablettes (WP7 est un système d'exploitation excellent... Pourquoi ne pas continuer sur cette lancée ?).
- Inclure par défaut Office complet dans cet OS (geste fort et moins de piratage).
- Et, forcément, laisser tomber WinRT (le langage de développement dédié aux applications Windows 8).
Au final, ma version "personnelle" de Windows 8 ne conviendrait-elle pas davantage aux habitués tout en renforçant la puissance et la sécurité de leurs PC ? Permettrait-elle toujours de configurer son Windows comme on le veut ? Permettrait-elle l'installation et la distribution facile d'applications, qu'elles soient certifiées ou non ?
Pour terminer, j'ai peur que la direction prise par Microsoft ne lui fasse perdre 2 marchés : celui des utilisateurs expérimentés qui ne veulent pas que leur PC de travail se transforme en gros smartphone hyperconnecté, et celui des gens qui veulent la simplicité mais qui restent par habitude et qui, du coup, iront voir ailleurs.
Rendez-vous dans quelques mois.

Ces derniers temps, j'ai pas mal réfléchi à la meilleure façon de séparer un projet .Net en couches. Classes métier, accès simplifié à la BDD, tests unitaires avec bouchons, injection de dépendances, mapping, ... Tellement de problématiques à gérer lorsqu'on crée une architecture .Net.
Du coup je me suis fixé un objectif simple : créer une architecture de solution .Net "propre, avec
- des couches bien séparées (classes métier, logique métier, accès aux données, services, tests unitaires, interfaces utilisateur),
- aucun Framework pour les tests unitaires (histoire que le code reste simple à appréhender et à comprendre pour un débutant),
- de l'injection de dépendances pour pouvoir brancher facilement les tests unitaires sur des "bouchons",
- enfin, un accès aux données simplifié, sans SQL ni procédures stockées (donc avec Linq et Entity Framework).
Au final, j'ai créé un simpliste "moteur de blog", capable d'afficher des articles puisés dans une BDD SQL Server 2008 Express. Pour éviter que ce beau projet .Net ne se perde dans les méandres de mon disque dur avant de sombrer dans l'oubli, je me suis dit que le partager avec vous serait le meilleur moyen de lui offrir une seconde vie. Qui sait, peut être sera-t-il utile à de nombreuses personnes ! 
Le voici donc : projet HTA.DemoBlog.zip (1,54 mb). Le code source est sous licence LGPL, donc vous pouvez l'utiliser comme bon vous semble ! 
Voici, déjà, ce qu'il y a à noter sur cette architecture (pour chaque couche, j'ai créé un solution folder) :
1) Couche Business
- BusinessEntities : les objets métier, qu'on manipulera dans l'application. L'intérêt est d'avoir des objets complètement détachés de la BDD et qu'on pourra manipuler dans une autre application, dans une bibliothèque de classes, dans un module à part, ... Les objets ne sont vraiment que des conteneurs indépendants de tout le reste.
- BusinessLogic : Tous les traitements métier. Ici on retrouvera la logique métier, les Try Catch, les calculs compliqués, les logs, ... Ce code sera celui utilisé par les applications (qu'il s'agisse de Winforms, d'ASP.Net, de WPF, ...). Cette couche interagit avec la couche DataAccess et utilise les objets BusinessEntities.
2) Couche Resources
- DataAccessInterfaces : les interfaces qui définiront les méthodes de la couche DataAccess.
Intérêt double : pouvoir utiliser des bouchons (mock) qui implémentent cette interface dans les tests unitaires, afin d'avoir des tests unitaires indépendants de la BDD et donc qui peuvent être lancés à chaque build / release.
L'autre intérêt est de pouvoir changer de source de données ou les méthodes qui interagissent avec la BDD sans avoir à recoder des choses dans d'autres couches (vu qu'elles implémentent ces interfaces et non pas les objets DataAccess directement).
- DataAccess : la couche qui interroge la BDD. Elle renvoie des objets BusinessEntities et possède des méthodes définies dans DataAccessInterfaces. Vu qu'elle est détachée, on peux utiliser ce qu'on veut pour stocker nos données : Entity Framework, Linq 2 Sql, des procédures stockées, des fichiers XML, ... Si on doit changer de source de données ou de façon d'exploiter notre source de données, on n'aura qu'à recréer une couche qui implémentera les interfaces DataAccessInterfaces, et le tour sera joué.
3) Couche Tests
- Tests.Mock : des classes bouchon (donc avec des données en dur) qui implémentent les interfaces DataAccessInterfaces.
- Tests : les tests unitaires. Ces tests testent la couche BusinessLogic via les mocks plutôt que via la couche DataAccess (afin de ne tester que la logique métier, sans risquer de modifier les données en base).
4) Couche Interfaces (UI)
- La couche qui contiendra les interfaces utilisateur (WebApp, Winforms, WPF, ASP.Net MVC, ...) qui utiliseront uniquement le code métier dans la couche BusinessLogic et qui manipulera des objets BusinessEntities.
5) Couche Core
- La couche Core contient des méthodes génériques utiles au projet mais qui pourront être réutilisées / utiles pour d'autres projets. (Cryptographie, Logs, ...)
6) Couche Database
- Le projet Database permet de gérer le contenu de la BDD SQL Server, de mettre à jour les procédures stockées et de modifier les scripts de déploiement, sans avoir à le faire à la main.
7) Couche Service
- Cette couche permet de créer des services qui utilisent la couche métier et donc de rendre le code accessibles de l'extérieur. (Pour l'utiliser, par exemple, via une application web distante ou via une application Windows Phone 7).
L'architecture ressemble donc à ceci :

Voyons maintenant le code un peu plus en détail.
Dans un premier temps, intéressons-nous à la classe ArticleBE, de la couche BusinessEntities, qui définit la structure d'un article :
public class ArticleBE : ICloneable
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public string Author { get; set; }
public string Tags { get; set; }
public DateTime Date { get; set; }
public object Clone()
{
ArticleBE result = new ArticleBE()
{
Id = Id,
Date = Date,
Author = Author.Clone() as string,
Content = Content.Clone() as string,
Tags = Tags.Clone() as string,
Title = Title.Clone() as string
};
return result;
}
}
Comme vous pouvez le voir, le code est vraiment le plus simple possible. Nous avons juste nos propriétés et la méthode Clone (j'expliquerais l'intérêt de la méthode Clone plus tard). Pas de dépendances, pas de contraintes, pas de spécificités, ... ce code peut véritablement être réutilisé n'importe où. Il est possible, dans le cas d'une application demandant une exposition de services, de rendre notre classe Serializable.
Maintenant, nous allons travailler sur la couche DataAccessInterfaces. Dans cette couche, nous allons définir les interfaces et donc les méthodes que notre couche d'accès aux données devra mettre à disposition. Ici, étant donné que nous ne travaillons que sur une table, nous n'allons créer qu'une interface, à savoir IArticleDA :
public interface IArticleDA : IDisposable
{
List<ArticleBE> GetArticles();
ArticleBE GetArticleById(int idArticle);
void AddArticle(ArticleBE article);
void EditArticle(ArticleBE article);
void DeleteArticle(ArticleBE article);
void Save();
}
Le code, encore une fois, est assez simple. Nos classes qui implémenteront IArticleDA devront nous fournir toutes ces méthodes + Dispose, puisque nous implémentons IDisposable.
Maintenant que cette interface est définie, nous allons voir la partie accès aux données. Comme vous pouvez le voir, j'ai ajouté au projet DataAccess un ADO.Net Entity Data Model pour pouvoir interroger la base de données très facilement via Linq. Cependant, pour pouvoir transformer nos objets Entity en objets BusinessEntities, j'ai créé une class ArticleMap dans le dossier Mapping :
public static class ArticleMap
{
internal static List<ArticleBE> Map(List<Article> articles)
{
if (articles == null || articles.Count == 0)
{
return new List<ArticleBE>();
}
List<ArticleBE> result = new List<ArticleBE>();
foreach (Article article in articles)
{
result.Add(Map(article));
}
return result;
}
internal static ArticleBE Map(Article article)
{
if (article == null)
{
return null;
}
ArticleBE result = new ArticleBE();
result.Id = article.IdArticle;
result.Author = article.Author;
result.Content = article.FullContent;
result.Date = article.Date;
result.Tags = article.Tags;
result.Title = article.Title;
return result;
}
internal static Article Map(ArticleBE article, Article result = null)
{
if (result == null)
{
result = new Article();
}
result.Author = article.Author;
result.FullContent = article.Content;
result.Date = article.Date;
result.Tags = article.Tags;
result.Title = article.Title;
return result;
}
}
Le code est, encore une fois, très simple. Déjà, toutes les méthodes sont en internal car elles ne doivent être accessibles qu'aux classes de notre projet DataAccess. Le mapping Article => ArticleBe est simple, mais le mapping ArticleBE => Article a une petite spécificité. En effet, dans le constructeur, j'ai passé un paramètre facultatif : un objet Article.
Pourquoi ? Lorsqu'on veut mettre à jour un objet Article en base depuis un objet ArticleBE, plutôt que d'avoir à modifier chaque propriété dans notre code d'accès aux données, il suffit d'appeler la méthode Map avec comme second paramètre notre objet Article à mettre à jour. (Il s'agit de la méthode la plus simple que j'ai pu trouver pour mettre à jour un objet Entity à partir d'une classe BusinessEntities.)
Intéressons-nous ensuite au code de notre classe ArticleDA, qui implémente IArticleDA :
public class ArticleDA : IArticleDA
{
private HTADemoBlogEntities _entities = null;
public ArticleDA()
{
_entities = new HTADemoBlogEntities();
}
public List<ArticleBE> GetArticles()
{
return ArticleMap.Map(_entities.Article.OrderByDescending(e => e.IdArticle).ToList());
}
public ArticleBE GetArticleById(int idArticle)
{
return ArticleMap.Map(_entities.Article.Where(e => e.IdArticle == idArticle).FirstOrDefault());
}
public void AddArticle(ArticleBE article)
{
_entities.Article.AddObject(ArticleMap.Map(article));
}
public void EditArticle(ArticleBE article)
{
Article existingArticle = _entities.Article.Where(e => e.IdArticle == article.Id).FirstOrDefault();
if (existingArticle != null)
{
ArticleMap.Map(article, existingArticle);
}
}
public void DeleteArticle(ArticleBE article)
{
Article existingArticle = _entities.Article.Where(e => e.IdArticle == article.Id).FirstOrDefault();
if (existingArticle != null)
{
_entities.Article.DeleteObject(existingArticle);
}
}
public void Save()
{
_entities.SaveChanges();
}
public void Dispose()
{
if (_entities != null)
{
_entities.Dispose();
}
}
}
Rien de très compliqué. J'ai juste une méthode Save (déclarée dans IArticleDA) qui permet de sauver en base les modifications faites à nos objets Article, à l'aide de SaveChanges. Ca nous permet, dans le cas où nous avons de nombreux articles à créer ou à modifier, de ne pas faire de SaveChanges à chaque fois. Nous n'appellerons la méthode qu'à la fin de notre traitement métier.
Voyons maintenant la couche BusinessLogic, avec la classe ArticleBL :
public class ArticleBL : IDisposable
{
private IArticleDA _dataFactory = null;
public ArticleBL()
: this(new ArticleDA())
{
}
public ArticleBL(IArticleDA dataFactory)
{
_dataFactory = dataFactory;
}
public List<ArticleBE> GetArticles()
{
try
{
return _dataFactory.GetArticles();
}
catch (Exception ex)
{
LogError.Log(ex);
return new List<ArticleBE>();
}
}
public ArticleBE GetArticleById(int idArticle)
{
try
{
if (idArticle < 0)
{
throw new ArgumentException();
}
return _dataFactory.GetArticleById(idArticle);
}
catch (Exception ex)
{
LogError.Log(ex);
return null;
}
}
public void AddArticle(ArticleBE article)
{
try
{
if (string.IsNullOrEmpty(article.Title)
|| string.IsNullOrEmpty(article.Author)
|| article.Date == DateTime.MinValue)
{
throw new ArgumentException();
}
_dataFactory.AddArticle(article);
}
catch (Exception ex)
{
LogError.Log(ex);
}
}
public void EditArticle(ArticleBE article)
{
try
{
if (article.Id < 0
|| string.IsNullOrEmpty(article.Title)
|| string.IsNullOrEmpty(article.Author)
|| article.Date == DateTime.MinValue)
{
throw new ArgumentException();
}
_dataFactory.EditArticle(article);
}
catch (Exception ex)
{
LogError.Log(ex);
}
}
public void DeleteArticle(ArticleBE article)
{
try
{
if (article.Id < 0)
{
throw new ArgumentException();
}
_dataFactory.DeleteArticle(article);
}
catch (Exception ex)
{
LogError.Log(ex);
}
}
public void Save()
{
try
{
_dataFactory.Save();
}
catch (Exception ex)
{
LogError.Log(ex);
}
}
public void Dispose()
{
try
{
if (_dataFactory != null)
{
_dataFactory.Dispose();
}
}
catch (Exception ex)
{
LogError.Log(ex);
}
}
}
Ici, le code devient un peu plus intéressant. Pour ne pas manipuler directement d'objets ArticleDA (ce qui permet de changer très facilement de source de données), le constructeur de ma classe demande en paramètre un objet IArticleDA. Si aucun objet implémentant IArticleDA n'est passé au constructeur, la classe utilisera le constructeur vide qui créera automatiquement un objet ArticleDA qu'elle passera au constructeur avec paramètre.
Vous pouvez également voir que les tests "métier", à savoir une tentative de lecture d'un article ayant un Id < 0, la tentative d'ajout d'un article sans titre, ... déclencheront la levée d'une ArgumentException. Le code de chaque méthode étant dans un Try Catch, cette exception sera attrapée et logguée (voir classe LogError dans la couche Core).
Enfin, la méthode Dispose appellera la méthode Dispose de notre classe implémentant IArticleDA, ce qui coupera la connexion à la BDD.
Voyons maintenant un exemple d'utilisation de nos méthodes métier. Si nous nous rendons dans le contrôleur ArticleController de l'application MVC, nous pouvons voir ceci :
public class ArticleController : Controller
{
public ActionResult Index(string message = "")
{
ViewBag.Message = message;
using (ArticleBL business = new ArticleBL())
{
return View(business.GetArticles());
}
}
public ActionResult Details(int id)
{
using (ArticleBL business = new ArticleBL())
{
return View(business.GetArticleById(id));
}
}
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(ArticleBE article)
{
using (ArticleBL business = new ArticleBL())
{
business.AddArticle(article);
business.Save();
}
return RedirectToAction("Index", new { message = "The article has been created." });
}
public ActionResult Edit(int id)
{
ArticleBE article = null;
using (ArticleBL business = new ArticleBL())
{
article = business.GetArticleById(id);
}
if (article == null)
{
return RedirectToAction("Index", new { message = "This article doesn't exist." });
}
return View(article);
}
[HttpPost]
public ActionResult Edit(ArticleBE article)
{
using (ArticleBL business = new ArticleBL())
{
business.EditArticle(article);
business.Save();
}
return RedirectToAction("Index", new { message = "The article has been updated." });
}
public ActionResult Delete(int id)
{
ArticleBE article = null;
using (ArticleBL business = new ArticleBL())
{
article = business.GetArticleById(id);
if (article == null)
{
return RedirectToAction("Index", new { message = "This article doesn't exist." });
}
business.DeleteArticle(article);
business.Save();
}
return RedirectToAction("Index", new { message = "The article has been deleted." });
}
Et oui, pour utiliser nos données en base, il suffit de faire un using. A la sortie du using, la méthode Dispose de l'objet sera appelée, et donc la connexion sera automatiquement coupée. Cette méthode a ses avantages (sécurité, pas de risques de fuite mémoire) comme ses inconvénients (nombreuses connexions / déconnexions), mais rien ne vous empêche de la modifier.
Regardons enfin, en détail, les tests unitaires. Déjà, dans la couche Tests.Mock, le code est assez parlant :
public class MockArticleDA : IArticleDA
{
private List<ArticleBE> articles = new List<ArticleBE>()
{
new ArticleBE() {
Id = 1,
Title = "Demo 1",
Tags = "demo,article,.Net",
Date = DateTime.UtcNow,
Content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vehicula fringilla quam, nec interdum arcu faucibus id. Sed placerat elementum venenatis. Quisque tempor sem a mi elementum at elementum orci aliquet. Morbi venenatis convallis orci. Sed vitae felis vel sapien cursus viverra. Nulla et elit non ante suscipit dictum. Proin id erat ligula. Vestibulum vehicula augue vel nunc consequat porta. Nam tincidunt lobortis lacus, eu varius velit vestibulum vitae. Sed quis nisi erat, at egestas odio. Cras tempor mauris et dui blandit placerat euismod libero sagittis.",
Author = "demoUser",
},
new ArticleBE() {
Id = 2,
Title = "Demo 2",
Tags = "demo,article,PHP",
Date = DateTime.UtcNow,
Content = "PHP : Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vehicula fringilla quam, nec interdum arcu faucibus id. Sed placerat elementum venenatis. Quisque tempor sem a mi elementum at elementum orci aliquet. Morbi venenatis convallis orci. Sed vitae felis vel sapien cursus viverra. Nulla et elit non ante suscipit dictum. Proin id erat ligula. Vestibulum vehicula augue vel nunc consequat porta. Nam tincidunt lobortis lacus, eu varius velit vestibulum vitae. Sed quis nisi erat, at egestas odio. Cras tempor mauris et dui blandit placerat euismod libero sagittis.",
Author = "demoUser",
},
new ArticleBE() {
Id = 3,
Title = "Demo 3",
Tags = "demo,article,Java",
Date = DateTime.UtcNow,
Content = "JAVA : Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vehicula fringilla quam, nec interdum arcu faucibus id. Sed placerat elementum venenatis. Quisque tempor sem a mi elementum at elementum orci aliquet. Morbi venenatis convallis orci. Sed vitae felis vel sapien cursus viverra. Nulla et elit non ante suscipit dictum. Proin id erat ligula. Vestibulum vehicula augue vel nunc consequat porta. Nam tincidunt lobortis lacus, eu varius velit vestibulum vitae. Sed quis nisi erat, at egestas odio. Cras tempor mauris et dui blandit placerat euismod libero sagittis.",
Author = "demoUser",
}
};
public List<ArticleBE> GetArticles()
{
// we clone the list to avoid to avoid any ref problem
return articles.Select(e => e.Clone() as ArticleBE).ToList();
}
public ArticleBE GetArticleById(int idArticle)
{
ArticleBE result = articles.Where(e => e.Id == idArticle).FirstOrDefault();
if (result != null)
{
// we clone the item to avoid to avoid any ref problem
return result.Clone() as ArticleBE;
}
return null;
}
public void AddArticle(ArticleBE article)
{
article.Id = articles.Max(e => e.Id) + 1;
articles.Add(article);
}
public void EditArticle(ArticleBE article)
{
ArticleBE result = articles.Where(e => e.Id == article.Id).FirstOrDefault();
if (result != null)
{
result.Author = article.Author;
result.Content = article.Content;
result.Date = article.Date;
result.Tags = article.Tags;
result.Title = article.Title;
}
}
public void DeleteArticle(ArticleBE article)
{
ArticleBE result = articles.Where(e => e.Id == article.Id).FirstOrDefault();
if(result != null)
{
articles.Remove(result);
}
}
public void Save()
{
}
public void Dispose()
{
}
}
On voit donc, ici, qu'on implémente bien notre interface d'accès aux données IArticleDA. Ainsi, pour tester notre couche de logique métier sur cette classe, il nous suffira de passer un objet MockArticleDA dans le constructeur de notre classe métier, et le tour est joué !
Notre classe MockArticleDA possède une liste d'articles créée à chaque construction d'un objet. La spécificité, ici, est que j'utilise la méthode Clone de nos objets ArticleBE pour ne renvoyer que des copies de ces objets à notre classe de tests. Cela permet d'éviter de modifier involontairement les données de nos objets par référence (par exemple dans le cas d'un test de modifications des données sans sauvegarde).
Enfin, je vous laisse vous intéresser à la classe ArticleBLTest, dont voici 2/3 méthodes :
[TestInitialize()]
public void MyTestInitialize()
{
IArticleDA mockDataFactory = new MockArticleDA();
business = new ArticleBL(mockDataFactory);
}
[TestMethod()]
public void DeleteArticle_Generic_Test()
{
// params
int nbArticles = 3;
// --
// we check if we have 3 articles
List<ArticleBE> articles = business.GetArticles();
Assert.AreEqual(nbArticles, articles.Count);
// we delete the article
business.DeleteArticle(articles.First());
List<ArticleBE> actual = business.GetArticles();
Assert.AreEqual(nbArticles - 1, actual.Count);
}
[TestMethod()]
public void GetArticleById_With_Wrong_Parameters()
{
// params
int fakeId = -1;
// --
ArticleBE article = business.GetArticleById(fakeId);
Assert.IsNull(article);
}
[TestMethod()]
public void AddArticle_With_Wrong_Parameters()
{
// params
string author = "Me";
string content = "It's not working !";
string title = "Test article";
string tags = "great,article,news";
DateTime now = DateTime.Now;
int nbArticles = 3;
// --
// we check if we have 3 articles
List<ArticleBE> articles = business.GetArticles();
Assert.AreEqual(nbArticles, articles.Count);
ArticleBE article1 = new ArticleBE()
{
Author = author,
Content = content,
Tags = tags,
Title = title
};
ArticleBE article2 = new ArticleBE()
{
Content = content,
Date = now,
Tags = tags,
Title = title
};
ArticleBE article3 = new ArticleBE()
{
Author = author,
Content = content,
Date = now,
Tags = tags,
};
// we create the articles. it shouldn't work
business.AddArticle(article1);
business.AddArticle(article2);
business.AddArticle(article3);
articles = business.GetArticles();
// we check if we still have 3 articles
Assert.AreEqual(nbArticles, articles.Count);
}
Il est à noter que la méthode MyTestInitialize est appelée avant chaque test unitaire. Ensuite, pour chaque test, on vérifie les données déjà présentes, les données à la fin du test, et, en cas d'erreur, on verra tout de suite que le test a échoué. Comme vous avez pu le voir, mes méthodes de tests ont des noms très clairs, afin de savoir, au premier coup d'oeil, pourquoi nos tests ont pu échouer.
Je pense avoir fait le tour des spécificités du code de cette minuscule application. Si vous ne comprenez pas quelque chose, ou si vous avez besoin d'une précision, n'hésitez pas à demander dans les commentaires.
Enfin, pour déployer cette application chez vous, il vous suffit de restaurer la BDD dans un SQL Server 2008 (script Backup.bak, dans le zip fourni) et de modifier les chaînes de connexion dans les 3 fichiers de configurations (HTA.DemoBlog.DataAccess, HTA.DemoBlog.WebService et HTA.DemoBlog.Tests).
N'hésitez donc pas à tester cette "application" (HTA.DemoBlog.zip (1,54 mb)) et à me donner votre avis. 
Bonne journée à tous !
P.S. Oui je sais, le code n'est pas commenté, mais étant donné la simplicité des classes, je n'ai pas pensé que c'était nécessaire.
P.S.2 Quand j'aurais le temps, je ferais probablement une autre version un peu plus poussée avec Moq + StructureMap.
P.S.3 Je crois que c'est mon article le plus long depuis le début de mon blog. 7869 mots, pour presque 30 000 caractères. L'équivalent d'un document Word de 15 pages ! Wouah !
image modifiée de Bert Kaufmann, sous licence CC

Lorsqu’on développe une application WinForm ou WPF en .Net, on peut être amené à avoir envie de stocker des informations qui doivent rester confidentielles, comme une clé de cryptage ou un morceau de code. Malheureusement, en .Net, tout ce qui est compilé peut être très simplement décompilé
!
Ainsi, si vous développez, par exemple, une application de chiffrement comme celle-ci :

N’importe qui peut, en lançant JustDecompile (ou n’importe quel autre décompilateur .Net), naviguer dans votre code pour trouver ce que vous auriez aimé cacher : la clé de cryptage de votre application.

Vous vous dites qu’ici ce n’est pas si grave... Mais si l’application stockait dans son fichier de configuration des informations d’authentification chiffrées, est-ce que ce serait grave ? Bien sûr ! Une autre personne ayant accès au fichier de configuration et à l’application pourrait très simplement récupérer les informations d’authentification et les utiliser ! 
Comment faire pour cacher des données dans ce cas ?
Il n’y a, concrètement, aucun moyen de cacher des données dans une application .Net... N’importe qui d’assez déterminé pourra toujours se débrouiller pour y accéder... Cependant, vous pouvez toujours compliquer la vie du pirate à l’aide de l’obfuscation !
"Mais dis voir Tommy, qu’est ce que l’obfuscation ?
"
D'après cette chère Wikipedia : "L'obfuscation, ou assombrissement, est une stratégie de protection de la vie privée qui consiste à publier en quantité des informations. De cette manière, on tente de "noyer" les informations existantes que l'on souhaite cacher."
Ainsi, en utilisant l’obfuscation sur notre programme, on le rendra bien plus difficile à lire et à comprendre.
Pour faire cela, j’ai trouvé un petit freeware très simple capable d’obfuscer en quelques clics un programme (ou une bibliothèque) .Net : EasyObfuscator.NET. Il vous suffit de le télécharger ici et de l’installer.
Une fois ceci fait, lancez-le, et vous vous retrouverez devant cette fenêtre :

Vous n’avez plus qu’à faire glisser votre .exe ou votre .dll dans l’application, et à attendre la fin du traitement :


Et voilà, votre objet a été obfuscé
! Ouvrons-le maintenant avec JustDecompile :

On peut voir que tous les noms de méthodes / propriétés ont disparu ! Ca ne va pas simplifier la vie du petit malin qui essaie de décompiler votre application !
Enfin, si la personne arrive (par miracle
) à trouver la bonne méthode, voilà ce qu’elle verra :

Vous vous doutez bien que désormais, la personne mal intentionnée qui naviguera dans votre application dans l’espoir d’y retrouver la clé de chiffrement abandonnera vite, à moins d’être vraiment déterminée ! 
(Imaginez-vous devoir naviguer dans des milliers de lignes de codes anonymes qui ne comportent que des adresses mémoires !)
Cependant, l’obfuscation est un processus qui alourdit le fonctionnement de l’application / la bibliothèque... Etant donné qu’on rajoute des données, les traitements et le poids des dll seront forcément plus conséquents. 
C’est donc à vous de voir si ça vaut le coup ou non, sachant qu’une personne décidée pourra toujours reconstituer les données !
J’espère que cet article vous a plu. Si vous connaissez d’autres façons de cacher des données au sein d’un programme C#, n’hésitez pas à les partager à travers les commentaires !
image modifiée de DominÖ, sous licence CC
Comment Hitler aurait-il réagit face à la décision d'abandonner Silverlight au profit d'HTML5 ? A voir à tout prix !

Comme vous le savez, Microsoft a dévoilé Windows 8 il y a quelques semaines. Si les nouveautés apportées par l'OS ont été très bien accueillies, la communauté de développeurs .Net a commencé à hurler devant les choix faits par Microsoft.
Pourquoi ? Quels choix ?
Microsoft, très connu pour ses difficultés à choisir (et surtout à affirmer ses choix) des langages et des technologies pour ses plateformes, a décidé de laisser tomber tout ce sur quoi ils travaillent depuis 10 ans. Les API .Net ? Terminé. WPF ? Terminé. Winforms ?
Terminé. Silverlight ? Terminé.
Microsoft se tourne désormais vers HTML 5 et Javascript pour son Windows 8 ! Et oui, HTML 5. HTML 5 dont la version définitive des spécifications n'arrivera qu'en 2014 voire 2015. Et Javascript. Le langage objet le plus imbuvable de tous, trop dynamique, trop vieux, très lourd à utiliser.
Pour rappel, JavaScript a été créé en 1995. HTML 5 sera définitivement créé d'ici 3 à 4 ans.
Qu'est ce que ça va changer ? Tout. Fini les API qui existent actuellement. Finis les programmes qui tournent sous Windows 7. Fini l'utilisation de Visual Studio telle que vous la connaissez. Fini les bindings en 3 clics. Fini le C#.
Avec HTML 5, vous allez devoir vous mettre au JavaScript. Tous les développeurs .Net vont voir leurs compétences détruites d'un coup suite à un choix de Microsoft. Un mauvais choix. Tout est à réapprendre. Tout est à refaire.
Pourquoi ? Pourquoi Microsoft ? Pourquoi nous impose-t-il ça ?
Une première réponse a été donnée : avec Windows 8, Microsoft a décidé de créer un système d'exploitation compatible avec les PC mais également avec les tablettes. Génial !
Sauf qu'un pc, ce n'est pas une tablette ! Pour moi un PC c'est un produit rapide, puissant, qu'on dirige avec un clavier et une souris et qui permet d'être productif !
La tablette est tout l'inverse ! Elle est destinée à être utilisée ponctuellement, pour tout ce qui est divertissement, multimédia, ... Mais pas pour le travail, la bureautique, les échanges de mails... Aller sur Facebook sur sa tablette, c'est facile et rapide. Travailler et écrire des mails sur une tablette, c'est autre chose !
Les PC sous Windows 8 ne seront plus des PC mais des... navigateurs.
Il reste à espérer deux choses :
- Que Microsoft craque sous la pression et fasse machine arrière (sous la pression de la communauté de développeurs... Mobilisez-vous !) pour persévérer dans ses technologies qui sont excellentes aujourd'hui et qui continueraient à l'être si elles ne sont pas abandonnées... (Tous ceux qui ont travaillé avec WPF / C# savent que c'est mille fois mieux et mille fois plus puissant que HTML / Javascript !)
- Que Microsoft gère mieux le HTML5 dans Windows 8 que dans son dernier Internet Explorer 9.
Je vais peut être me mettre à Java et GWT moi...
source 1, source 2
image retouchée de backofthenapkin, sous licence CC
En mélangeant Kinect et WPF, les développeurs de The Technology Studio ont réussi à réaliser un "Oeil de Sauron" qui suit vos mouvements !
Le résultat est bluffant en tout cas ! La sphère projette l'image de l'oeil de notre méchant préféré sur ses parois, et grâce à la détection de mouvements du Kinect, l'oeil nous suit vraiment.
Le code du projet est disponible ici, et vous pouvez l'utiliser même si vous n'avez pas de "Puffersphere".
Je trouve vraiment excellentes toutes les dérives engendrées par le dernier outil à la mode de Microsoft. J'espère que ça continuera dans ce sens là.
A quand un device qui croise manette de Wii, Kinect et 3D pour permettre des combats de sabre laser dans l'univers de Star Wars ?
source