Ce présent billet est rédigé suite à une réponse que j’ai fournie à une question posée sur StackOverflow. La question était de savoir comment faire pour éviter la duplication de code Razor dans plusieurs vues. Les vues concernées par cette duplication sont au nombre de 15 d’après le développeur.
Ce qui n’est pas très pratique et pas du tout maintenable le jour où il y aura un bogue ou toute autre amélioration à apporter à ces parties concernées par la duplication de code.
Le code Razor en question est le suivant :
<div class="col-xs-12">
@if (@Model.Rating == 0)
{
<img src="/Images/Rating/NoRating.jpg" alt="" width="125">
}
else if (@Model.Rating == 1)
{
<img src="/Images/Rating/One.jpg" alt="" width="125">
}
else if (@Model.Rating == 2)
{
<img src="/Images/Rating/Two.jpg" alt="" width="125">
}
else if (@Model.Rating == 3)
{
<img src="/Images/Rating/Three.jpg" alt="" width="125">
}
else if (@Model.Rating == 4)
{
<img src="/Images/Rating/Four.jpg" alt="" width="125">
}
else if (@Model.Rating == 5)
{
<img src="/Images/Rating/Five.jpg" alt="" width="125">
}
</div>
J’ai noté deux solutions simples dans ma réponse.
Solution 1 :
Dans cette solution, il suffit de créer une méthode d’extension, nommons la Rating, pour la classe utilitaire System. Web. Mvc. HtmlHelper comme suit :
public static class RatingExtensions
{
public static MvcHtmlString Rating(this HtmlHelper helper, short rating)
{
string ratingImageName;
switch (rating)
{
case 0:
ratingImageName = "NoRating.jpg";
break;
case 1:
ratingImageName = "One.jpg";
break;
// And so on....
default:
throw new IndexOutOfRangeException(string.Format("The following rating: {0} is not expected.",
rating));
}
var imgTag = new TagBuilder("img");
imgTag.Attributes.Add("src", string.Format("/Images/Rating/{0}", ratingImageName));
imgTag.Attributes.Add("width", "125");
return new MvcHtmlString(imgTag.ToString());
}
}
Avec cette méthode d’extension, partout où il y a duplication de code nous pourrons écrire la seule et unique ligne suivante :
@Html.Rating(Model.Rating)
Solution 2 :
Créer tout simplement une vue partielle, nommons la _Rating. cshtml, dont le modèle sera typé, type short dans mon cas. Si le code dupliqué est partagé par pas mal de contrôleurs alors il faudra placer la vue partielle dans le dossier Shared. Notre vue partielle ressemblera à ça :
@model short
@{
var imageSrc = "/Images/Rating/";
switch (Model)
{
case 0:
imageSrc += "NoRating.jpg";
break;
case 1:
imageSrc += "One.jpg";
break;
// And so on....
default:
throw new IndexOutOfRangeException(string.Format("The following rating: {0} is not expected.",
Model));
}
}
<div class="col-xs-12">
<img src="@imageSrc" alt="" width="125">
</div>
Pour l’utiliser, il faudra remplacer toutes les parties dupliquées par le code ci-dessous :
@{
Html.RenderPartial("_Rating", Model.Rating);
}
Conclusion :
Je préfère la solution 2 dans le cadre de petits projets et où le code n’a pas besoin d’être partagé entre plusieurs applications. Si c’est le cas alors la solution 1 est celle à utiliser vu qu’elle peut être codée dans une librairie séparée et puis être partagée via un package Nuget par exemple.
Cependant une troisième solution a été proposée par un autre utilisateur ici. Sa solution tire parti des modèles d’affichage, une fonctionnalité offerte par ASP.Net MVC, mais pas que ça vu qu’on est obligé de créer notre propre ModelMetadataProvider. Les deux solutions présentées dans cet article répondent parfaitement à la demande du questionneur et sont simple d’utilisation si vous êtes partisan du principe KISS.
Si vous avez d’autres solutions à nous partager alors n’hésitez pas à les indiquer en commentaires de l’article.