How to create an ActionLink with Properties for the View Model

☆樱花仙子☆ 提交于 2019-12-23 10:09:31

问题


I have a ViewModel with a Filter property that has many properties that I use to filter my data

Example:

class MyViewModel : IHasFilter
{
     public MyData[] Data { get; set; }
     public FilterViewModel Filter { get; set; }
}

class FilterViewModel
{
    public String MessageFilter { get; set; }
    //etc.
}

This works fine when using my View. I can set the properties of Model.Filter and they are passed to the Controller. What I am trying to do now, is create an ActionLink that has a query string that works with the above format.

The query string generated by my View from above looks like this:

http://localhost:51050/?Filter.MessageFilter=Stuff&Filter.OtherProp=MoreStuff

I need to generate an ActionLink in a different View for each row in a grid that goes to the View above.

I have tried:

Html.ActionLink(
    item.Message,
    "Index",
    "Home",
    new { Filter = new { MessageFilter = item.Message, }, },
    null);

I also tried setting the routeValues argument to:

new MyViewModel { Filter = new FilterViewModel { MessageFilter = item.Message, }, },

But these do not generate the query string like the above one.


回答1:


You could create one RouteValueDictionary from a FilterViewModel instance and then use ToDictionary on that to pass to another RouteValues with all the keys prefixed with 'Filter.'.

Taking it further, you could construct a special override of RouteValueDictionary which accepts a prefix (therefore making it more useful for other scenarios):

public class PrefixedRouteValueDictionary : RouteValueDictionary
{
  public PrefixedRouteValueDictionary(string prefix, object o)
    : this(prefix, new RouteValueDictionary(o))
  { }

  public PrefixedRouteValueDictionary(string prefix, IDictionary<string, object> d)
    : base(d.ToDictionary(kvp=>(prefix ?? "") + kvp.Key, kvp => kvp.Value))
  { }
}

With that you can now do:

Html.ActionLink( 
  item.Message, 
  "Index", 
  "Home", 
  new PrefixedRouteValueDictionary("Filter.", 
    new FilterViewModel() { MessageFilter = item.Message }), 
  null); 

The caveat to this, though, is that the Add, Remove, TryGetValue and this[string key] methods aren't altered to take into account the prefix. That can be achieved by defining new versions of those methods, but because they're not virtual, they'd only work from callers that know they're talking to a PrefixedRouteValueDictionary instead of a RouteValueDictionary.




回答2:


Interesting question (+1). I'm assuming that the purpose is to use the default model binder to bind the querystring parameters to to your Action parameters.

Out of the box I do not believe that the ActionLink method will do this for you (of course there is nothing stopping you from rolling your own). Looking in reflector we can see that when the object is added to the RouteValueDictionary, only key value pairs are added. This is the code that adds the key value pairs and as you can see there is no traversing the object properties.

foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
{
    object obj2 = descriptor.GetValue(values);
    this.Add(descriptor.Name, obj2);
}

So for your object

var values = new { Filter = new Filter { MessageFilter = item.Message } }

the key being added is Filter and the value is your Filter object which will evaluate to the the fully qualified name of your object type.

The result of this is Filter=Youre.Namespace.Filter.

Edit possible solution depending on your exact needs


Extension Method does the work

Note that it uses the static framework methods ExpressionHelper and ModelMetadata (which are also used by the existing helpers) to determine the appropriate names that the default model binder will understand and value of the property respectively.

public static class ExtentionMethods
{
    public static MvcHtmlString ActionLink<TModel, TProperty>(
        this HtmlHelper<TModel> helper,
        string linkText,
        string actionName,
        string controllerName,
        params Expression<Func<TModel, TProperty>>[] expressions)
    {
        var urlHelper = new UrlHelper(helper.ViewContext.HttpContext.Request.RequestContext);

        var url = urlHelper.Action(actionName, controllerName);

        if (expressions.Any())
        {
            url += "?";

            foreach (var expression in expressions)
            {
                var result = ExpressionHelper.GetExpressionText(expression);

                var metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, helper.ViewData);

                url = string.Concat(url, result, "=", metadata.SimpleDisplayText, "&");
            }

            url = url.TrimEnd('&');
        }

        return new MvcHtmlString(string.Format("<a href='{0}'>{1}</a>", url, linkText));
    }
}

Sample Models

public class MyViewModel
{
    public string SomeProperty { get; set; }

    public FilterViewModel Filter { get; set; }
}

public class FilterViewModel
{
    public string MessageFilter { get; set; }
}

Action

public ActionResult YourAction(MyViewModel model)
{
    return this.View(
        new MyViewModel
        {
            SomeProperty = "property value",
            Filter = new FilterViewModel
            {
                MessageFilter = "stuff"
            }
        });
}

Usage

Any number of your view model properties can be added to the querystring through that last params parameter of the method.

@this.Html.ActionLink(
    "Your Link Text",
    "YourAction",
    "YourController",
    x => x.SomeProperty,
    x => x.Filter.MessageFilter)

Markup

<a href='/YourAction/YourController?SomeProperty=some property value&Filter.MessageFilter=stuff'>Your Link Text</a>

Instead of using string.Format you could use TagBuilder, the querystring should be encoded to be safely passed in a URL and this extension method would need some additional validation but I think it could be useful. Note also that, though this extension method is built for MVC 4, it could be easily modified for previous versions. I didn't realize that that one of the MVC tags was was for version 3 until now.



来源:https://stackoverflow.com/questions/10007832/how-to-create-an-actionlink-with-properties-for-the-view-model

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!