2

I've overridden UrlHelper.Content() method. Now I want my implementation to be used instead of default UrlHelper class.

How can I configure MVC to tell it which class to inject into WebViewPage.Url property?

Update 1:
The idea is simple. Bundles support cache busting by adding timestamp query parameter to the url.
I want the same functionality for single resource.
UrlHelper class allows to override its Content(string) method. Consequently it is possible to take resource's timestamp into account when generating final string.

Update 2:
It seems like my premise was wrang. I thout that src="~..." is equivalent to src="@Url.Content("~...")". That is not the case.

Pavel Voronin
  • 13,503
  • 7
  • 71
  • 137

2 Answers2

2

You're gonna need to introduce your own WebViewPage that is providing its own implementation of UrlHelper which will override the Content() method.

First, create the types:

public class MyUrlHelper : UrlHelper
{
    public MyUrlHelper() {}
    public MyUrlHelper(RequestContext requestContext) : base(requestContext) {}
    public MyUrlHelper(RequestContext requestContext, RouteCollection routeCollection) : base(requestContext, routeCollection) { }

    public override string Content(string contentPath)
    {
        // do your own custom implemetation here,
        // you access original Content() method using base.Content()
    }
}

public abstract class MyWebPage : WebViewPage
{
    protected override void InitializePage()
    {
        this._urlHelper = new MyUrlHelper(this.Request.RequestContext, RouteTable.Routes);
    }

    private MyUrlHelper _urlHelper;
    public new MyUrlHelper Url { get { return _urlHelper; } }
}

// provide generic version for strongly typed views
public abstract class MyWebPage<T> : WebViewPage<T>
{
    protected override void InitializePage()
    {
        this._urlHelper = new MyUrlHelper(this.Request.RequestContext, RouteTable.Routes);
    }

    private MyUrlHelper _urlHelper;
    public new MyUrlHelper Url { get { return _urlHelper; } }
}

Then register your custom MyWebPage in ~/Views/Web.Config:

  <system.web.webPages.razor>
    ....
    <pages pageBaseType="Your.NameSpace.MyWebPage">
         ....
    </pages>
  </system.web.webPages.razor>
haim770
  • 48,394
  • 7
  • 105
  • 133
  • Is it necessary to introduce new Url property? I think it is enough to set Url property value of base WebViewPage. – Pavel Voronin Oct 29 '14 at 10:04
  • Since `WebViewPage.Url` is a non-virtual property, you need to hide it using the `new` keyword. – haim770 Oct 29 '14 at 10:05
  • You need the `Url` property to point to your custom `MyUrlHelper`. – haim770 Oct 29 '14 at 10:06
  • All I need is polymorphic behavior, MyUrlHelper class doesn't contain additional properties. So Url property will suffice here, at least I hope for this. – Pavel Voronin Oct 29 '14 at 10:11
  • To gain this 'polymorphic behavior', you need to provide your own implementation. – haim770 Oct 29 '14 at 10:13
  • Yes, but why cannot I just substitute the instance of Url property? – Pavel Voronin Oct 29 '14 at 10:29
  • Because your Views are compiled at runtime into an arbitrary class that is deriving from the Type defined in your `Web.Config`. You must replace this page-base class with your own implementation if you need different behavior. – haim770 Oct 29 '14 at 10:32
  • What I mean is that in MyWebPAge inhereted from WebViewPage it is ok just to set WebViewPage.Url property without introducing 'new MyUrlHelper Url' property. – Pavel Voronin Oct 29 '14 at 10:36
  • Oops, I missed that option somehow. I think that'll work too. – haim770 Oct 29 '14 at 10:38
  • It seems like my premise was wrang. I thout that src="~..." is equivalent to src="@Url.Content("~...")". That is not the case. For Url.Content everything works. – Pavel Voronin Oct 29 '14 at 11:35
1

I don't have a direct answer to your question but you might just create an extension of the URLHelper class like this:

public static class CustomUrlHelper
{
    public static string CustomContent(this UrlHelper helper, string contentPath)
    {
        // your Content method 
    }
}

and then just call this method on the Url object like this:

@Url.CustomContent("~/Content/Site.css")
Groyoh
  • 342
  • 1
  • 7
  • This isn't going to work in our case. Frankly speaking I'm not sure 'override' also will help. We use tilde in urls: 'src="~...."' MVC4 converts these urls into normal. I hope it does so calling Url.Content. – Pavel Voronin Oct 29 '14 at 09:55
  • @voroninp I'm sorry to hear that. When you say that "MVC4 converts these urls into normal", you mean that it converts "~/Content/Site.css" to "/Content/Site.css" and that you need it to be "~/Content/Site.css"? – Groyoh Oct 29 '14 at 10:04
  • Not exactly. I just want Convert to take file timestamp into account. For cache busting. So, when I write src="~Scripts/my.js", I want final result to be src="VirtualDirPath/Scripts/my.js? – Pavel Voronin Oct 29 '14 at 10:08
  • @voroninp, You better use Bundles for that. – haim770 Oct 29 '14 at 10:09
  • @voroninp well, I've just tried and I managed to create an extension method that transform `src='@Url.CustomContent("~Scripts/my.js")'` to `src='VirtualPath/Scripts/my.js?'`. Would you like me to add the code here? – Groyoh Oct 29 '14 at 11:11
  • As you wish. My problem was in injecting my own instance. haim770 answer helped me. But for others who will read this post it may be helpful. – Pavel Voronin Oct 29 '14 at 11:14
  • As it was not the main topic of this question, I don't think it would be helpful if you already solved your issue. Anyway, good luck with your project! – Groyoh Oct 29 '14 at 11:26