
Allez, une petite astuce très simple pour aujourd'hui. Beaucoup de gens la connaissent, mais je me dis que ça peut toujours servir à certaines personnes.
Dans à peu près tous les langages, on finit souvent par avoir besoin d'afficher un popup de confirmation. Sur le Web on peut faire ça très simplement grâce au JavaScript, mais dans les applications Winforms, c'est un peu plus compliqué.
A l'origine, j'utilisais un formulaire dédié, que j'appelais en ShowDialog, et qui renvoyait la réponse sélectionnée. Mais j'ai trouvé bien plus simple : la classique MessageBox. En effet, elle permet déjà de faire ça :
if (MessageBox.Show("Are you sure?","Confirm", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
// Ok
}
else
{
// Not ok
}
Ce qui donne :

Voilà, c'était l'astuce simplissime du jour pour les développeurs .Net. En espérant qu'elle sera utile à quelqu'un.
Bonne journée et bonne semaine à tous !
image modifiée de Anton Fomkin, sous licence CC

Allez, une petite astuce .Net, ça faisait longtemps.
Lorsqu'on est un psychopathe de Visual Studio, comme moi, on se retrouve souvent avec des dizaines de fichiers ouverts dans notre IDE préféré. Du coup, difficile de s'y retrouver lorsqu'on recherche un fichier précis dans l'explorateur de solution.
Heureusement pour nous, il existe une petite astuce très pratique pour s'y retrouver un peu mieux !
Il suffit de se rendre dans :
Tools / Options / Projects and solutions / General
Et de cocher l'option :
Track active item in solution explorer

Ainsi, lorsque vous cliquerez sur l'onglet d'un fichier ouvert, l'explorateur de solution placera automatiquement son focus sur le fichier concerné !
Pratique non ?
Allez bonne journée à tous !
image modifiée de renatotarga, sous licence CC

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

Il y a quelques jours, j'ai été confronté à un problème intéressant...
Je faisais, dans ma couche de logique métier, de l'injection de dépendance à travers le constructeur de ma classe, comme ceci :
public class ArticleBL
{
private IArticleDA _dataFactory = null;
public ArticleBL(IArticleDA dataFactory)
{
_dataFactory = dataFactory;
}
}
Mais, ne voulant pas avoir à déclarer un nouvel objet ArticleDA (implémentant mon interface IArticleDA) à chaque création d'objet ArticleBL, je voulais utiliser une valeur par défaut (merci C# 4
) dans mon constructeur, comme ceci :
public class ArticleBL
{
private IArticleDA _dataFactory = null;
public ArticleBL(IArticleDA dataFactory = new ArticleDA())
{
_dataFactory = dataFactory;
}
}
Seulement voilà, ce n'est pas possible. En effet, au moment de la compilation, les valeurs par défaut d'une méthode doivent être des constantes. Du coup comment faire ?
Et bien la solution est très simple : il suffit de créer un constructeur sans paramètres qui appellera notre constructeur avec paramètre (avec ":"), en lui fournissant notre valeur par défaut, comme ceci :
public class ArticleBL
{
private IArticleDA _dataFactory = null;
public ArticleBL() : this(new ArticleDA())
{
}
public ArticleBL(IArticleDA dataFactory)
{
_dataFactory = dataFactory;
}
}
Et le tour est joué ! 
image modifiée de Anton Fomkin, sous licence CC

