LinkBuilder in Controller

Last post 07-28-2008 6:36 AM by Luis Abreu. 9 replies.

Sort Posts:

  • LinkBuilder in Controller

    07-07-2008, 3:08 AM
    • Member
      point Member
    • jirava.net
    • Member since 07-07-2008, 6:57 AM
    • Posts 1

    Hi,

      I want to use LinkBuilder in my Controller derived class but there is no other way insted of creating new ViewContext.

      Why I need to use LinkBuilder? Because the output from Controller is XML (my own XmlResult) and inside Xml I need to use Urls from my Routing. For example the RSS feed created using System.ServiceModel.Syndication.

       Is there any other way, how to create url from defined Routes and don't need ViewContext? And why the url creation depends on ViewContext?

       Thank you

     
    Jarda



     

  • Re: LinkBuilder in Controller

    07-07-2008, 6:47 AM
    • Contributor
      4,994 point Contributor
    • Paul Linton
    • Member since 04-30-2008, 3:16 AM
    • Posts 872

    You can pass a null Context

    LinkBuilder.BuildUrlFromExpression<UserController>(null, u => u.update(id));

    Got a c# problem? Try .NET Book Zero from Charles Petzold, it's a free pdf.
  • Re: LinkBuilder in Controller

    07-24-2008, 8:21 AM
    • Member
      18 point Member
    • danmalc3000
    • Member since 07-24-2008, 12:15 PM
    • Posts 16
     
    Paul Linton:

    You can pass a null Context

    LinkBuilder.BuildUrlFromExpression<UserController>(null, u => u.update(id));

    I also looked into this. Looking at the code in reflector, if the ViewContext is not passed, then it attempts to create a new RequestContext from the current HttpContext.

     if (requestContext != null)
    {
    return requestContext;
    }
    HttpContext current = HttpContext.Current;
    if (current == null)
    {
    throw new InvalidOperationException(RoutingResources.RouteCollection_RequiresContext);
    }
    return new RequestContext(new HttpContextWrapper(current), new RouteData());

     I have a couple of issues with this:

    - First parameter to LinkBuilder.BuildUrlFromExpression should be of type RequestContext, as this is the type used in underlying call to RouteCollection.GetVirtualPath
    - Could we have an overload to clarify this?

    Thanks

    Dan
     

  • Re: LinkBuilder in Controller

    07-24-2008, 8:40 AM
    • All-Star
      25,662 point All-Star
    • Luis Abreu
    • Member since 02-12-2005, 1:22 AM
    • Madeira [Portugal]
    • Posts 5,368
    • TrustedFriends-MVPs
    Hello. well, regarding 1, I think that ViewContext is being used to ensure that you'll use that helper *only* from a view page. As you've said, you can call RouteTable.Routes.GetVirtualPath directly to obtain the correct url for a specific route. regarding 2, you can always, for instance, build an extension method that extends the routes collection and that will always return the correct url in string format.
    --
    Regards,
    Luis Abreu
    email: labreu_at_gmail.com
    EN blog:http://msmvps.com/blogs/luisabreu
  • Re: LinkBuilder in Controller

    07-24-2008, 9:00 AM
    • Member
      18 point Member
    • danmalc3000
    • Member since 07-24-2008, 12:15 PM
    • Posts 16

    Thanks for the suggestions. 

    1. I guess if the method is only intended to be used from within a view, then your point is valid. However the source code doesn't (currently) have any reliance on ViewContext.

    2. That is possible, but LinkBuilder contains all of the ExpressionTree goodness, so you can provide the controller, action and parameters in a strongly typed way

    If LinkBuilder _is_ suitable for use in a broader context, then the method signature should reflect this.

    As the original post suggests, there are quite a few scenarios where you'd want to build urls from within a controller. 


  • Re: LinkBuilder in Controller

    07-24-2008, 9:20 AM
    • All-Star
      25,662 point All-Star
    • Luis Abreu
    • Member since 02-12-2005, 1:22 AM
    • Madeira [Portugal]
    • Posts 5,368
    • TrustedFriends-MVPs
    hello again. 1. well, that's what I thought after seeing that it declared the parameter as viewcontext... 2.agreed. I'm assuming that the team will solve this in a future release... I'm still curious though: I normally need routes on the interfaces, not in controllers. What scenarios are you guys needing this type of information?
    --
    Regards,
    Luis Abreu
    email: labreu_at_gmail.com
    EN blog:http://msmvps.com/blogs/luisabreu
  • Re: LinkBuilder in Controller

    07-24-2008, 9:36 AM
    • Member
      18 point Member
    • danmalc3000
    • Member since 07-24-2008, 12:15 PM
    • Posts 16

     A couple of possible scenarios where you'd want to use controllers to build links:

    • Generating article links in an RSS feed
    • Product links in data feed for e-commerce site (text file supplied to affiliates etc). 

    In these cases, the text would be generated directly by the controller and written to response via a ContentResult.

    I am also working on an XSLT-based site and it is easier to include urls in the model objects as you can't call LinkBuilder from within an XSLT. In this project, we are creating simplified DTOs based on business objects that contain all data needed for display purposes, including links.

    Where is the best place to log this type of request for changes to the framework?

    Dan 

     


     

  • Re: LinkBuilder in Controller

    07-24-2008, 10:33 AM
    • All-Star
      25,662 point All-Star
    • Luis Abreu
    • Member since 02-12-2005, 1:22 AM
    • Madeira [Portugal]
    • Posts 5,368
    • TrustedFriends-MVPs
    Hello again Dan. I understand...I have had similar problems and what I did was leave those responsibilities to the view. let me explain this better. For instance, the products example you gave. in my case, i'd have a collection of products and I'd delegate the rendering to the view (where the view would be responsible for generating text, html, xxml, whatever - the important thing is that in delegating to the view, I will be able to let it generate the correct links). notice that I'm not saying that generating the urls on the contorller is wrong. I'm just saying that in similar scenarios I ended up generating the links in the view.
    --
    Regards,
    Luis Abreu
    email: labreu_at_gmail.com
    EN blog:http://msmvps.com/blogs/luisabreu
  • Re: LinkBuilder in Controller

    07-28-2008, 6:28 AM
    • Member
      18 point Member
    • danmalc3000
    • Member since 07-24-2008, 12:15 PM
    • Posts 16

    Thanks for the useful advice, in the case of data feeds etc I would probably also opt to use a view as suggested.

    However when creating DTOs that get converted to XML for use by XSLT (e.g. CategoryDTO has a URL property) I have no choice but to generate the urls within the controller action.

    Although the ViewContext parameter in the call to LinkBuilder.BuildUrlFromExpression can be null, this will result in a new RequestContext being created from the current HttpContext in the underlying call to RouteCollection.GetVirtualPath (in the System.Web.Routing assembly). This will result in an exception if the method is called outside of a real HttpContext, e.g. during a unit test.

     

    I ended up writing the following class (see below) which allows you to generate a url to an action using a RequestContext and Expression.

    I'm dubious that LinkBuilder will ever require the properties of the ViewContext because the method that generates the link (RouteCollection.GetVirtualPath method in System.Web.Routing) requires a RequestContext. The RedirectToRouteResult in MVC (which runs "Controller side") also makes a call to GetVirtualPath with a RequestContext. I'm hoping that LinkBuilder.BuildUrlFromExpression will be changed in a future MVC release to use a RequestContext parameter instead so we can get rid of this work-around.

    Cheers

    Dan
     

     
    1    using System;
    2 using System.Linq.Expressions;
    3 using System.Web.Mvc;
    4 using System.Web.Routing;
    5 using Microsoft.Web.Mvc.Internal;
    6 7 namespace Whatever
    8 {
    9 /// <summary>
    10 /// Creates url that can be used in a link to a controller action, based on an
    11 /// Expression containing a call to the action with the required parameters
    12 /// </summary>
    13 /// <remarks>
    14 /// This is based on the LinkBuilder.BuildUrlFromExpression method within MVC preview 4.
    15 /// However the original method requires a ViewContext which is not available when the
    16 /// method is invoked from within a Controller method.
    21 /// </remarks>
    22 public static class ControllerLinkBuilder
    23 {
    24 public static string GetActionUrl 25 (RequestContext context, Expression> action) where T : Controller
    26 {
    27 return BuildUrlFromExpression(context, action);
    28 }
    29 30 public static string BuildUrlFromExpression 31 (RequestContext context, Expression> action) where T : Controller
    32 {
    33 RouteValueDictionary values = ExpressionHelper.GetRouteValuesFromExpression
    34 (action);
    35 VirtualPathData vpd = RouteTable.Routes.GetVirtualPath(context, values);
    36 return (vpd == null) ? null : vpd.VirtualPath;
    37 }
    38 }
    39 }
     
  • Re: LinkBuilder in Controller

    07-28-2008, 6:36 AM
    • All-Star
      25,662 point All-Star
    • Luis Abreu
    • Member since 02-12-2005, 1:22 AM
    • Madeira [Portugal]
    • Posts 5,368
    • TrustedFriends-MVPs
    Hello. well I guess that is all a matter of perspective. For instance, I'd use a different approach. For instance, I'd go with a DTO that has the necessary info about an object and I'd delegate the generation of the url to the view. As always, this is open to interpretation and your scenario might also be viable (I was just tryting to say that there's also an alternative where you don't need to get the url from the controller - for instance, in my example I'd considder that the url is a representation of the data and can be delegated to my view). regarding your code, yeah, it seems good :)
    --
    Regards,
    Luis Abreu
    email: labreu_at_gmail.com
    EN blog:http://msmvps.com/blogs/luisabreu
Page 1 of 1 (10 items)