2010-07-14 10 views
0

Dans l'une des applications que je construis, j'ai construit un système à base d'attributs très flexible pour décrire les produits dans ma base de données dans laquelle chaque produit peut avoir un nombre indéterminé d'attributs attribué à chaque attribut ayant un "type" unique. Ainsi, par exemple, un type d'attribut peut être "Catégorie" et la valeur attribuée à un seul attribut serait quelque chose comme "Camions". Il n'y a pas de restrictions sur le nombre d'attributs assignés à un produit donné et comme les attributs et les types d'attributs sont stockés dans la base de données à côté des produits, mon application ne sait pas à l'avance ce que seront ces attributs.ASP.NET MVC - Nettoyage des URLS dans le modèle complexe Scénarios de liaison

L'une des caractéristiques des options pour un type d'attribut donné est de savoir si oui ou non il est « consultable ». Dans le cas où un attribut est consultable, je peux alors utiliser sa valeur associée à son nom de type pour rechercher/filtrer mes produits. Ainsi, par exemple, un utilisateur peut souhaiter renvoyer tous les produits ayant le type d'attribut "Catégorie" égal à "Camions" et le type d'attribut "Couleur" égal à "Rouge". Rien de trop unique là-bas. Le problème que je rencontre est que parce que mon système ne sait pas à l'avance quels sont mes noms de type d'attribut, je ne peux pas facilement créer une méthode d'action acceptant les paramètres dans un format lisible comme string category ou string color. En tant que solution, j'ai utilisé le support de DefaultModelBinder pour la liaison à un dictionnaire. Avec cette approche, il suffit de formater mes noms de champs dans la bonne structure, puis ma méthode d'action peut accepter un IDictionary<string,string> parameters. Tout cela fonctionne assez bien, mais cela crée des URL vraiment désagréables lorsque l'utilisateur exécute un filtre basé sur des liens par un seul paramètre, c'est-à-dire "Voir plus de produits dans les chariots de catégorie". Avec le DefaultModelBinder se liant à un dictionnaire nécessite que votre modèle de nommage des champs ressemble à ce qui suit:

<input type="hidden" name="parameters[0].Key" value="Category" /> 
    <select name="parameters[0].Value"> 
    <option value="Trucks">Trucks</option> 
    <option value="Compacts">Compacts</option> 
    <option value="SUVs">SUVs</option> 
</select> 
<input type="hidden" name="parameters[1].Key" value="Manufacturer" /> 
<select name="parameters[1].Value"> 
    <option value="Ford">Ford</option> 
    <option value="Toyota">Toyota</option> 
    <option value="Honda">Honda</option> 
</select> 

Non seulement est-ce très bavard, mais il a aussi un peu frustrant en raison du fait que chaque clé/valeur doit contenir un index ordinal dans le nom du champ. Bien que cela soit acceptable pour un formulaire POST, il n'est pas particulièrement idéal dans une URL GET car nous nous retrouvons avec des URL ressemblant à ?parameters[0].Key=Category&parameters[0].Value=Trucks&parameters[1].Key=Manufacturer&parameters[1].Value=Ford. Non seulement c'est moche, mais il est très limité dans son implémentation car toute modification de l'URL pourrait potentiellement détruire l'ensemble des résultats (si l'utilisateur voulait juste chercher par le second paramètre en modifiant l'URL, il devrait supprimer le premier paramètre et renuméroter la collection entière de manière appropriée).

Ce que je cherche est une meilleure façon de gérer ce genre de situation. Idéalement, je voudrais simplement avoir une valeur de chaîne de requête ?Category=Red et filtrer en conséquence, mais alors ma méthode d'action ne sait pas s'il y a réellement un paramètre "Catégorie" auquel se lier. Y en a-t-il entre les deux qui me permettraient d'avoir des paramètres de chaîne de requête plus propres qui ne permettraient pas de créer des structures d'URL aussi horribles?

Je pensais à construire peut-être mon propre ModelBinder personnalisé, mais je voudrais éviter que s'il y a une autre façon.

Répondre

2

Je préfère de beaucoup vos "propres" URIs: ?Category=Red. Commençons donc là et voyons comment cela pourrait fonctionner.

Vous pouvez charger toutes les catégories à l'exécution, non? En haut de ma tête:

IEnumerable<string> allCategories = Categories.GetAll(); 
var usedCategories = Request.QueryString.AllKeys.Intersect(allCategories); 
var search = from c in usedCategories 
      select new 
      { 
       Key = c, 
       Value = Request.QueryString[c] 
      }; 

Vous pouvez utiliser ceci tel quel ou créer un classeur personnalisé. Dans les deux cas, ce n'est pas beaucoup de code.

+0

Nice. J'aime beaucoup cette solution. Je rapporterai quand je verrai comment cela fonctionne. –

+0

Bravo monsieur. Vraiment. –

+0

Solution agréable et élégante. Merci. –