En .Net, lorsqu'on écrit des requêtes Linq, on utilise souvent la clause orderby (qui correspond, comme vous vous en doutez, à la clause ORDER BY du SQL) pour trier les données récupérées.
Or, en SQL, pour faire un tri multiple, la syntaxe est très simple :
SELECT * FROM Personne ORDER BY Nom ASC, Prenom ASC
En intercalant simplement une virgule entre nos paramètres de tri, on obtient une liste triée en fonction des paramètres spécifiés.
En Linq, la syntaxe est quasiment la même :
IEnumerable<Personne> personnesLinq = from personne
in PersonnesTable
orderby personne.Nom, personne.Prenom
select personne;
Maintenant, comment faire ça avec les expressions lambda ?
La solution est très simple : il faut utiliser la méthode ThenBy, juste après le OrderBy :
IEnumerable<Personne> personnesLambda = PersonnesTable
.OrderBy(p => p.Nom)
.ThenBy(p => p.Prenom);
En espérant que ça vous soit utile...
Bonne journée !
image modifiée de Anton Fomkin, 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
Désolé de ne pas poster beaucoup d'articles ces derniers temps, mais je travaille sur deux gros projets en simultané (boulot + perso), donc il m'est difficile de prendre quelques heures pour écrire des articles (j'en viens même à rêver de code la nuit...). Ca devrait aller mieux d'ici quelques semaines.
Pour en revenir au sujet, j'ai eu un problème au travail tout à l'heure. Je suis tombé sur un bout de code bizarre. Il ressemblait à peu près à ça :
List<int> ids = new List<int>() { 1, 5, 6, 7, 8 };
List<int> nouvelleListe = ids;
for(int i = 0; i < 5 ; i++)
{
nouvelleListe.RemoveRange(0, 1);
}
(Pour ceux qui ne le savent pas, un RemoveRange de (0, 1) supprime le premier élément de la liste.)
Là je vais vous poser une question piège : qu'est ce qu'on a dans la variable ids ?
Une liste d'entiers ou rien du tout ?
Rien du tout, et oui ma bonne dame. En effet, lorsqu'on fait :
List<int> nouvelleListe = ids;
On crée une référence vers notre liste d'ids, donc en travaillant sur nouvelleListe, on travaille également sur ids.
Pour remédier à ça et récupérer une liste à part, il suffit de cloner notre liste... Mais comment faire ? Dans ma recherche, je suis tombé sur des morceaux de code plus compliqués les uns que les autres...
Alors que la solution est toute bête :
List<int> nouvelleListe = new List<int>(ids);
Et oui, il suffit d'utiliser le bon constructeur. Comme quoi, des fois, lire la doc peut vous apporter la solution !
Allez, bon weekend à tous !
source de la solution
Je ne sais pas si vous connaissez Regex Hero, mais depuis que je l’ai découvert, je ne m’en passe plus.
RegexHero est un site en Silverlight permettant de tester toutes vos expressions régulières. Il offre de nombreuses choses comme la possibilité de voir en temps réel ce que trouvera votre expression régulière dans un texte, la possibilité de générer directement le code C# pour tester votre expression régulière, ou encore la possibilité d’optimiser automatiquement votre regex.
C’est le genre de site à toujours avoir dans sa boite à outils de développeur (.Net ou pas) ! 
N’hésitez pas à l'essayer !

Bonjour à tous,
Voilà aujourd'hui je me suis dit que j'allais partager avec vous du code que j'utilise régulièrement et qui pourrait vous être utile...
En effet, je suppose qu'il vous est déjà arrivé d'avoir à utiliser des Enum en C#. Or, s'il suffit de caster votre objet en int pour avoir sa valeur, il est bien plus difficile d'obtenir une valeur textuelle définie :
public enum EnumWeapons
{
Blade = 1,
Gun = 2,
Axe = 3,
Bow = 4
}
// ensuite on doit ajouter dans le code de notre application :
string weaponName = string.Empty;
switch(myEnumWeapons)
{
case EnumWeapons.Blade: weaponName = "Epée"; break;
//...
}
Il serait tellement plus simple d'avoir une méthode ToString qui renverrai automatiquement une chaîne de caractères définie à la création de notre Enum...
Mais attendez, c'est possible ! J'avais expliqué il y a quelques semaines comment ajouter des méthodes à une classe en créant une extension. Il nous suffit d'appliquer ça ici :
public enum EnumWeapons
{
Blade = 1,
Gun = 2,
Axe = 3,
Bow = 4
}
public static class EnumWeaponsHelper
{
public static string ToExplicitString(this EnumWeapons enumWeapons)
{
switch (enumWeapons)
{
case EnumWeapons.Axe: return "Hache";
case EnumWeapons.Blade: return "Epée";
case EnumWeapons.Bow: return "Arc";
case EnumWeapons.Gun: return "Revolver";
default: return string.Empty;
}
}
}
Grâce à ce code, lorsque vous aurez un objet EnumWeapons, vous aurez une belle méthode ToExplicitString() qui vous donnera une version textuelle définie par vos soins de votre objet !

En espérant que ça vous sera utile 
Bonne journée à tous !
Lorsque vous développez en C#, il vous arrive souvent d'avoir à caster (ou convertir, mais bon, caster c'est plus fun et ça fait un peu Meuporg !) vos objets :
Homme homme = (Homme)personne;
Or, si votre objet personne n'est pas un objet Homme mais un objet Femme, que se passera-t-il ? (Vous avez 5 minutes, ensuite je ramasse les copies !) Votre code lèvera une exception ! (Bon, tous ceux qui se sont trompés vont au coin !)

Heureusement, C#PO, spécialiste de la conversion en C#, est là pour nous aider !
Comment faire autrement ? Voici trois solutions possibles, de la plus mauvaise à la meilleure (selon C#PO, droïde de protocole C# / C#) :
-
try
{
homme = (Homme)personne;
}
catch
{
femme = (Femme)personne;
}
Ici, rien de bien compliqué, on utilise un try catch pour attraper l'exception qui sera levée si jamais l'objet personne se révèlerait être un objet Femme au lieu d'un objet Homme. Je vous recommande d'éviter ce code car attraper des exceptions doit rester quelque chose d'exceptionnel...
-
if(personne is Homme)
{
homme = (Homme)personne;
}
else
{
femme = (Femme)personne;
}
Ici, on utilise le mot clef is pour vérifier si notre objet est de type Homme ou Femme. Ce code est le plus "propre" des trois codes présents ici, mais il faut à chaque fois s'amuser à tester le type de votre objet. A voir selon votre façon de coder si vous préférez cette façon de faire ou la suivante.
-
homme = personne as Homme;
femme = personne as Femme;
Enfin, ici, vous avez le code le plus simple et le plus rapide à saisir, ce dernier utilisant le mot clef as. Ce mot clef permet de faire un cast qui renverra null si l'objet n'est pas du type indiqué. C'est moins "propre" que d'utiliser le mot clef is pour tester en amont, mais ça a le mérite d'être très rapide à saisir et d'éviter d'avoir à utiliser un try catch à chaque cast.
Voilà, j'espère que cet article vous aura été utile, en tout cas n'hésitez pas à donner dans les commentaires votre façon de faire, surtout si elle diffère des méthodes que je viens de vous donner. (Et on dit merci à C#PO !
)
Bon dev à tous !
image de Andres Rueda