append versionnumber to asp:ScriptManager's compositescript tag

we are trying to get the following scenario working:

we use asp:scriptmanager / CompositeScripts to combine our scripts into a few script blocks but after each deploy to the test system we have trouble with testers not getting updated versions of css and javascript (browser cache). For the CSS we have defined our own css user control which appends a Siteversion parameter "?v=1.0.190" for instance to the css url. This siteversion is defined in web.config / appsettings and is bumped on every deploy.

We wanted to be able to use the same strategy for javascripts, but thus far I haven't had any success. When rendering the script tag. the Scriptmanager renders

<script src="/ScriptResource.axd?d=..." type="text/javascript"></script> 

Given that the current configured siteversion was 1.0.190, I would want it to render

<script src="/ScriptResource.axd?d=...&v=1.0.190" type="text/javascript"></script> 

How do I get at the "script" html output from the script manager so I can change it? It doesn't seem to be present in the stuff rendered in Render, RenderChildren or RenderControl

Yours Andreas

Answers:

Answer

A much simpler solution in your page's onload works just fine for me:

ScriptManager.GetCurrent(this).CompositeScript.Path += "?v=" + MyBuildNumber.Value;
Answer

I dug around in reflector for a bit and it looks like this is unfortunately a tricky thing to add. MS didn't provide any good extension points that I could find. However, if you're willing to resort to a nice reflection hack, adding the following ControlAdapter for ScriptManager should do the trick:

public class VersionedScriptManagerAdapter : ControlAdapter
{
    protected new ScriptManager Control
    {
        get { return (ScriptManager) base.Control; }
    }

    protected override void OnPreRender(System.EventArgs e)
    {
        base.OnPreRender(e);

        var compositeScriptField = Control.GetType().GetField("_compositeScript",
                                                              BindingFlags.NonPublic | BindingFlags.Instance);
        var currentCompositeScript = Control.CompositeScript;
        var versionedCompositeScript = new VersionedCompositeScriptReference();
        CopyCompositeScript(currentCompositeScript, versionedCompositeScript);
        compositeScriptField.SetValue(Control, versionedCompositeScript);
    }

    private void CopyCompositeScript(CompositeScriptReference sourceCompositeScript, CompositeScriptReference targetCompositeScript)
    {
        targetCompositeScript.Path = sourceCompositeScript.Path;
        targetCompositeScript.ResourceUICultures = sourceCompositeScript.ResourceUICultures;
        targetCompositeScript.ScriptMode = sourceCompositeScript.ScriptMode;
        foreach (var scriptReference in sourceCompositeScript.Scripts)
        {
            targetCompositeScript.Scripts.Add(scriptReference);
        }
    }

    private class VersionedCompositeScriptReference : CompositeScriptReference
    {
        protected override string GetUrl(ScriptManager scriptManager, bool zip)
        {
            string version = ConfigurationManager.AppSettings["ScriptVersion"];
            return base.GetUrl(scriptManager, zip) + "&v=" + version;
        }
    }
}

Then to hook up this control adapter, you will need to create a Web.browser file and put it in your App_Browsers folder on the website. The Web.browser file should look something like this:

<browsers>
    <browser refID="Default">
      <controlAdapters>
        <adapter controlType="System.Web.UI.ScriptManager"
          adapterType="MyNamespace.VersionedScriptManagerAdapter">
        </adapter>
      </controlAdapters>
    </browser>
</browsers>

I tested this out and it worked for me. I hope its helpful to you.

Answer

I will suggest a diferent aproche. The chache is your problem, then change the cache Header. Here is an example that I make and test it and its works just fine. On the global.asax on the very start of the call...

protected void Application_BeginRequest(Object sender, EventArgs e)
{
    HttpApplication app = (HttpApplication)sender;

    string cTheFile = HttpContext.Current.Request.Path;

    if (cTheFile.EndsWith("ScriptResource.axd", StringComparison.InvariantCultureIgnoreCase))
    {
        // here is the trick with your version !
        string etag = "\"" + app.Context.Request.QueryString.ToString().GetHashCode().ToString() + "1.0.190" + "\"";
        string incomingEtag = app.Request.Headers["If-None-Match"];

        app.Response.Cache.SetETag(etag);

        if (String.Compare(incomingEtag, etag) == 0)
        {
            app.Response.StatusCode = (int)System.Net.HttpStatusCode.NotModified;
            app.Response.StatusDescription = "Not Modified";                            
        }
        else
        {
            app.Response.Cache.SetExpires(DateTime.UtcNow.AddMinutes(1));
            app.Response.Cache.SetMaxAge(new TimeSpan(0, 1, 0));
            app.Response.Cache.SetCacheability(HttpCacheability.Public);
        }
    }
}

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.