2012-12-20 12 views
6

Mam model, z niektórymi zagnieżdżonymi właściwościami, listami ... i chcę uzyskać parametry z tego modelu.How-to generate querystring from model with asp.net mvc framework

Czy jest jakiś klasy/pomocnika w asp.net mvc ramy, aby to zrobić?

Wiem, że z segregatorem modelu możemy powiązać model z kwerendy, ale chcę zrobić odwrotność.

Dzięki.

+0

Oto jeden pomysł: http://stackoverflow.com/questions/829080/how-to-build -a-query-string-for-a-url-in-c –

+0

Czy tworzysz cały URL lub po prostu zapytanie o kod? –

+0

Chcę zbudować querystring z instancji modelu, która może być później używana z narzędziem ModelBinder. model => Właściwość1 = 123 i Właściwość2 = Test i elementy [0] Imię = Cześć i przedmioty [1] .Nazwa = Kolejne –

Odpowiedz

4

Jestem dość pewny, że nie ma funkcji "serializowania do ciągu zapytania" w ramach, głównie dlatego, że nie sądzę, że istnieje standardowy sposób reprezentowania zagnieżdżonych wartości i zagnieżdżonych kolekcji w ciągu zapytania.

Pomyślałem, że byłoby to całkiem łatwe do zrobienia przy użyciu infrastruktury ModelMetadata, ale okazuje się, że istnieją pewne komplikacje związane z uzyskiwaniem elementów z właściwości o wartości kolekcji przy użyciu ModelMetadata. Zhackowałem razem metodę rozszerzenia, która działa wokół tego i stworzyła rozszerzenie ToQueryString, które można wywołać z dowolnego obiektu ModelMetadata, który posiadasz.

public static string ToQueryString(this ModelMetadata modelMetadata) 
{ 
    if(modelMetadata.Model == null) 
     return string.Empty; 

    var parameters = modelMetadata.Properties.SelectMany (mm => mm.SelectPropertiesAsQueryStringParameters(null)); 
    var qs = string.Join("&",parameters); 
    return "?" + qs; 
} 

private static IEnumerable<string> SelectPropertiesAsQueryStringParameters(this ModelMetadata modelMetadata, string prefix) 
{ 
    if(modelMetadata.Model == null) 
     yield break; 

    if(modelMetadata.IsComplexType) 
    { 
     IEnumerable<string> parameters; 
     if(typeof(IEnumerable).IsAssignableFrom(modelMetadata.ModelType)) 
     { 
      parameters = modelMetadata.GetItemMetadata() 
            .Select ((mm,i) => new { 
             mm, 
             prefix = string.Format("{0}{1}[{2}]", prefix, modelMetadata.PropertyName, i) 
            }) 
            .SelectMany (prefixed => 
             prefixed.mm.SelectPropertiesAsQueryStringParameters(prefixed.prefix) 
            );   
     } 
     else 
     { 
      parameters = modelMetadata.Properties 
         .SelectMany (mm => mm.SelectPropertiesAsQueryStringParameters(string.Format("{0}{1}", prefix, modelMetadata.PropertyName))); 
     } 

     foreach (var parameter in parameters) 
     { 
      yield return parameter; 
     } 
    } 
    else 
    { 
     yield return string.Format("{0}{1}{2}={3}", 
      prefix, 
      prefix != null && modelMetadata.PropertyName != null ? "." : string.Empty, 
      modelMetadata.PropertyName, 
      modelMetadata.Model); 
    } 
} 

// Returns the metadata for each item from a ModelMetadata.Model which is IEnumerable 
private static IEnumerable<ModelMetadata> GetItemMetadata(this ModelMetadata modelMetadata) 
{ 
    if(modelMetadata.Model == null) 
     yield break; 

    var genericType = modelMetadata.ModelType 
         .GetInterfaces() 
         .FirstOrDefault (x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>)); 

    if(genericType == null) 
     yield return modelMetadata; 

    var itemType = genericType.GetGenericArguments()[0]; 

    foreach (object item in ((IEnumerable)modelMetadata.Model)) 
    { 
     yield return ModelMetadataProviders.Current.GetMetadataForType(() => item, itemType); 
    } 
} 

Przykład użycia:

var vd = new ViewDataDictionary<Model>(model); // in a Controller, ViewData.ModelMetadata 
var queryString = vd.ModelMetadata.ToQueryString(); 

nie testowałem go bardzo dokładnie, więc nie mogą być pewne błędy zerowe ref czai się w niej, ale wypluwa prawidłowy ciąg kwerendy dla obiektów złożonych Próbowałem.

+0

Dziękuję bardzo! - okazało się to bardzo użyteczne, gdy w moim przypadku przekazałem model z powrotem do usługi renderowania obrazów. Możesz także dodać drugie statyczne rozszerzenie na tym 'obiekcie' (lub jeśli masz wspólną bazę lub interfejs na twoich klasach widoków, takich jak' IValidatableObject'), więc możesz po prostu użyć 'ViewModel.ToQueryString()' – StuartLC

0

@ Kod Steve miał niewielki błąd, gdy dodatkowe zagnieżdżanie i wyliczenia były przypadkiem.

Przykładowy model

public class BarClass { 
    public String prop { get; set; } 
} 

