See Joel Program

August 19, 2008

.NET 3.5 SP1 Doesn’t Provide Composite Script Registration from an IScriptControl (out-of-the-box)

Filed under: AJAX, ASP.NET AJAX — Joel Rumerman @ 9:43 pm

Note: Below the following little story, there’s a work around that I came up with so if you’re looking for help, jump down a bit.

The Story

A story of despair. Here’s how my evening went.

Act I Scene I: I just downloaded and installed the Visual Studio 2008 SP1 / 3.5 SP1 and I’m excited to take a look at how to use the Script Combining mechanism. I’ve been using the ToolScriptManager’s script combining ability for a while now, and while it is okay, it has its quirks and I was hoping the one in .NET 3.5 SP1 would be better.

Act I Scene II: I notice that the ScriptManager has a new templated section called CompositeScript. This is the spot I need to investigate closer.

Act II Scene I: I go to one of my trusty controls (AirlinesMiles.cs from my Client Event Pool example) and start to poke around at how I’m going to get the control’s script files to be part of the composite script.

Act II Scene II: Hmmm… where’s the method call on the ScriptManager object to register my ScriptReference with the CompositeScript template? Wait, I can’t find one. What the?

Shouldn’t there be something like this…

public IEnumerable<ScriptReference> GetScriptReferences()
{
    ScriptReference sr = new ScriptReference("ControlLib.UserMileageInput.js", typeof(UserMileageInput).Assembly.FullName); ;
    sr.RegisterAsCompositeScript = true;
    yield return sr;
}

or something like this…

protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);
    bool registerAsCompositeScriptControl = true;
    ScriptManager.GetCurrent(this.Page).RegisterScriptControl<UserMileageInput>(this, registerAsCompositeScriptControl);
}

but no, there isn’t anything like that.

Act II Scene III: @#$3234%%$@ #$@ @#$!

Act III Scene I: Really …… really?? There’s no built in way for my control to at least ask to be registered with the ScriptManager as a part of the composite script?

Act III Scene II: Despair.

Okay, I’m feeling a bit dramatic tonight, but I’m at least a little annoyed for feeling a bit overlooked as a control developer.

I get that MSFT probably had a discussion internally about whether to allow controls to register as a composite script or not and that its obvious that the feature didn’t make it. Maybe they don’t see it as part of the control developer’s UX, but only the page developer’s UX. I don’t know. Maybe their idea is to have us pull out the ScriptReferences of every single embedded JS file that could possible be loaded by any control and place them into the CompositeScript template? I’m not really sure what MSFT’s work-around plan is or why the ScriptControlManager wasn’t updated to support this ability in the first place, but I’ve got one that at least on the surface works. I shouldn’t have to though. I should be able to register my scripts as normal as I have for the past 3+ years.

Workaround

So I’ve come up with a little hacky work around for this problem for now and we’ll see where it takes me…

protected override void OnPreRender(EventArgs e)
{
      base.OnPreRender(e);
      ScriptReference sr = new ScriptReference("ControlLib.UserMileageInput.js", typeof(UserMileageInput).Assembly.FullName);
      sr.NotifyScriptLoaded = false;
      ScriptManager.GetCurrent(this.Page).CompositeScript.Scripts.Add(sr);
      ScriptManager.GetCurrent(this.Page).RegisterScriptControl<UserMileageInput>(this);
}
public IEnumerable<ScriptReference> GetScriptReferences()
{
    return null;
}

Pretty much I’ve moved the ScriptReference registration from the GetScriptReferences method to the OnPreRender method. This works in my little example, but I’ve yet to test it on a larger application.

Closing

Finally, for those MSFT folkd who every once in a while peruse my blog, please comment and explain why this turned out this way or if I missed something please let me know. I’m trying to hold back too much judgement until I hear from you.

