2010-12-15 16 views
3

Ce n'est pas une question sur la façon de lier correctement une série de cases à cocher à un modèle property (une question fréquente) - mon site fonctionne parfaitement en lisant les valeurs de checkbox à partir d'une requête, POST ou GET + chaîne de requête.Dans ASP.NET MVC, comment générer des chaînes de requête de nom répété ('? V = 1 & v = 2 & v = 3') avec Html.ActionLink

Il s'agit de savoir comment utiliser Html.ActionLink pour générer un lien qui formate plusieurs valeurs de case à cocher correctement dans la chaîne de requête.

J'ai donc le modèle suivant:

public class ModelType 
{ 
    public string[] V { get; set; } 
} 

Et je lie, par exemple, 3 cases à cocher pour ce modèle dans la vue que j'ai trois valeurs possibles (et, oui, des combinaisons desdites valeurs).

Voici la finale Html

<INPUT id="chk1" value="1" type="checkbox" name="V"> 
<INPUT id="chk2" value="2" type="checkbox" name="V"> 
<INPUT id="chk3" value="3" type="checkbox" name="V"> 

Si les trois valeurs sont vérifiées lorsque le formulaire est soumis au serveur en tant que GET alors la chaîne de requête générée est, bien sûr, ?V=1&V=2&V=3.

La reliure de modèle fonctionne magnifiquement, et tout est heureux.

Cependant, disons que je veux produire un lien vers la même action, en passant soit un type anonyme ou RouteValueDictionary pour produire la même chaîne de requête; états logiques que vous feriez quelque chose comme ceci:

Version 1: Avec une instance de ModelType comme modèle dans la vue:

<%= Html.ActionLink("Test link", null /* action name */, new { V = Model.V }) %> 

Version 2: Initialisation du membre « V » directement sous forme de tableau:

<%= Html.ActionLink("Test link", null /* action name */, 
    new { V = new string[] { "1", "2", "3" } }) %> 

[dans la question que je dis RouteValueDictionary parce que dans les deux cas, ces deux types anonymes sont transformés en se tels avant lien génération se produit.]

Dans les deux cas, la chaîne de requête produite est la suivante: ?V=System.String%5B%5D. Maintenant, je comprends que c'est parce que le générateur de lien appelle simplement ToString() sur le tableau; mais il semblerait qu'il n'y ait aucun moyen de transmettre à MVC une valeur qui l'amènera à générer ?V=1&V=2&V=3.

Puisque ni la langue nous permet de faire:

new { V="1", V="2", ... } 

ne RouteValueDictionary nous permet de faire:

d["V"] = "1"; d["V"] = "2"; ... 

J'ai aussi essayé la mesure désespérée de pré-traitement d'une RouteValueDictionary, de sorte que pour chaque IEnumerable<string>, je produis une seule chaîne avec chaque valeur séparée par une virgule, puis réécrit cette chaîne - de sorte que la chaîne de requête se termine comme ceci: ?V=1%2C2%2C3.Mais bien sûr, MVC ne lie pas automatiquement une telle chaîne à un tableau (vous obtenez juste une chaîne dans le tableau avec les virgules dedans); donc chaque propriété qui sera liée de cette manière doit être liée à la coutume - ce qui, pour moi, semble juste être un peu trop de cerceaux à franchir.

Donc, il semble que je suis bloqué.

Ai-je raté quelque chose? Aurai-je besoin d'écrire des versions spéciales d'ActionLink qui font ce que je veux?

Toute aide, comme toujours, grandement appréciée.

Répondre

3

Donc je suis allé et a étudié ceci et c'est mon point de vue. J'espère que quelqu'un pourra trouver une réponse plus optimiste, mais de toute façon je ne le pense pas.

En suivant le procédé d'extension Html.ActionLink tout le long, l'URL de base que ce produit est généré par le (toux) scellée et (toux toux!) interne de classe System.Web.Mvc.ParsedRoute et son procédé Bind dans le System.Web.Routing Assemblée.

A la fin de cette méthode (ce qui est une liste de réflecteur) - Voici comment cela génère la chaîne de requête:

if (unusedNewValues.Count > 0) 
{ 
    bool flag5 = true; 
    foreach (string str2 in unusedNewValues) 
    { 
     object obj5; 
     if (acceptedValues.TryGetValue(str2, out obj5)) 
     { 
      builder.Append(flag5 ? '?' : '&'); 
      flag5 = false; 
      builder.Append(Uri.EscapeDataString(str2)); 
      builder.Append('='); 
      builder.Append(Uri.EscapeDataString(
       Convert.ToString(obj5, CultureInfo.InvariantCulture))); 
     } 
    } 
} 

Je dois mentionner d'abord que unusedNewValues est un HashSet<string> qui signifie - malheureusement - que il ne rendra jamais que un nom/valeur. Vous ne pouvez pas fudger la chaîne de données avec les chaînes a=b&a=c(...) intégrées car la valeur de la chaîne est échappée - il n'est donc pas possible de générer de telles chaînes de requête dans Mvc à moins de remplacer l'infrastructure de routage.

En conséquence, une alternative sera d'écrire un type de données de collection qui fait quelque chose comme ce qui suit (ce qui est une mise en œuvre sale):

public class StringCollection : List<string> 
{ 
    public override string ToString() 
    { 
    //use the pipe character as a delimiter - but this doesn't work 
    //if the strings being carried around ccould naturally contain '|'! 
    return string.Join("|", this.ToArray()); 
    } 
} 

et mettre en œuvre ensuite un liant de modèle personnalisé (provenant de DefaultModelBinder) pour le type qui utilise d'abord la méthode par défaut, puis vérifie si la collection résultante n'a qu'une seule chaîne contenant '|' personnages en elle. Si c'est le cas, il post-traite la collection, en remplaçant son contenu par les valeurs étendues de la chaîne unique.

Ceci est horrible - mais le moins méchant de toutes les solutions que je peux actuellement penser.

Juste doivent maintenant faire une charge de changements à tous mes types de modèles existants qui utilisent les collections de chaînes qui pourraient être liés à interroger les valeurs de chaîne ...

1

eu un problème similaire récemment. J'ai fini par abandonner le HtmlHelper/RouteValueDictionary et juste construit mon URL à partir de zéro. Quelque chose comme:

<a href="/Controller/Action?<%: string.Join("&", set.Select(i => "v=" + i))%>">Link</a> 

Peut ne pas plaire aux goûts de tout le monde, mais cela a fonctionné pour moi.