public class FooClass { 
    public List<BarClass> bar { get; set; } 
} 

public class Model { 
    public FooClass foo { get; set; } 
} 

kod testowy

var model = new Model { 
    foo = new FooClass { 
     bar = new List<BarClass> { 
      new BarClass { prop = "value1" }, 
      new BarClass { prop = "value2" } 
     } 
    } 
}; 

var queryString = new ViewDataDictionary<Model>(model).ModelMetadata.ToQueryString(); 

Wartość queryString powinno być:

"?foo.bar[0].prop=value1&foo.bar[1].prop=value2" 

Ale @ Kod Steve'a daje następujący wynik:

"?foobar[0].prop=value1&foobar[1].prop=value2" 

zaktualizowanego kodu

Tu jest nieco zmodyfikowaną wersją Rozwiązanie @ Steve'a:

public static class QueryStringExtensions { 
    #region inner types 

    private struct PrefixedModelMetadata { 

     public readonly String Prefix; 
     public readonly ModelMetadata ModelMetadata; 

     public PrefixedModelMetadata (String prefix, ModelMetadata modelMetadata) { 
      Prefix = prefix; 
      ModelMetadata = modelMetadata; 
     } 
    } 

    #endregion 
    #region fields 

    private static readonly Type IEnumerableType = typeof(IEnumerable), 
           IEnumerableGenericType = typeof(IEnumerable<>); 

    #endregion 
    #region methods 

    public static String ToQueryString<ModelType> (this ModelType model) { 
     return new ViewDataDictionary<ModelType>(model).ModelMetadata.ToQueryString(); 
    } 

    public static String ToQueryString (this ModelMetadata modelMetadata) { 
     if (modelMetadata.Model == null) { 
      return String.Empty; 
     } 

     var keyValuePairs = modelMetadata.Properties.SelectMany(mm => 
      mm.SelectPropertiesAsQueryStringParameters(new List<String>()) 
     ); 

     return String.Join("&", keyValuePairs.Select(kvp => String.Format("{0}={1}", kvp.Key, kvp.Value))); 
    } 

    private static IEnumerable<KeyValuePair<String, String>> SelectPropertiesAsQueryStringParameters (this ModelMetadata modelMetadata, List<String> prefixChain) { 
     if (modelMetadata.Model == null) { 
      yield break; 
     } 

     if (modelMetadata.IsComplexType) { 
      IEnumerable<KeyValuePair<String, String>> keyValuePairs; 

      if (IEnumerableType.IsAssignableFrom(modelMetadata.ModelType)) { 
       keyValuePairs = modelMetadata.GetItemMetadata().Select((mm, i) => 
        new PrefixedModelMetadata(
         modelMetadata: mm, 
         prefix: String.Format("{0}[{1}]", modelMetadata.PropertyName, i) 
        ) 
       ).SelectMany(prefixed => prefixed.ModelMetadata.SelectPropertiesAsQueryStringParameters(
        prefixChain.ToList().AddChainable(prefixed.Prefix, addOnlyIf: IsNeitherNullNorWhitespace) 
       )); 
      } 
      else { 
       keyValuePairs = modelMetadata.Properties.SelectMany(mm => 
        mm.SelectPropertiesAsQueryStringParameters(
         prefixChain.ToList().AddChainable(
          modelMetadata.PropertyName, 
          addOnlyIf: IsNeitherNullNorWhitespace 
         ) 
        ) 
       ); 
      } 

      foreach (var keyValuePair in keyValuePairs) { 
       yield return keyValuePair; 
      } 
     } 
     else { 
      yield return new KeyValuePair<String, String>(
       key: AntiXssEncoder.HtmlFormUrlEncode(
        String.Join(".", 
         prefixChain.AddChainable(
          modelMetadata.PropertyName, 
          addOnlyIf: IsNeitherNullNorWhitespace 
         ) 
        ) 
       ), 
       value: AntiXssEncoder.HtmlFormUrlEncode(modelMetadata.Model.ToString())); 
     } 
    } 

    // Returns the metadata for each item from a ModelMetadata.Model which is IEnumerable 
    private static IEnumerable<ModelMetadata> GetItemMetadata (this ModelMetadata modelMetadata) { 
     if (modelMetadata.Model == null) { 
      yield break; 
     } 

     var genericType = modelMetadata.ModelType.GetInterfaces().FirstOrDefault(x => 
      x.IsGenericType && x.GetGenericTypeDefinition() == IEnumerableGenericType 
     ); 

     if (genericType == null) { 
      yield return modelMetadata; 
     } 

     var itemType = genericType.GetGenericArguments()[0]; 

     foreach (Object item in ((IEnumerable) modelMetadata.Model)) { 
      yield return ModelMetadataProviders.Current.GetMetadataForType(() => item, itemType); 
     } 
    } 

    private static List<T> AddChainable<T> (this List<T> list, T item, Func<T, Boolean> addOnlyIf = null) { 
     if (addOnlyIf == null || addOnlyIf(item)) { 
      list.Add(item); 
     } 

     return list; 
    } 

    private static Boolean IsNeitherNullNorWhitespace (String value) { 
     return !String.IsNullOrWhiteSpace(value); 
    } 

    #endregion 
} 
Powiązane problemy