如何使用asp.net mvc框架从模型生成查询字符串

Yoa*_*. B 6 c# asp.net-mvc-4

我有一个模型,有一些嵌套属性,列表......我想从该模型中获取一个查询字符串参数.

asp.net mvc框架中是否有任何类/帮助程序来执行此操作?

我知道使用模型绑定器我们可以从查询字符串绑定模型,但我想做反向操作.

谢谢.

Ste*_*ble 4

我相当确定框架中没有“序列化到查询字符串”功能,主要是因为我认为没有标准方法来表示查询字符串中的嵌套值和嵌套集合。

我认为使用 ModelMetadata 基础设施可以很容易地做到这一点,但事实证明,使用 ModelMetadata 从集合值属性获取项目存在一些复杂性。我编写了一个解决该问题的扩展方法,并构建了一个 ToQueryString 扩展,您可以从您拥有的任何 ModelMetadata 对象调用。

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

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

我还没有非常彻底地测试它,所以其中可能潜伏着一些空引用错误,但它为我尝试过的复杂对象吐出了正确的查询字符串。