12 Comments »

  1. Yup, that’s a feature for application developers. This is not a feature to help component developers build their script file from multiple scripts. That’s a separate problem and that should be done at build-time, not at runtime. I know the tooling is still a little lacking for this scenario, and we’re working on providing a solution for that too, but this solution is not runtime script combining.
    The scenario the feature addresses is an application developer who wants to create the optimized set(s) of scripts he wants his users to download and cache.
    I hope this clarifies.

    Comment by Bertrand Le Roy — August 19, 2008 @ 11:40 pm

  2. Oh, sorry I think I misunderstood the scneario you’re asking for, which seems to be closer to what the toolkit’s script manager is doing: force your scripts into the combination whether the application developer wants it or not.
    The thing is, this is a tricky feature to get right. Combining scripts could give you a significant performance boost, a very small one, or might actually hurt performance, depending on a number of factors. That’s why the feature is purely opt-in by the application developer, not the component developer.
    The typical way you’d use that feature is by adding the script profiler control to your pages on your development site (this control is available from CodePlex/aspnet), determine the set of scripts that you want on your combination based on your knowledge of your application and actual performance measurements, and then copy-paste the list of references the profiler gives you into the ScriptManager’s combined script property.

    Comment by Bertrand Le Roy — August 19, 2008 @ 11:46 pm

  3. I should probably add that there is nothing to do in particular from the component developer to make his scripts combinable. And script resource can be combined, including framework scripts and scripts that were released before SP1.

    Comment by Bertrand Le Roy — August 19, 2008 @ 11:48 pm

  4. Hi Bertrand,

    Thanks for commenting. I was hoping you or Dave would.🙂

    Yes, the second scenario is more of what I’m referring to and yes, the ToolScriptManager is what I’m using right now to accomplish this. The first scenario is not really relevant for this talk.

    I don’t think I want to force the scripts to combine if the application doesn’t want to, but I definitely think that the ability to combine scripts that come from components should be in the base ScriptManager and not in the ToolScriptManager, which is what I expected this release would provide.

    Why would the optional ToolScriptManager have a major feature where it can combine component based scripts that didn’t make it to the base manager? I don’t see the reasoning for spinning a different version of script combining that works completely differently! Now you’ve got two different versions of script combining… have fun maintaining both and getting the community to understand when I should use one vs. the other.

    Asking me to suck out the embedded JS file and add it to the composite script tag as a ScriptReference defeats the one the primary purposes of me embedding it in the first place, which is that the control owns its scripts and everything is encapsulated in it. I place a control on the page and it registers server functionality, client layout, and client functionality. Simple as that. All I want now is a way for those components not to each cause an HTTP request to a small JS file AND maintain the ability to develop small JS files.

    Further, how would I go about setting up my composite scripts in an UpdatePanel scenario that loaded some controls in situation A, some controls in situation B? Would I simply put all the script references in the composite block to begin with?

    Finally, any application that is a bit more complex than a series of web pages relies upon custom built components. You added a page-level feature without any thoughts to the majority of complex ASP.NET projects that use components and controls to build functionality. Unfortunately, I see a really small uptake on the script combining feature in SP1 because it doesn’t work at the component level.

    Comment by Joel Rumerman — August 20, 2008 @ 7:37 am

  5. Well, we really think script combining should be the decision of the app developer and that like most performance optimization decisions it should be a conscious decision based on measurements and careful analysis. Solving a performance problem in a generic way often doesn’t work, and the toolkit version of script combining (that we’re going to retire eventually) falls into that category. For example, if you have many pages in your site that use different components, there is a pretty good chance that script combining as done by the toolkit will actually be worse than no combining because each page will have a different combination, resulting in poor cacheability.
    I’m not sure I’m getting your point about the component owning its script: the components still own their script and it’s still embedded in the same dll as the component. I don’t see how being able to reference it in a combination takes anything away from that. You can still just register the control and be ready to play. Script combining is an optional optimization that again should be the result of careful analysis of the benefits or lack thereof.
    The ability for you to develop using small files is entirely different, that’s more the question of how you build your script libraries and that’s a problem we’re working on right now. In the meantime, you can use simple build tasks to concatenate small script files into a large one at compile-time.
    The point you raise about UpdatePanel is very interesting and something we baked into the design. If a partial update adds a script that was already in the combination, it won’t be added, but if it wasn’t, it will be dynamically loaded as an individual script. It’s the application developer’s call whether it’s worth the extra initial payload to get everything up front in the combination or if the partial update will occur rarely enough that it’s worth delaying it. Many factors enter into that decision and there’s no way we can make that guess automatically.
    I think you’re being unfair when you say we gave no thought to the component-based scenario. We gave it a lot of thought actually and our current answer (but I’m open to suggestions on making that better) is to provide the profiling tool that enables the application developer to discover what scripts he uses on his page, including ones that were added by components.

    Comment by Bertrand Le Roy — August 20, 2008 @ 1:22 pm

  6. Bertrand,

    First, thanks so much for engaging me in this conversation. It takes brain cycles to come up with thoughts and write them down, and I appreciate it.

    ….

    I guess I’m missing something. If I combine Script A, B, and C, wouldn’t that create a unique URL that corresponds to those three script files? So on the next page if I combined Script B, C and D, that’d be creating a different unique URL so I’d be downloading Script B and C over again. Based on this premise, the ToolScriptManager and ScriptManager’s new ability have the exact same problem of cacheability. (Am I wrong about the unique URL? I’m not at home with my test bed so I can’t test right now.) The only way I see that not being a problem is if the script URL doesn’t contain information about what scripts it’s going to load, but I can’t see how.

    As a hook for the component developer, why couldn’t the internal ScriptControlManager object, when processing the IScriptControls that were registered with it to add the scripts to the page, check the ScriptReference and look at a property on it “TryAndRegisterAsCompositeScript” and if the application developer allows it, add the script to the composite script collection? That way the app developer is still in charge of script combining (by adding a property to disallow controls from doing this to the ScriptManager, i.e. “AddAutomaticCompositeScriptsFromControls”) but can also benefit from controls that ask for it. IMO, it seems the best of both worlds and seems like an easy fix (of course we both know nothing is).

    As for me being unfair, in my comment, yes, I was. I really should have said you provided a page-level feature without a similar component-level feature which is needed by the majority of complex projects that use components and controls. (In my defense, I said in the post that I was sure that there was debate over this and giving the ability to the component developer didn’t win.)

    I just can’t see myself as an application developer with a dynamic application that loads controls based upon a series of variables and situations, trying to figure which scripts I’m going to put into this composite script block. It seems that I would want to combine all the scripts for all the components that got loaded on the page. That is my main use case and that’s missing.

    Comment by Joel Rumerman — August 20, 2008 @ 3:15 pm

  7. In the situation that you describe, the application developer might choose to combine B and C on both pages, in which case each page only downloads what’s specific to them (this works well if most users only visit one of the two pages). Or he might decide to have completely different combinations like you describe, and that is the only possibility when using the toolkit’s manager. Or he might choose to combine A, B, C and D in both pages, which might be a good idea if most users visit both pages. As you see, the choice really depends on many factors that we can’t possibly guess.
    Again, what is important is not whether the application accepts combining, but *what* it combines. This can’t be done automatically *and* right.
    How do you know that systematically combining all scripts on the page is a good idea? We have had long debates about whether we should have a property on ScriptManager to auto-combine (even if it’s false by default) and decided against it because we thought most people would just set it to true and forget about it, which is a poor performance decision. If a feature’s only goal is better performance and it actually degrades performance in typical scenarios, it’s not a good feature. We knew it would be difficult to explain😉

    Comment by Bertrand Le Roy — August 20, 2008 @ 4:33 pm

  8. You know what? I think I should blog about that…

    Comment by Bertrand Le Roy — August 20, 2008 @ 4:33 pm

  9. I totally see your point about it depends on what the user actually does. On a page where I load components A, B, and C and they all have individual scripts associated, what’s the case that I wouldn’t want to combine them? If I go to another page and it has components A and B? In that case, on the first page I would’ve wanted to combine A and B on the first page, and excluded C (maybe) so A and B are cached.

    I’m kind of seeing your point now and I guess I’m the exact user who you were worried about that would combine all of his scripts somewhat blindly.

    But in my case if I’ve got 15 HTTP requests for custom scripts, and I can get that down to one request each time, it seems that’s what I should do everytime … but maybe not. Maybe I’m actually shooting myself in the foot a little bit because unless the exact same combination of scripts is used next time, I won’t get a cache hit.

    Ahhh, too much food for thought.

    Definitely blog about your last thought (and this whole topic)!

    You opened my eyes a bit and I thought I knew what I was doing🙂.

    Comment by Joel Rumerman — August 20, 2008 @ 5:07 pm

  10. Question, how do you debug scripts set in the CompositeScript?

    Comment by Jason — September 29, 2008 @ 6:40 am

  11. Try following:

    http://www.codeproject.com/KB/aspnet/AspNetOptimizer.aspx

    Comment by Moiz Dhanji — October 21, 2008 @ 6:54 am

  12. Moiz,

    It’s funny. I hardly ever read articles from my daily Code Project email, but I happened to actually read most of the articles that came attached to yesterday’s email so I’d already read your article.

    That being said, while I appreciate the solution you’ve come up with, it doesn’t solve my problem any better than the ToolkitScriptManager does. It’s still a shotgun approach where you combine all the ScriptReferences that are on the page into one request. I think this is normally fine. Bertrand would disagree, though, saying it should be left up to the page developer which files are combined.

    More so, it doesn’t utilize any of the combine script features of .NET 3.5 SP1. You’ve completely rolled your own solution here. This post was mainly a complaint that MSFT didn’t supply any way for the control developer to participate in the composite script combining without jumping through a hoop and changing the way they coded.

    Now one way I’ve been working with to utilize the .NET 3.5 SP1 composite script collection is to override OnResolveScriptReference in my own ScriptManager class, much like you and the ToolScriptManager did. I haven’t completely finished it yet, but it looks promising.

    Comment by Joel Rumerman — October 21, 2008 @ 7:31 am


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: