Today @ 10:39 am
- from Hood River, Oregon
I'm finally getting ready to install my new Server and after some back and forth and testing on Windows 2008 I decided to go ahead and bite the bullet and go with the 64 bit version of Server. I've had mixed feelings about installing 64 bit given that I have had exactly zero experience running under 64 bit. I've not really seen a compelling reason on the desktop to run a 64 bit OS especially given the many reports of hardware and software incompatibilities.
So, I got my Dell T300 server last week. It's a Xeon quad 2.5ghz, 4 gig, 2 250gig drives. The machine is sweet and screams, but I didn't realize what a freaking beast it would be. It's insanely huge and weighs nearly 50 pounds (no shit - the package weighed 58 pounds!). It's a full size box and looking inside it uses that space - the cooling tower is as high as the full height of the case, with 6 fans and wind tunnels to route air that make the thing sound like jet when starting up and testing all the fans at once. Entertaining - good thing it's going into a server room and not into an office <s>... In retrospect it might have been smarter to buy a rack server box, but prices for a similarly configured machine are nearly triple.
When the box arrived I was still on the fence whether to install 32 bit or 64 bit Server 2008. I still have a host of 32 bit server apps and so I've been reluctant to go 64 bit. But Microsoft's been saying all along that this version of server will be the last 32 bit version and most of the server products are going to be 64 bit probably before the next version of the server OS. Time to get familiar with 64 bit it seems.
So I installed the 64 bit version right away to 'play' with my existing server apps to see how they'd fare on a 64 bit box. The initial verdict was - no problems and a few pleasant surprises - at least so far (Murphy will come a knocking I'm sure). Today the final pieces of extra hardware (more memory and a 10k boot drive) arrived and now I'm knee deep in reinstalling the box from scratch for the live machine.
Here are a few thoughts and comments that might be interesting to some of you as well.
32 Bit Mode for Application Pools in IIS 7 - Yeah!
My biggest original concern against installing a 64 bit version of Windows Server is that I have a number of older COM and ISAPI based applications running on my site. In IIS 5/6 that would have been a fatal issue given that IIS 6 and 5 required that the Web server is switched into 32 bit mode entirely in order to run ANY 32 bit applications, like my ISAPI assembly.
It turns out IIS 7 makes this process much more granular with the ability to switch an IIS Application Pool into 32 Bit mode rather than the entire Web Server:

Cool that and that 'little detail' right there addressed my biggest concern about the old apps running on the 64 bit version of Server.
I hooked up this Application Pool in the original test builds of the OS I had created and ran a two day load test on the existing West Wind Web Connection sites and the apps chugged away at outrageous speed on this hardware mind you and without any hiccups. Looks like the 32 bit apps (West Wind Web Connection ISAPI + Visual FoxPro COM) will have no issues whatsoever on the 64 bit server.
The new quad processor is going to help these older apps tremendously too. There are a few rather slow requests that have been chugging up the CPU to near 100% levels at times for a handful of particularly long queries, but with the quad these queries will no longer hold the entire machine at ransom. Testing those particular scenarios resulted in smooth operations which solves yet another issue that has been the cause of occasional server lockups in the past.
ASP.NET apps can run just fine in 64 mode unless there are special circumstances (like the app is accessing some 32 bit inprocess COM components!) so none of this applies for typical ASP.NET apps, but it's a life saver for my 'other' apps.
A few 64 Bit Application Gotchas
I did run into a few utility applications that failed to run under 64 Bit Windows Server. Systernals AutoRuns bonked a bunch of times when I originally ran it, although that magically cleared itself up (possibly after turning UAC off). One other small internal application (an old VB configuration app) also choked and died unceremoniously, but all minor issues.
I also ran into a problem with my own West Wind Web Monitor utility which is a .NET application. The app would start and immediately crash after startup with an error about side by side configuration. A look in the event log brings this:
It turns out this problem is related to a reference in the Assembly manfiest file (WebMonitor.exe.manifest) which includes a reference to the above DLL and so fails.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
name="Westwind.WebMonitor.WebMonitor"
type="win32"
/>
<description>West Wind West Wind Web Monitor</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>
This is an old left over from the days when Application.EnableVisualStyles would crash apps frequently and using the assembly manifest would allow for Windows Theming without this Application call. Removing the .manifest file made the app run both as a desktop app and as a Windows Service. Alternately removingjust the reference to the above assembly worked.
One odd thing about this though: The error occurs only when running running West Wind Web Monitor out of the x86 (32 bit) Program Files path. When running from 64 bit (just plain Program Files) path (more on that below) the manifest file is not a problem.
Which made me go back to my installer (I use and love Tarma Installer) and changed the installation to create output into the 64 bit folder if available for the installation :
Now on 64 bit machines the app installs properly into Program files instead of Program Files (x86).
Running a .NET app as 64 bit
It is kind of cool to see your .NET app running as a 64 bit app, without having done anything special to work in 64 bit mode. It just works:
ASP.NET Applications - No Problem
The final big chunk of functionality that needs to work is of course ASP.NET applications. Since these apps are .NET 64 bit should be no problem and it looks like this is indeed the case. I'm half way through moving sites and getting them back up and running and they are all chugging along happily in 64 bit mode without any issues.
There's some setup I had to go through with this however, because I decided to move all apps to integrated mode in IIS 7. Integrated mode use the new integrated ASP.NET style IIS pipeline that natively runs managed code inside of the IIS engine itself which allows runnning managed modules and handlers directly from the server. This allows for lower level request interaction such as filtering ANY content - not just ASP.NET mapped extensions - through .NET based code modules at the Virtual Application level.
IIS 7 can still run in the same 'Classic' mode as IIS 6 did, but I figure it's a good idea to move apps consistently over to the integrated pipeline.
The biggest issue in this regard is that Web.Config requires a few small changes. Namely modules and handlers need to be moved into the <system.webServer> section.
<?xml version="1.0"?>
<configuration>
<system.web>
... Remove these:
<httpHandlers>
<add verb="GET" path="wwBanner.ashx" type="Westwind.Web.Banners.BannerHandler, Westwind.Web.Controls" />
<add verb="POST" path="MetaWebLogApi.ashx" type="Westwind.WebLog.MetaWebLogApi,WeblogFramework" />
</httpHandlers>
<httpModules>
<add name="PostRedirector" type="Westwind.WebLog.PostRedirector,weblog" />
<add name="wwScriptCompression" type="Westwind.Web.Controls.wwScriptCompressionModule,westwind.web.controls" />
</httpModules>
...
</system.web>
... Add here:
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<add verb="GET" name="wwBanner" path="wwBanner.ashx" type="Westwind.Web.Banners.BannerHandler, Westwind.Web.Controls" />
<add verb="POST" name="metaweblog" path="MetaWebLogApi.ashx" type="Westwind.WebLog.MetaWebLogApi,WeblogFramework" />
</handlers>
<modules>
<add name="PostRedirector" type="Westwind.WebLog.PostRedirector,weblog" />
<add name="wwScriptCompression" type="Westwind.Web.Controls.wwScriptCompressionModule,westwind.web.controls" />
</modules>
</system.webServer>
</configuration>
Basically IIS 7 doesn't like the <httpHandlers> and <httpModules> in system.web and they have to be moved to <system.webServer> instead. There are slight changes in the way the attributes are set so it's not just a plain copy. You can remove the old section or - if you need to support the app in multiple locations (say dev and live with one running IIS 5/6 the other IIS 7) you can use the validateIntegratedModeConfiguration attribute to disable the validation which allows the sections to exist in both places.
I'm doing this manually when I see failures on sites because I tend to want to apply the validateIntegratedModeConfiguration flag, but there's also an IIS utility that can migrate web.config for you automatically
%systemroot%\system32\inetsrv\APPCMD.EXE migrate config "West Wind Dev Site/wwstore"
where West Wind Dev site is the name of the site and wwstore is the virtual. You can find out more on this process here.
This is tedious as you might imagine regardless of whether you do it manually or with the utility because you have to find each app. I have somewhere around 80 virtuals on my site and remembering all of them is definitely time consuming. We'll see how much content will get fucked up here on the site once it goes live <g>...
SQL Server 2008
The final piece to install was SQL Server 2008 which showed up - very timely - on MSDN yesterday. The installation went surprisingly smooth. I say this because I had lots of issues with SQL 2005 installations in the past where the installer failed or failed to install vital pieces etc. SQL 2008's installation process feels much more streamlined and is loads faster than the interminable SQL 2005 install that used to take an hour or more. The setup and configuration console finally puts all the things you need to configure in one place instead of in obscure utilities that aren't linked or referenced anywhere. It also looks like SQL 2008 has connections open out of the box unlike SQL 2005 which had to explicitly enable external connections.
I haven't had much time to play with SQL 2008 yet - I just installed attached my existing databases of a few apps (more coming) and everything just worked. Performance is excellent (as mentioned earlier) but it's hard to tell whether there are improvements in SQL or whether it's merely the hardware. <g> SQL Management Studio looks mostly unchanged so it's not a 180 degree change again - everything's still where I'd remembered it being. One thing though I noticed that's really cool is that the Query editor now has Intellisense support built in and it's even pretty snappy.
One thing that is a problem though is that you need the SQL 2008 client tools to administer SQL 2008 - the 2005 tools do not work. Not surprising, but I didn't think of that. So I went to install SQL 2008 on my laptop as well and there is where I ran into problems. The install failed with .NET 3.5 SP1 failing to install. Looks like there's some conflict with some Visual Studio Hot fixes according to Microsoft. Scary - after screwing around with this last night for a couple of hours I had to give up. Word is that the Visual Studio 2000 SP1 installer will deal with the hotfixes properly and so - hopefully - this will work better. I'll have to wait until next week.
In the meantime I can only administer the SQL 2008 server via Remote Desktop...
But of course that issue has nothing to do with the server install. This is a client configuration issue on my end.
So far so good
So far things are looking very good for the new server. I'm pretty stoked and I wish I could get this thing online as soon as possible. All the things I was potentially worried about running under 64 bit are running without any issues which is a relief. However, I'm not even close to done. So far I've only installed the core applications - the bread and butter apps that keep the business running. All the little sample apps and obscure little places where code is running are probably what's going to take most of the time to configure. There are also 4 more sites (which are really just single apps) to configure. Ugh, it's going to be a long process. Still I hope to get the server online by early next week...
Wish me luck.
Monday @ 4:00 pm
- from Hood River, Oregon
A few days ago somebody asked me why I bothered in the West Wind Ajax Toolkit with building a custom JSON parser, which is a valid question. After all Microsoft now has two separate JSON parsers in the .NET framework so why use a custom parser?
Some time ago - before the days of Microsoft Ajax 1.0 - I built my own .NET JSON serializer and deserializer, because there's was limited support for JSON serialization and more importantly deserialization available at the time. There were a few frameworks out there - including my favorite which was Anthem at the time - which included JSON serializers that could send JSON data to JavaScript clients for consumption. Generating JSON output is actually pretty straight forward as the encoding mechanisms are easy to produce even for complex and hierarchical objects from managed code. The other end - de-serialization into a specific .NET type - is considerably more difficult to do because the language format for JSON is fairly terse with many redundant symbols. Parsing JSON is no picknic especially if you want to build a parser that can validate JSON up front or during processing as invalid (and in fact, the one I built doesn't).
When I first started out on this path I used Anthem's serializer as a base line and then tacked on my own de-serializer. Right around the time that happened Microsoft released the fist builds of ASP.NET AJAX (ATLAS at the time) which also included a new JavaScriptSerializer, which I was glad to see at the time. I figured my code wouldn't be needed for too long so I didn't spend much time on making the de-serializer work perfect in all scenarios and let it be 'good enough' for now to wait for release in the .NET framework. Heck, after all deserialization of client data coming back to the server is less common than sending data, especially when talking about complex types coming back from the client. But the need quickly accelerated as we all discovered the power of building JSON style RPC service interfaces that could effectively feed data back into the front Web UI. It turned out to be a long haul to MS AJAX 1.0 and in the meantime I needed a solution and so the parser evolved.
Fast forward to today. Currently the .NET framework contains two JavaScript JSON parsers to choose from:
- JavaScriptSerializer in System.Web.Extensions
- DataContractJsonSerializer in System.Runtime.Serialization.Web
JavaScriptSerializer in System.Web.Extensions
This component is part of the ASP.NET AJAX update library in System.Web.Extensions and allows serialization and deserialization of data. The interface is pretty straight forward and works reasonably well for most data types in application scenarios. The serializer works by simply passing an object and returning a string JSON result. Type support is adequate although there are a some useful omissions.
In short you can do stuff like this:
StockServer ss = new StockServer();
StockQuote quote = ss.GetStockQuote("MSFT");
JavaScriptSerializer ser = new JavaScriptSerializer();
string json = ser.Serialize(quote);
StockQuote quote2 = ser.Deserialize<StockQuote>(json);
to serialize and then deserialize content.
At first glance this looks great - it's nice and easy but there's a small, but annoying shortcoming here: The Deserialize method is generic and so requires a type known at compile time - it can't be type variable, but has to be a type constant.
StockQuote quote2 = ser.Deserialize<StockQuote>(json);
The generic parameter must be a type constant (or typeof()) in order to work, but you can't do something like this:
Type stockType = quote.GetType();
...
StockQuote quote2 = ser.Deserialize<stockType>(json); // doesn't work
because generics are compiler based and create effectively overloaded method stubs for each type variation. Bummer in this case. I really hate it when APIs provide ONLY generic interfaces and no way to specify a type dynamically.
Anyway, this means that out of the gate you can't easily create dynamic routines that do things like parsing a number of parameters say to forward calls to an object dynamically as you might do to build a JSON service.
Actually - after posting a small blip on Twitter today - I got a hint from Stuart Thompson about using Reflection to provide a generic parameter at runtime. With that it's possible to access to the Deserialize method with a type variable:
public static object FromJsonString(string json, Type type)
{ // *** Have to use Reflection with a 'dynamic' non constant type instance
JavaScriptSerializer ser = new JavaScriptSerializer();
object result = ser.GetType()
.GetMethod("Deserialize") .MakeGenericMethod(type)
.Invoke(ser, new object[1] { json }); return result;
}
It's ugly but it works for this scenario.
To make this more reusable you can create an Extension class that provides JSON features ToJson() on object and FromJson() on string along with classic static methods:
/// <summary>
/// Json Extensions based on the JavaScript Serializer in System.web
/// </summary>
public static class JsonExtensionsWeb
{ /// <summary>
/// Serializes a type to Json. Note the type must be marked Serializable
/// or include a DataContract attribute.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ToJsonString(object value)
{ JavaScriptSerializer ser = new JavaScriptSerializer();
string json = ser.Serialize(value);
return json;
}
/// <summary>
/// Extension method on object that serializes the value to Json.
/// Note the type must be marked Serializable or include a DataContract attribute.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ToJson(this object value)
{ return ToJsonString(value);
}
/// <summary>
/// Deserializes a json string into a specific type.
/// Note that the type specified must be serializable.
/// </summary>
/// <param name="json"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object FromJsonString(string json, Type type)
{ // *** Have to use Reflection with a 'dynamic' non constant type instance
System.Web.Script.Serialization.JavaScriptSerializer ser
= new System.Web.Script.Serialization.JavaScriptSerializer();
object result = ser.GetType()
.GetMethod("Deserialize") .MakeGenericMethod(type)
.Invoke(ser, new object[1] { json }); return result;
}
/// <summary>
/// Extension method to string that deserializes a json string
/// into a specific type.
/// Note that the type specified must be serializable.
/// </summary>
/// <param name="json"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object FromJson(this string json, Type type)
{ return FromJsonString(json, type);
}
public static TType FromJson<TType>(this string json)
{ System.Web.Script.Serialization.JavaScriptSerializer ser
= new System.Web.Script.Serialization.JavaScriptSerializer();
TType result = ser.Deserialize<TType>(json);
return result;
}
}
One thing that's nice about the JavaScriptSerializer is that it works on most common .NET types and doesn't require any special object attributes. I think this is an important feature of any JSON serializer because unlike typical 'Service' interfaces often times JSON is used purely for UI services to feed data and having to specify any sort of attribute or markup contract only gets to be counter productive.
This is especially useful when used in combination with dynamic type results through anonymous types explicitly created in code or - probably more to the point - LINQ based custom results that are filtered down to only return the exact data required to the client.
Using the JavaScriptSerializer with the above extension classes allow you do the following with an anonymous type:
// *** Can serialize anonymous types with JavaScriptSerializer
var valueDyn = new
{ Name = "Rick Strahl",
Entered = DateTime.Now.AddDays(-10),
Number = 100.22M
};
json = valueDyn.ToJson();
this.WriteValue(json);
Note that while you can Serialize an anonymous type, you cannot Deserialize it because anonymous types don't have a parameterless constructor. However, if you have a concrete type that matches the object signature of the object serialized into JSON you can still potentially import the data. So you can serialize just the data you need on the client, but you can deserialize back into the full originating type.
Another issue: JavaScriptSerializer chokes on circular references which are common in data models (like LINQ to SQL).
Anyway - this dynamic behavior is quite useful and is a big plus of this serializer over the DataContractSerializer as we'll see in a minute.
The JavaScriptSerializer has another problem though: It's now marked obsolete and according to the documentation and compiler error we're supposed to use DataContractJsonSerializer for Json serialization. I think it was a good idea that JSON moved into a more generic portion of the .NET framework, but unfortunately the DataContractJsonSerializer has even more restrictive interface than JavaScriptSerializer.
So in summary:
Pros:
- Easy to use
- Doesn't require any marker interfaces to serialize objects
Cons:
- API requires static types for Deserialization (generic method only)
- Dynamic Types require Reflection and extra code
- Class has been deprecated by Microsoft
- Doesn't work with a few common data structures
- Chokes on circular references
DataContractJsonSerializer
DataContractJsonSerializer is part of .NET 3.5 WCF extensions and lives in System.ServiceModel.Web. The primary purpose of this new Serializer - and it shows - is to handle the JSON serialization support for the .NET 3.5 WCF Http extensions which provide REST and AJAX services that are specifically geared to the Web model. As such the WCF 3.5 REST and AJAX services are quite at odds with the rest of WCF in that they don't interoperate really as smoothly as other service types that can usually just tweak a few configuration settings and enjoy the same behavior for various different protocols. The difference here is that the HTTP protocols are highly specialized to REST and AJAX scenarios which doesn't fit with the SOAP style messaging normally used in WCF. IOW, the HTTP REST/AJAX services are the odd man out when it comes to WCF functionality.
The DataContractSerializer clearly is geared towards serving the needs of the WCF platform as it uses and even requires use of attributes to adorn objects and members to allow them to be serialized and deserialized. And therein is probably the biggest downfall of the DataContractJsonSerializer: It requires WCF compatible types and attributes.
The model for serialization is also a bit more verbose than the JavaScriptSerializer, although it's nothing that a small wrapper method can't fix easily. Here's how to serialize and deserialize:
StockServer ss = new StockServer();
StockQuote quote = ss.GetStockQuote("IBM");
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(StockQuote));
MemoryStream ms = new MemoryStream();
ser.WriteObject(ms, quote);
string json = Encoding.Default.GetString(ms.ToArray());
// *** Deserialize
ms = new MemoryStream(Encoding.Unicode.GetBytes(json));
ser = new DataContractJsonSerializer(typeof(StockQuote));
StockQuote quote2 = ser.ReadObject(ms) as StockQuote;
ms.Close();
The DataContractJsonSerializer is a little more work to use than the JavaScriptSerializer as it works off input streams, but it's still pretty simple. The code used can be easily abstracted and the serializer works well as long as you're working off serializable types.
For completeness here's the same extension class using the DataContractSerializer as the base converter:
public static class JsonExtensions
{ /// <summary>
/// Serializes a type to Json. Note the type must be marked Serializable
/// or include a DataContract attribute.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ToJsonString(object value)
{ DataContractJsonSerializer ser = new DataContractJsonSerializer(value.GetType());
MemoryStream ms = new MemoryStream();
ser.WriteObject(ms, value);
string json = Encoding.Default.GetString(ms.ToArray());
return json;
}
/// <summary>
/// Extension method on object that serializes the value to Json.
/// Note the type must be marked Serializable or include a DataContract attribute.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ToJson(this object value)
{ return ToJsonString(value);
}
/// <summary>
/// Deserializes a json string into a specific type.
/// Note that the type specified must be serializable.
/// </summary>
/// <param name="json"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object FromJsonString(string json, Type type)
{ MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json));
DataContractJsonSerializer ser = new DataContractJsonSerializer(type);
object value = ser.ReadObject(ms);
ms.Close();
return value;
}
/// <summary>
/// Extension method to string that deserializes a json string
/// into a specific type.
/// Note that the type specified must be serializable.
/// </summary>
/// <param name="json"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object FromJson(this string json, Type type)
{ return FromJsonString(json,type);
}
}
The big problem with DataContractSerializer is that it only works off serializable types. You need to either have the type marked with a [DataContract] and explicit [DataMember] values or the type must be marked as [Serializable]. Both might make good sense for WCF style service scenarios but for JSON messages sent from Web applications this requirement is pretty much a deal killer. This also means no anonymous type support so if you want to use this serializer with data returned from LINQ queries you'll have to make sure you return full entities and these entities are marked up with the appropriate attributes. Yechh...
Pros:
- Microsoft's official JSON parser
- Works with dynamic types
- Updated and integrated with WCF
- Deals with circular references (also some control over depth of serialization)
Cons:
- Requires marked up types - Serializable or DatContract/DataMember required
- Requires a number of assembly refs
So where does that leave us?
As I mentioned at the outset, the two parsers are workable, but both have their issues. Clearly Microsoft is pushing towards DataContractJsonSerializer, but with it come some limitations that make it somewhat unusable in certain scenarios. For me the inability to push results from dynamic LINQ queries to the client is proving to be fatal.
JavaScriptSerializer works well enough with a few exceptions, but the problem here is the the Obsoleting by Microsoft. Also I have a fair bit of legacy code that uses DataSets and DataTables fed back from list based data of business objects and it's very useful to feed that to the client as object arrays. This will change eventually, but in the meantime this old code needs to continue to work with this data. This can be done with JavaScriptSerializer by creating special TypeResolvers but it's a pain - this should just be built in even if some functionality is one way.
The other issue is that both serializers are using some proprietary formats for things like dates. It's not a bad solution as long as you're running MS AJAX or a compatible framework (my toolkit also supports those formats), but if a client passes data in a different format (say new Date() or the new 'suggested' ISO date literal) the deserializers choke, which is problematic when dealing with non-.NET based client JSON serialization.
For me - it means I can't switch to the built in JSON parsers, so for the time being I'm sticking with my existing home grown parser which addresses all of these issues. But, maintenance of a parser is always a pain especially as standard changes and additions are likely to occur. JavaScript serialization is pretty trivial, but the deserialization can get pretty gnarly quickly especially if validation is required :-{. I'd much rather use something out of the box. So I'm hoping that Microsoft will step up and provide something that's a little more light handed than the DataContractJsonSerializer.
There are also several other libraries out there including JSON.Net, Ajax.Net Pro, Anthem.Net and several others that also provide JSON services with varying degrees of flexibility as is the West Wind Ajax Toolkit where you can find the above mentioned free standing JSON parser.
I use JSON serialization daily and for the moment at least I'm glad that I have a custom parser in place because I have more control over the process and can allow any kind of content to go through the parser and format it as needed easily. In the end that's what counts...
Saturday @ 4:36 pm
- from Hood River, Oregon
Woo hoo. I just rolled over the magic 1000 Weblog post mark (uh, 1001 now <sigh>) last night and didn't even notice until today when I was about to post another entry. Looking at the site this morning I realized the big fat triple 0's showing up on the Post count.
It's been a little unceremonious I suppose - it's pure luck that I noticed since I rarely look at the actual post or comment counts these day. Heck I don't even visit the actual site all that often - I post from Live Writer and usually jump straight into my Admin interface if I need to customize my post's attributes so I rarely look at the numbers.
To make things even less ceremonious, the last couple of weeks have been slow for me with posts as I finished up a contract in Europe and started traveling back to the US. Things should pick back up now that I'm back 'on the horse' with a bit more content lining up.
One thousand posts doesn't seem like all that much but this number stretches back over four and a half years with the inaugural post on December 13th, 2003. Looking over those really old posts is like a jump back into history. Then again it was a lot easier back then to find something to write about. Nowadays it's easy to dismiss something simple as too mundane to post about - gotta reconsider that attitude. Some of the most useful posts or that generate the most responses are those quick and dirty hints and tips.
Ok, time to move on - onwards to the next 1000. I suspect it'll take a little longer yet.
[Later]
Just as I was going to post this entry my server wasn't available. Grrrr... failures are getting more frequent now. Ironically I've spent the last day building up my new server that will replace this box probably next weekend. The Murphy of the old wants to make himself known just one last time. This time I was able to check out the box (the ISP kindly hooked up a separate KVM to the box and it turns out that network card is jittery - box is alive and running locally, but hte network out connection wasn't working. Jiggled the wire a bit and it came right back. In the end opened the box and reseated the card hoping that it'll get me through the next week <g>...
Saturday @ 1:23 am
- from Hood River, Oregon
If you install and run or manage a Windows Server OS you've probably run into the annoying shut down dialog. You know what I mean - that dialog we curse at that slows down or 'hangs' the shutdown. Maybe you have even been so lucky by walking away after clicking Restart on the machine and forgetting about the damn dialog hanging the system before rebooting/restarting. You walk away thinking you're done only to find the server hung an hour later. Well, just did that - thankfully on test install, thank you very much. It's not a secret how to get around this and it's easy to fix, but buried deep enough that it's easy to not find it immediately and so not fix it.
I'm running through a few test installs on my new server, which arrived yesterday. I'm installing Windows 2008 Server on it and running through my configuration scenarios. This is actually the first time for me on Win2008 and so I'm purposefully going through the server configuration and installation to familiarize myself a little better with the OS in general. I know Win2003 pretty well but Server 2008 is going to take some time to get used to. Overall things are going well, but I've done 4 separate installs now with one main install and 3 virtual installs to experiment with the Server 2008 hypervisor. And each time I've run into the shutdown dialog about 5 times of Microsoft software related reboots.
Here's my friendly dialog being given the finger:
Most helpful. I can't remember that I've EVER put anything useful into this dialog other than "F*** you". I'd love to see the record that Microsoft gets from this - I'm sure there are some choice comments.
Well as it turns out this data isn't even going to Microsoft - instead it's going into the event log so net admins can get a chart out and see just how often Windows needed to be rebooted. Yippieh - how enormously useful.
Anyway, the dialog is very annoying and althogh easy to disable it's not exactly obvious. Here's how:
- Run the Group Policy Editor by running gpEdit.msc from the Run box
- Navigate to Computer Configuration | Administrative Templates | System and find Display Shutdown Event Tracker
Like this:

and poof it's gone. Peace and quiet returns to the shutdown process. The same approach can be used in Server 2003, although I think the setting name is negated - something like Don't display Shutdown Event Tracker or such.
Now, I'd really like to know if there's anybody who's using this tool religiously and putting nice little comments into it and evaluating this data. I think more than anything this thing is just annoying the heck out of people and having it on by default just seems a plain bad call. All it is is counterproductive and pissing people off - one more reason that Big Brother is watching and 'controlling our machines'. Not so, but the perception is there and this one thing that just doesn't need to be there.
It's not like we don't know what causes restarts - it's no secret to Microsoft that 90% of reboots in Windows are caused not by any failures, but by Microsoft Software Installations which are amongst the most intrusive types of applications that install on any system. And Microsoft probably knows that already and so do we. Duh!
It's easy to fix of course, but it's buried several levels deep and just inconvenient enough that it gets put off and off again and never done in the end. In fact, I just noticed that my live Web server box (running Win2003) still has the dialog popping up, because, well it didn't seem so important at the time, but I know it has annoyed me more than a few times and I couldn't offhand remember how to turn off the dialog, so it's stayed up - for 5 years. Why not make it obvious and have a check box option on the dialog to turn it off?
Life should be so easy.
This is nothing new of course and if you want to find out more about what the Shutdown Event Tracker does, how it's supposedly useful and where it stores this information this link provides additional information.
August 01, 2008 @ 8:42 am
- from Hood River, Oregon
I'll be doing my annual presentation at the Portland Area .NET User Group (PADNUG) next Tuesday August 5th, 2008. The topic this time around is Using jQuery with ASP.NET which is a fun presentation that covers JavaScript and specifically jQuery use with ASP.NET without using ASP.NET Ajax.
Here's the presentation abstract:
Using jQuery with ASP.NET
jQuery is a compact and powerful JavaScript library that is quickly becoming one of the most popular client-side libraries. jQuery’s appeal lies in its compact implementation and flexible and elegant use of selectors to pick up document elements that can then be manipulated using jQuery’s flexible set of useful functions in a browser-independent way. From AJAX functionality, to easy browser independent DOM manipulation, to simple effects, this compact library provides many ways to make client scripting much easier. Additionally, a vast community of add-in authors have added hundreds of extremely useful and easy to use plug-ins that provide many useful features to common client-side tasks. In this session, I’ll demonstrate a host of features of jQuery as well as demonstrate how you can integrate this powerful client-side library with ASP.NET on the server. We’ll look at how to provide JSON services through ASP.NET in a couple of ways as well as looking into ways that you can integrate existing components and plug-ins as ASP.NET server controls with a little bit of work to provide ASP.NET-style interaction with jQuery.
When and Where
Microsoft Portland Office located in Lincoln Tower
10260 SW Greenburg Road
Suite 600
Portland, OR 97223
Meeting Date: Tuesday, August 5th, 2008
Meeting Time: 6pm
If you haven't come out to PADNUG before, this is a good time as any to do it. User Groups are a great place to see a presentation on a topic you're interested without having to go off to a conference and hey it's free. It's also a good way to get involved in the local developer community and network with other developers, shoot the shit or just plain see and hear what others are doing. There's also some time after the presentation down at a local pub to grab some beer and ask questions and harass of the speaker <s>. Or something like that...
If you're in the Portland Area or even a little further out, stop by and enjoy a really fun presentation (for me at least).
See 'ya there...
July 31, 2008 @ 10:28 pm
- from Hood River, Oregon
It's been a long summer of fun out of the country for me. Some time off, some time away from the 'usual' and a time to go a bit lighter on work and time in front of the computer - just what the doctor ordered. Now it's back to reality and the catching up which invariably is left behind by 3 months of physical absence and putting off some work related stuff.
The time off's been well spent. As I mentioned in a previous post about development cycles, I'd reached my burnout phase
and it'd had been nagging at me pretty hard since the beginning of the year. So this year I decided it's time to get away from the rat race for a while to refresh the spirit, enthusiasm and creativity a bit by getting away and going back to the motherland.
As a result I spent the summer in Europe traveling to Switzerland to visit my girlfriend Nadia. We spent a good time of these 3 months routing through central Europe, visiting my parents in Berlin, hanging out in a German Western Ranch (imagine that <g>) for a while and criss crossing Switzerland and Italy stomping through mountains, ramparts and castles and 'schlemming' our way through it all. Oh yeah, and finding some really shitty windsurfing <g>...
I just put up a ton of photos on my photo site (more on Nadia's photo album), in case you want to take a peek, although the pictures haven't been all sorted and captioned yet. Lots of fun stuff looking back and that's not the half of it <g>.
To be fair to Nadia though - this wasn't an all entertainment vacation either - I had a 4 week contract assignment in Geneva spread out over the beginning and end of the trip, which actually turned out to be a really fun, if intense and draining experience, working with a really fun bunch of folks. A conversion to Web project which required some really fun and dynamic AJAX functionality that turned out to be a very close match to the kind of stuff I've been building for the last half a year, so this assignment was a wonderful match/experience in applying some new tools and technologies in a real world environment with very rapid and efficient results - in short the kind of project one can only hope for.
But now - well now it's back to the grind. I'm just digging myself out of a mountain of mail and 3 months of back mail is REALLY a mountain. People are calling non-stop since I've been back yesterday throwing a backlog of requests my way which is strangely comforting, knowing that I can get away for a bit and not lose those trusty folks and that those same folks were kind enough not to bother me to much while I was on sabbatical. How lucky is that?
Now the real question is - where to start? There are so many things to do, so many things to catch up with and it's hard to decide where to start. But the good news is that I am full of energy now - re-charged and ready to charge which is quite the change from when I left. The trick now will be to harness that enthusiasm properly and not let it diffuse on too much diversity of the things that are on my list. Wish me luck - because it's so damn easy to get distracted.
Like writing this entry or putting photos on the site. <g> Back to work already...
July 22, 2008 @ 1:05 pm
- from Geneva, Switzerland
By default if you open a new window in Tab based browsers like FireFox 3 or IE 7 new windows pop up in new tabs. In most situations this is the desirable behavior, but sometimes it's in fact required to get a new window to pop up on the desktop and get it activated immediately.
I ran into this situation today in a complex Intranet application that basically allows editing of several sets of related data simultaneously to content in the 'main' window. While I fully agree with the the school of thought that believes too many windows in Web UIs are evil, in this situation I really couldn't see good alternatives. Alternatives are opening in Tabs (not acceptable if more than one window gets opened at a time) or using DOM internal pop up windows (ala HoverPanel). The latter also isn't really acceptable as the pop up forms are rather complex and page management would get increasingly complex to manage in a single page especially since often times the SAME forms are popped up with different data.
So in the end I decided separate windows are probably the best choice. It's been a while since I had last thought about this so I figured a quick window.open() would be all that's needed but as I found out, that only opens new tabs. How do you 'force' windows to open as windows rather than in a new tab?
As it turns out it's quite simple to do - you can specify a few parameters in the window options to effectively force the window to pop up as a window instead of loading into a tab:
var childWindows = [];
function showApplet(appletName,id)
{ var win = window.open( String.format("applet.aspx?applet={0}&id={1}",appletName,id),
"_blank", "resizable=yes,scrollbars=yes,width=800,height=600,status=yes" );
win.focus(); // force active
childWindows.push(win); // save for later release
}
Any time window.open() specifies parameters that are explicitly attributable to a 'window' - like resizable, scrollbars or height or width - the window does in fact get popped up as a window. Omit any of these window specific parameters and the window loads using the browser's default settings. Note the win.focus() which is useful in forcing the new window to be activated. This is especially useful if you create only one new window and repeatedly load content into it as these windows generally do not come to the top of the desktop window stack.
Speaking of default settings, window loading can also be controlled via the browser settings. In FireFox you can specify whether to open windows in tabs or in a new window:
Other browsers have similar options.
However, the above code snippet allows overriding these settings so there really should never be a reason to choose the Open in New Window option.
Cleaning up Opened Windows
Note that when building your own applications and forcing windows to pop up it's often also important to remove windows again when you're done with your page, or after an operation has completed. In my app scenario for example I had to close a window after some action occurred in the child window or a final Save operation occurs in the main window for example.
So it's useful to keep track of windows opened in a page and then maybe even automatically close the child windows when the page is navigated off of. You'll notice in the code above that windows are stored in a childWindows array, which is done for precisely this reason. To close out all windows the following code is used:
function unloadChildWindows()
{ for (var x=childWindows.length-1; x > -1; x--)
{ var win = childWindows.pop();
win.close();
win = null;
}
}
$(document).ready(function(){ $(window).unload( unloadChildWindows );
});
which runs through each of the child windows and closes them. If you want to automatically close all windows when the page is closed or navigated off of you also hook the window.unload event and fire this function - here using jQuery to fire the event and route it to the function.
All that said, it shouldn't be often that you need to pop up windows on a Web page. It's a bad Web UI practice in general, but in those occasions that you must it's usually a requirement that can't be worked around. Hopefully this will be useful to you in those rare circumstances.
July 21, 2008 @ 1:56 am
- from Geneva, Switzerland
If you've been visiting here for a while, you may have noticed that the advertising on this Web log has been cut down quite a bit recently. At the beginning of the month I decided to switch off from the mish mash of advertising that I was running previously, which included Google Adsense, Steve Smith's Lake Quincy Network, James Avery's The Lounge Network network and some of my own ads. Advertising here has brought in a small amount of side income that has roughly covered the hosting of the site and a little bit extra. While it's not been a lot of money it's always been enough to make me teeter on the side of the $ signs to put the ads onto the site, but it required the mish mash to provide even this small stream of revenue . At the same time the amount taken in seemed small in comparison to the amount of content that is actually provided here - it's hard to gauge the worth of ad space of course, but I was actually considering turning off advertising altogether at some point because of the whole disproportion between ad payout and what the Ad services charge advertisers and what gets paid back to publishers (especially Google and Adsense which is an abomination - Google is getting rich of both advertisers and publishers because of it).
So a couple of months ago I started talking with Steve Smith about advertising and some of the pain points for publishers and advertisers alike and started throwing some ideas around about a few different approaches to serving ads. Steve's really the guy with the ideas, but there was a bit of back and forth and I liked what I was hearing. I've been frustrated with the advertising publishing I've been doing for a variety of reasons and was in fact thinking of just dropping the whole thing. However after talking with Steve a bit I decided to see what he'd come up with.
The end result is what now has become DevMavens which is a highly focused and somewhat exclusive advertising arrangement that serves ads to only a few member sites that are part of the DevMavens network and includes only a small number of advertisers at any given month. The concept of this type of specialized network isn't new (in fact, The Lounge used a similar model), but what is different for me at least is that the payback for the publishers is a bit larger than the typical CPM network. For me, the payment arrangements result in a significantly higher payback for running ads and as a bonus it requires only a single, relatively small ad on each page of the Web log.
I hope you'll agree that this single ad format is much less distracting than the 6 or so ads that were running previously on all the pages here, so hopefully this won't be just a boon for me but also for those of you who kindly come to visit this Web log frequently and actually click through into the site either from the RSS feed or from search results. I'm also glad to be working with Steve, who's been great to work with in the past for ad tuning and pinging on advertising issues in general, as well as just being a good developer friend I talk to from time to time about dev issues anyway.
Love Hate Relationship with Ads
Advertising for me is a love/hate issue and it's ironic because I tend to think of advertising as the root of a lot of problems on the Web. So in a way it's a bit hypocritical to whore space on this site to advertising. But yet the lure of some recurring side income is always very enticing. The reality is that a lot of effort has gone into the nearly 1000 posts that have made it into the Web log and there's a certain amount of profit potential there even as the content keeps growing. It seems a waste to throw that away and the income is maybe an added incentive to keep at it, especially on those stretches when writer's block or very busy schedules seem to get in the way.
There's no telling whether the DevMavens campaign will continue to pan out, but for the moment this definitely is a big improvement on all fronts as it simplifies things, makes for a cleaner layout and doesn't hurt the bottom line. Woot.
July 19, 2008 @ 2:07 pm
- from Murten, Switzerland
It's time to retire my existing Web Server which is going on 7 years now. The box that this site is running on currently is an old style 2ghz Pentium 4 box with 1 gig of memory and it's starting to creak at the edges pretty hard with a few off and on failures to power and the system board components (lthe clock seized to work a few weeks ago). As of late the box has been locking up on a few occasions - and in fact just as I started writing this post today it just locked up again. Thankfully the lockups are not hardware fatal and the machine keeps on ticking after being rebooted by the ISP staff, but clearly this box is reaching the end of its hardware life cycle and usefulness.
I've been meaning to upgrade the box for a while, but frankly there's really not been a compelling reason to do so until recently. Even with the fairly large and various amount of stuff running on this server including the WebLog, our message board, a large variety of sample applications, my West Wind Web Store (in various different versions actually <s>) and a bunch of smaller internal applications, it never seizes to amaze me that this underpowered box does as well as it does. The other reason I'd been holding out as long as I had has been waiting for Server 2008. I wanted to wait for the new OS to update to save myself from an update in the future and having more synchronicity between Vista on the desktop and the 2008 server on live site.
Anyway, it's time to get a new box and I'm kind of torn between the choices on what to get. The last box was a home built box that cost me somewhere around $500 to build at the time. It was an emergency replacement box swapped in for a slowly failing server at the time and it just somehow worked out as the permanent replacement. I'd say for the years of services it's done that was a good price to value effort. The old box was basically a desktop box that my ISP at Gorge Net in Hood River has been kind enough to co-locate for me.
The question now is though what to get for a new box. I've been leaning towards just building another box with an Intel Core Quad processor and a gobs of memory, plus some high performance drives for best performance, but things have changed with processor designs significantly over the last few years with chips running super hot. So I'm seriously wondering whether a desktop processor is a good idea for an always-on server environment. Additionally I'm really pressed for time as I have a two week window to get the components, build the box, run a burn in and then move everything from the old server onto the new box while I'm actually in Hood River with physical access to the box. Finally, I'm not all that keen on building my own boxes anymore especially in regards to getting those monster processors installed and properly set up for cooling and optimal tuning. My last home brew installation of an internal office server took me way longer than I care to admit to get the machine into stable operation and while that kind of experimentation was fine for a desktop box I can fuck with anytime, I don't have that privilige with a server at a (somewhat) remote location.
So, I guess I'm asking for a some experienced advice amongst you, my dear readers.
Some of the choices I've been toying with are:
Build my own
If I build my own box the price is probably the lowest and I get exactly what I want, but as I mentioned above I am a little worried about component overheating and life time. Pricing for what I'm looking at looks to be around $600 or so for a Intel Core Quad with 4gig and two SATA drives plus a new box and a beefy power supply. The price is right but I'm not sure if this is still a good idea.
Another option is to buy a bare bones, pre-installed system that has Box and CPU mounted and ready to go. But in briefly looking around I didn't find a lot of choice in this space from most of the component vendors, so this is probably not an option.
Get a Desktop Box from Dell or some other Vendor
Pricing a box that has similar components (minus extra drive and maybe additional memory) from Dell costs roughly the same, but there's less control over the components. It's amazing how cheap boxes have become. Effectively the pricing doesn't seem too much more than home build but I'd probably have to do some component swapping to get the high end drive and memory upgrades still. The advantage for me though is that I don't have to wait for components to show up and build the box and hopefully get a reasonably tuned box that I can just install the OS on and be done with.
Here's with what I came up with at Dell:

Dell Inspiron 530
Intel Core 2 Quad Processor Q6600 (8MB L2 cache,2.4GHz,1066FSB), Genuine Windows Vista® Home Premium Service Pack 1
Unit Price
$728.00
Genuine Windows Vista® Home Premium Service Pack 1 (will be dumped)
Memory
4GB Dual Channel DDR2 SDRAM at 800MHz- 4DIMMs
Keyboard and Mouse Bundles
Dell USB Keyboard and Dell Optical USB Mouse
Monitor
No Monitor
Video Cards
ATI Radeon HD3650 256MB
Hard Drives
500GB Serial ATA Hard Drive (7200RPM) w/DataBurst Cacheâ„¢
Floppy Drive and Media Reader
3.5in Floppy Drive
Mouse
Mouse included with Keyboard purchase
Network Interface
Integrated 10/100 Ethernet
Optical Drive
16X DVD+/-RW Drive
Sound Cards
Integrated 7.1 Channel Audio
Warranty & Service
1Yr In-Home Service, Parts + Labor, 24x7 Phone Support
Sub total:$728.00
Looking over prices what it would cost me to build a box with similar components it's not going to be any cheaper, even if I end up replacing the drive with something faster later on. <shrug>
The question again is just how much can you trust a desktop box (especially from Dell and Inspiron) to last in a server environment today? I've had good luck with non-server boxes in the past with all of them lasting close to 5 years before they got obsolete.
Get a Server Box from Dell or some other Vendor
While at Dell I also checked out the servers and prices there also seem reasonably decent for a mid range non-rack server. For a quad core 2.5ghz Xeon with a similar configuration (plus 2 drives unfortunately) I'd end up with something like this:


PowerEdge T300
Quad Core Intel® Xeon® X3323, 2.5GHz, 2x3M Cache, 1333MHz FSB, No Operating System
$1,666.00 Save $688 on select PowerEdgeâ„¢ T300 servers through Dell Small Business.
Memory
4GB DDR2, 667MHz, 2x2GB Dual Ranked DIMMs
Primary Hard Drive
250GB 7.2k RPM Serial ATA 3Gbps 3.5-in Cabled Hard Drive
2nd Hard Drive
250GB 7.2k RPM Serial ATA 3Gbps 3.5-in Cabled Hard Drive
Hard Drive Configuration
Onboard SATA, 1-4 Drives connected to Onboard SATA Controller - No RAID
Floppy Drive
No Floppy Drive
Network Adapter
On-Board Dual Gigabit Network Adapter
CD/DVD Drive
16x DVD-ROM Drive, Internal, SATA
System Documentation
Electronic Documentation and OpenManage CD Kit
Chassis Configuration
Chassis with Cabled Hard Drive and Non-Redundant Power Supply
Hardware Support Services
3Yr Basic Hardware Warranty Repair: 5x10 HW-Only, 5x10 NBD Onsite
Power Cords
Power Cord, NEMA 5-15P to C13, wall plug, 10 feet
TOTAL:$978.00
which seems reasonable for a server. Components as a whole seem a little less powered than the desktop counterparts, but I suspect the Xeon processors are better optimized for server operation and heat dispersion for running in always on scenarios.
Unfortunately in various configurations I haven't been able to get exactly the config I'd like to see so I probably have to call Dell to fine tune a little bit if possible or at least ditch the third drive for a 10k boot drive.
Looking for Feedback
The environment will be almost purely for hosting my own Web applications, mostly ASP.NET applications running SQL Server as a data backend all local in this case plus a few older COM based Web applications. There are obviously more expensive solutions available especially in rack mount form but I'm incined to think that most of the server boxes are just badly overpriced for what performance they provide. Am I totally off my nut on that or does that seem reasonable at all?
I thought I'd take advantage of the vast pool of folks out there and I would love to hear some thoughts on what's worked for you in small shop server installs focused purely on Web Servicing for small to medium level Web load.
July 15, 2008 @ 12:46 pm
- from Garda See, Italy
I've finally had some time to update the West Wind Ajax Toolkit to version 1.90. A number of these changes I've written about and have updated previously, but this update finalizes a number of these updates into a full release. There are a number of enhancements to the existing functionality as well as a few new helper components that I'm finding very useful these days.
As always you can grab the latest version from:
West Wind Ajax Toolkit page
The toolkit consists of a number of controls to provide JSON callbacks and JSON service, popup hover windows, modal dialogs and a few other Ajax based controls. In addition the Toolkit includes a host of utility classes that provide services to ASP.NET AJAX pages including a generic JSON service, a Json Serializer, a ClientScriptProxy that can automatically route to either ClientScript or the ASP.NET ScriptManager, a ScriptVariables class that lets you embed data into client pages easily for client code access, a ScriptContainer for adding scripts to a page consistently in markup and through code, a script minifier and a JavaScript GZIP compression tool and much more.
Following are a few of the highlights of the release's new and changed components.
Improved JsonSerializer
Thanks to the help from Joe McLain there have been a number of improvements in the serialization code of the parser. It now deals much better with array/list de-serialization which previously was a bit shaky. Arrays/Lists are now supported as root objects or as embedded properties in objects. The latter worked before but there were problems with root arrays. A number of string formatting issues in deserialization are also fixed and the date parsing logic can now deal with ASP.NET AJAX/WCF style JSON dates, as well as the proposed ISO date literal and plain old new Date() syntax. Internally the serializer uses the ASP.NET AJAX date format now, but there's a switch which allows generating the new Date() 'code' syntax instead so you can use the serializer for creating fully runnable JavaScript.
ScriptVariables class
I talked about this class some time ago - it provides the ability to easily embed server variables into client side code by way of a client side object that contains these variables. So you get ServerVars.MyVariable with a server set value applied. Values can be applied literally via Add methods or be applied dynamically which reads values out of any object reference or control property.
Values are properly JSON encoded and can then easily be accessed on the client. The object also supports the ability to read these object values - optionally - back on the server side in a Postback if they are changed on the client. This is a great way to persist values without explicitly having to assign them to hidden variables or a field on the form.
You can create multiple server script variable objects as long as they each output a unique name (with the default being ServerVars) and control builders can also use this tool from within control code without interference with application level code by using a unique name.
Optionally this control can also optionally generate properties for each of the controls on a form and provide non-NamingContainer Ids. So even if txtName is inside of a user control you can access ServerVars.txtNameId to get the control name.
I've found this component to be extremely useful as it provides an easy way to embed:
- Variable names that are mangled by ASP.NET naming containers - no more <%= txtName.ClientID %>
either explicitly added or automatically generated through AddClientIds() - Pushing down initially loaded data for JavaScript to consume (including complex types)
I've found this class extremely useful in Ajax applications, especially for pushing down sensible control names and avoiding the <%= ClientID %>mess on the client.
ScriptContainer Class
I've mentioned this class in several posts - it's essentially a script manager that's somewhat similar to the ASP.NET ScriptManager, except it doesn't rely on .NET 3.5 and doesn't output MS Ajax scritps by default. It also provides a bit more control with the ability to determine where scripts are rendered: In the header, after the form tag (standard ASP.NET mode) or inline where the control sits. This setting can be applied either to the control in general or to the individual scripts added, which allows you a little more control over how scripts get loaded.
Scripts can be added either in markup or programmatically and there's only a single instance on a page that's allowed so all scripts can be added to the same manager. This means controls, page code and markup can ensure that scripts only get added once. The syntax uses HTML generic controls using <script> tags so there's Intellisense support for the embedded scripts.
Additionally you can specify whether you want to allow min script to be accessed (ie. .min.js files that are compressed or minimized). If you do and the file exists the control will use the minimized files when running the site in non-debug mode.
JavaScriptMinifier.MinfyDirectory Method
This new method can be used to minify all java script files in a directory or directory hierarchy in batch to create min script files. You specify the path the min extension and whether to recurse into child paths and this method will create the min files automatically checking for changes as needed. This works great in combination with the ScriptContainer class by sticking a call to this static function into Application_Start which will check for script changes on startup of the App.
A few JavaScript Helper classes for Formatting
This update also includes a number of simple helper functions for string, number and date formatting. String gets a basic but highly useful .format() function that uses simple C# string replacement (but without format options). Number gets a .numberFormat() function that does decimal and currency style formatting (c,n,f3,n2) and Date gets formatDate that provides basic formatting using the .NET date format specifiers ( ie. MM dd yyyy, HH mmy). This is nothing new, but it's just so damn useful and necessary that it has to be in the utility library.
Additional changes
There are many more small changes and updates especially in the JavaScript library that fix a number of small issues that have been reported and you can find out what's updated here:
What's new in West Wind Ajax Toolkit 1.90
Interested in Feedback
As always I'm interested in feedback on the components and how you might be using them. There's a lot of functionality in this toolkit, yet I suspect a lot of people are taking components out of it and using individual pieces in their own libraries or applications, which is great.
I'm especially interested though in feedback about the new ScriptContainer and ScriptVariables classes as I think they are great generic tools that could be expanded upon a bit more. Feedback can drive this better so if you have any comments post them here or on the support forum
What's Next?
A couple of things really should happen next. First of all the samples of the toolkit are getting reaaaallly long in the tooth. While they work and get the point across, most look crappy and use some rather lame examples. So the samples will be updated at some point shortly.
For the next major version there will be a complete reworking of the client library to use jQuery. There's a ton of overlap between wwScriptLibrary.js and what jQuery provides and using jQuery to provide the base and core library would reduce the code in wwScriptLibrary by more than half. jQuery doesn't provide all the utility functionality provided in wwScriptLibrary but there's definitely some overlap. I think most of the higher level components - especially the client side control interfaces - can easily be maintained backwards compatible or with only minor changes even given this major of an update. This will be a major change but it's looking good for keeping the interface intact and not breaking old code. Once this is done it'll be much easier to start integrating more jQyery based components in a similar fashion as the wwDatePicker.
Anyway, if you have any thoughts on any of this please leave a note here or on the message board.
July 11, 2008 @ 12:19 am
- from Garda See, Italy
When building generic ASP.NET Server controls that also provide a sort of API service to other custom controls or page level code, it's often necessary to ensure that only a single instance of a control exists, and that only that single instance of this control or component can be accessed in the context of an ASP.NET request. While this is usually easy to accomplish in actual page code (since you can reference the control or component explicitly), if the API is also consumed by other Server Controls it's crucial that it's easy and efficient to find and access that single instance of a control effectively. This isn't a typical scenario, but it's a common pattern for controls that expose API level functionality in addition to the actual control based markup.
This isn't a new concept (Simone had a great post on this some time ago), but I've run into this pattern so many times and have also gotten a number of questions about this that I thought I'd revisit it here since I just went through the excercise again.
For example, a few days ago I talked about a ScriptContainer control that's akin to the ASP.NET ScriptManager control, but with a few additional features. The idea is that there's a single level API that can be used to retrieve an instance to the ScriptContainer, so that code either in CodeBehind or a customer Server Control has access to that instance sitting on a page.
So, in this example I can reference the wwScriptControl on the page with code like this:
wwScriptContainer script = wwScriptContainer.Current;
script.AddScript("~/scripts/wwEditable.js", true);
and always be guaranteed that I get that single instance of the control whether it's already embedded in the page markup or if not it's created on the fly and returned to me. Incidentally the ASP.NET ScriptManager also does something similar with its GetCurrent() method, but it only returns an instance if there's already one loaded - it's your responsibility to create one and add it to the page otherwise. The wwScriptContainer.Current property here instead always returns a control instance including creating one and adding it to the page when none exists yet.
Another example, where this applies is my ClientScriptProxy API, which returns a single instance of the ClientScriptProxy object that references either a ScriptManager if one is on the page, or otherwise talks to the Page.ClientScript object and so provides the same behavior regardless of which API is available. In this case too I need to ensure that only a single instance of this object exists and that I can always retrieve that single instance from anywhere in code. The ClientScript.Current property serves exactly this purpose.
ASP.NET Singletons and HttpContext.Current
This Singleton pattern can be applied to any ASP.NET control (or even component or Page) that in addition to a markup based control interface also exposes a generic API that scoped to the current ASP.NET request/page executing. In ASP.NET request level state is managed through the HttpContext object which is yet another example of a Singleton.
The basic concept of a Singleton is very straight forward. Here's Simone's example which is as concise as it gets:
public class SingletonPerRequest
{ public static SingletonPerRequest Current
{ get
{ return (HttpContext.Current.Items["SingletonPerRequest"] ??
(HttpContext.Current.Items["SingletonPerRequest"] =
new SingletonPerRequest())) as SingletonPerRequest;
}
}
}
where the SingletonPerRequest() creates whatever object reference you need to create. The key thing is that the property is static and that it returns a value that is stored on the HttpContext.Items collection.
To understand how this works it's important to understand how the HttpContext object relates to the ASP.NET request flow. HttpContext.Current represents the current ASP.NET Request context and this context is available throughout the course of an ASP.NET request starting with the HttpApplication.BeginRequest pipeline event all the way through HttpApplication.EndRequest(). It also includes module and handler execution which is also part of the application pipeline.

ne important aspect of the HttpContext object is that it also contains an Items collection - an in-process statebag that allows you to attach arbitrary state at any point in processing of a request and access it later. Because HttpContext.Current is static and always accessible any state stored in Items is always available (unless explicitly released) until the end of the request. It's an ideal store for request specific information like for example store an instance reference to a control that should only be accessed through a single reference. IOW, a Singleton.
Long story short for my ScriptContainer example, I can use HttpContext.Current.Items["wwScriptContainer"] to save and instance of the script container control and then implement a custom method or property like .Current to retrieve that single instance.
Here's what this code looks like for ScriptContainer:
public ScriptContainer()
{ this._Scripts = new List<HtmlGenericControl>();
this._InternalScripts = new List<ScriptItem>();
if(HttpContext.Current != null)
{ // *** Save a Per Request instance in Context.Items so we can retrieve it
// generically from code with wwScriptContainer.Current
if (HttpContext.Current.Items.Contains("ScriptContainer")) throw new InvalidOperationException("Only one wwScriptContainer is allowed per page.");
HttpContext.Current.Items["ScriptContainer"] = this;
}
}
public override void Dispose()
{ if (HttpContext.Current != null)
HttpContext.Current.Items.Remove("ScriptContainer"); }
/// <summary>
/// Returns a current instance of this control if an instance
/// is already loaded on the page. Otherwise a new instance is
/// created, added to the Form and returned.
///
/// It's important this function is not called too early in the
/// page cycle - it should not be called before Page.OnInit().
///
/// This property is the preferred way to get a reference to a
/// wwScriptContainer control that is either already on a page
/// or needs to be created. Controls in particular should always
/// use this property.
/// </summary>
public static ScriptContainer Current
{ get
{ // *** We need a context for this to work!
if (HttpContext.Current == null)
return null;
ScriptContainer ctl = null;
if (HttpContext.Current != null)
{ // *** Retrieve the current instance
ctl = HttpContext.Current.Items["ScriptContainer"] as ScriptContainer;
if (ctl != null)
return ctl;
}
ctl = new ScriptContainer();
Page page = HttpContext.Current.Handler as Page;
if (page == null)
throw new InvalidOperationException("ScriptContainer.Current only works with Page based handlers.");
page.Form.Controls.Add(ctl);
return ctl;
}
}
The idea is that during initialization of the control the control/component is immediately pushed into a Context.Items item and saved. This instance becomes that single instance accessed. In fact, the constructor explicitly checks for multiple instances and throws if more than one is instantiated explicitly. This means you couldn't have multiple markup controls on the page or a markup control and manually create an instance.
Once stored the Item stored reference - if one exists - can then be retrieved in the .Current property getter which checks to see if the item exists and returns it.
Typically the .Current getter is also responsible for creating a new instance if one doesn't exist, but how that's accomplished or whether this is supported can vary wildly especially with controls. Here the code creates a generic instance and adds it to the ASP.NET form's Controls collection. Note that from within a static property you won't have access to the Page instance (ie. no this.Page) so you have to access the Page through the HttpContext.Current.Handler and cast that to a Page object.
But again how that works will depend on your specific scenario. The issue is that if you have a control there may be a number of configuration settings that must be set in order to create an instance 'generically'. In the case of the ScriptContainer a generic version is acceptable, but with other controls configuration may be required. You may also need to know whether the control was returned from an existing instance or whether a new instance was created and so you might have an additional property that returns this fact. Or you can return null from .Current and then explicitly let client code create the new instance and configure it.
Typically Singletons have to worry about potential multi-user issues, but in ASP.NET request scenarios using HttpContext.Current this isn't an issue since the instance stored on .Current is visible only to the current request, which is of course running on a single thread. So multi-threading and locking is not something you have to worry about.
This Singleton like approach is a pattern I use a lot for ASP.NET Controls that I create - it's a very useful tool for caching expensive construction and ensuring that you truly are only accessing a single instance of a control or component. But you can also apply this same sort of thing to Page level code or even request level code.
If you want to take a closer look at the ScriptContainer component you can find it as part of the West Wind Ajax Toolkit download.
July 07, 2008 @ 4:05 am
- from Murten, Switzerland
I've been thinking a bit about JavaScript inclusion in the last few weeks as I've been working on quite a few different Web projects that use a fair bit of JavaScript functionality. One thing that I'm still trying to feel good about is how JavaScript is included into pages. There are a number of different ways that script embedding can be done especially with ASP.NET and none of them are really perfect.
So what's the Problem?
The issue is plain and simple, how do you deal with JavaScript inclusion into the page given that you have a fair number of JavaScript files that are bound to change frequently and across multiple projects/applications? My assumption here is that I'm using a few libraries and utility classes both public (jQuery and jQuery plug-ins mainly) and some internally developed support libraries. All in all many pages I deal with have typically 4 - 8 related JavaScript resources in AJAX centric applications with about 15-20 JavaScript resources overall involved in a typical project. In addition there are a few ASP.NET custom controls that depend on some of these Ajax libraries and components.
My project load currently runs about 10 concurrent projects both internal and external and all of them are making use of these same script resources. Keeping versions of both my internal JavaScript libraries as well as external libraries in sync and up to date for each of these applications both in development and online gets to be challenging especially if the files are not necessarily delivered as a unified whole (ie. often I pick and choose which plug-ins to use).
So as you can probably see, it's pretty easy to be in 'script file hell' - I find myself using Beyond Compare quite frequently to compare what's changed across different projects and update scripts precariously. I don't think there are easy solutions to this problem other than being very organized and managing versions as anally as possible but doing so can be a real drag on productivity as I move from project to project wondering if I have the correct current version.
There are many different ways that JavaScript resources can be loaded into pages from the basic manually managed header embedding of each script to using full ASP.NET resource management of all script files used. And plenty of other options in between. Try as I might I have not found a solution that really makes me feel all warm and fuzzy, so I'm curious what you are doing and what works best for you.
But let's review some of the approaches that are available to script embedding. For a specific example, let's take the more generic case of the jQuery library. Here are some of the options you have to get jQuery into your page:
- Include in a scripts folder and link via script include as jQuery.js
- Include in a scripts folder and link via script include as jQuery-1.2.6.js (ie. version specific)
- Link to the version specific file at jQuery.com (typically jQuery-1.2.6.js)
- Link to the latest version at jQuery.com (ie. jQuery-latest.js)
- Link to some other script hosting site (ie. Google's Ajax Lib API) via script loading
- Embed scripts into ASP.NET Resources
- Use ScriptManager to load scripts either from Folder or Resources
- Use a custom script manager
All of these are options but they all have a few shortcomings as well.
Local Script Files
The most common and easiest to understand approach is to simply embed local script links into the page. Add scripts into the header with <script src="scripts/jquery.js"> and off you go. In many situations this approach is perfectly fine, especially if script files are static and don't change frequently.
<head runat="server">
<title>Configuration Settings</title>
<link href="Standard.css" rel="stylesheet" type="text/css" />
<script src="scripts/jquery.js" type="text/javascript"></script>
</head>
The issue with locally stored script files is that they they need to be updated if files changed. In a single application this is not a big issue. But if you're managing a large number of projects, changes to resusable scripts both of your or third party script libraries can easily become a burden and an exercise in version management. It'd be great if source control would solve this problem, but because resources are often mixed and matched storing a fixed set of script files under source control also doesn't really address this scenario either and wouldn't work very well anyway if the project is already under Source Control.
In addition, if you start using script code as part of related ASP.NET controls that might build on top of client script components and that code has some dependencies on specific versions you can run into subtle versioning problems that are difficult to detect and test for. But even for relatively simple things like the static core jQuery library this can get sticky as new versions are frequently distributed.
Versioning JavaScript files in general is a sticky issue. If you have local files, how do you version these JS resource? For example, many jQuery examples are shown using the version specific file like so:
<script src="~/scripts/jquery-1.2.6.js" type="text/javascript"></script>
This addresses versioning alright but then gets tedious when a new version gets released and all pages referencing this version needs to be updated. Again, with a single file this isn't terrible but now think about 10-20 resources used in an application that are all versioned this not really a good option. The other choice is to use a non-versioned file and just update the files - this is easier for updates but in turn can result in subtle feature incompatibilities if newer esions break old builds/dependencies.
I personally lean towards the latter approach of using non-versioned filenames and take my chances hoping that changes are backwards compatible. In my own libraries even a breaking change is easily caught because I know what was changed intimately, but with third party libraries this can be tricky. For example, while using the jQuery.ui beta versions there many subtle breaking changes that were difficult to catch once the code was migrated to the release version because 95% of the code worked great. Testing JavaScript code consistently - especially UI code - is difficult at best and it's easy to have a subtle bug to not show up until the app is in production for a while.
One Library Path for All Scripts
Another option for 'local' script files might be to store all scripts in one place - say off the root of the Web site similar to the way ASP.NET 1.1 used to store resources in a /WebClient folder - and access them through this single script location. This solves the problem of having to copy versioned files around for each application and having a single store to update third party libraries and modify internal libraries. If there's a single store it's also easier to put the files under source control and manage them effectively.
<script src="/scripts/jquery.js" type="text/javascript"></script>
But this approach also has a few problems. First it's not a generic solution because it requires that you use the same convention across applications. Might work fine for your own applications where you set policy, but if you're working to somebody else specs it may not be possible to dump scripts into a specific consistent location.
It also may not necessarily be Ok to update scripts for every application at once, especially if scripts have breaking changes that might not work with older code not adapted to it. If you work with script files that are dependent on one another and these files are updated out of band it's easy to run into version problems. This is always a concern and not having local copies or linking to a single non-versioned script file is prone to potential version issues. Having local files in each app allows isolation from this version issue that can crop up with a single shared library folder. This issue is probably not a major concern
I played around with this approach some time ago, but tossed the idea because of issues
External Hosting
Another centralized approach is to use external hosting for certain script resources. This is more applicable for public librarires like jQuery, Prototype etc. and some high profile plug-ins available for them. External hosting also offloads the bandwidth for downloading scripts from your site to whatever public site hosting the scripts. Additionally, chances are that these script resources may be already cached in user browsers as the public resources are more widely used by users in general browsing which provides additional performance gains.
For example, you can load the latest jQuery version from:
<script src="http://www.jquery.com/src/jquery-latest.js" type="text/javascript"></script>
or a version specific one from the
Google jQuery code repository:
<script src="http://jqueryjs.googlecode.com/files/jquery-1.2.6.js" type="text/javascript"></script>
Most other major libraries also provide similar direct links to libraries so that you can offload loading from your site.
Google recently also started hosting various JavaScript libraries with the Google's Ajax Lib API. This API allows you to load script via JavaScript code in the page and allows you to specify specific version numbers as part of the API call. This is a clever way to get a better handle on versioning as you can potentially use single script file to in your app to load various script files with 'factory' function calls that load each library with the appropriate version. FWIW, it doesn't require Google's API to do this; you can take a similar approach by yourself by creating a small loader script that can be updated for each application (see below on how this can be done with jQuery for example).
The disadvantage for external hosting is of course that you are dependent on an external provider to be up and running 100% of the time and to have adequate bandwidth to serve the script files.The provider can decide any time to remove files or APIs so at that point you may end up with bad links in your code. This is where an API - whether your own or Google's can actually buy some advantage because it can be changed in one place.
Another concern is that scripts loaded across sites can also trigger warnings in the browser in some situations like SSL connections (to non SSL script resources) or strict browser requirements for cross domain loading of any files.
Dynamic Loading
As mentioned above you can also load script dynamically from other script code. For example, it might be useful to load one script from another script to provide dependency loading. In jQuery for example you can do:
/// <reference path="jquery.js" />
$.getScript("scripts/basepage.js");
to pull in additional script code.
One potential advantage of this approach is that you can build a simple loading api that turns scripts into resources with maybe some constants that determine which scripts to load and from where, consistently across an application. Rather than linking all scripts in pages you can call a single script file that handles loading all other scripts.
However, when loading script dynamically it's important to be very careful and ensure that the code in the dependency is not accessed before the script has actually loaded. This can have tricky side effects. In one app I was working on just today pages would call into a loaded script function and it would work just fine the first time the page loaded or when a full refresh occurred, but not when the page was just reloaded via click (ie. most resources are already cached). The difference is that the other scripts are fully cached and so load time is significantly faster and the code executes before the second script page loads. In this case IE fails to see the function on page startup (even in $(document).ready() in jQuery) while FireFox, Opera and Safari properly delay the loading code.
Most JavaScript libraries allow working around this via events fired when the script loading completes. In jQuery there's a handler that can be passed as a second parameter that's a handler:
$.getScript("scripts/wwJquery.js",function() { showStatus("Ready");});
which will reliably cause dependent code to run. But it's not always feasible to tie code to a specific library having been loaded. Often you have to wait on multiple libraries. Oddly I've seen a host of problems with this approach both with jQuery, the Google API while script loading from the page's <script> tags seems to work just fine. Detecting script completion across browsers is tricky especially in IE it appears.
I have this approach on my list of things to play around with someday, but given the mixed results I've seen with load order failures I'm incline to pass this option up completely.
ASP.NET Resources
When using ASP.NET it's also possible to rein in JavaScript resources by building them into ASP.NET compiled assemblies as Resources. Using this approach allows the resources to be stored in a single location that is centrally managed. Web Applications can then access these resources by calling Page.ClientScript.RegisterClientScriptInclude(), Page.ClientScript.RegisterClientScriptResource() or the methods of the same name on the ASP.NET ScriptManager control.
When using resources in a separate non-Web project, the project can be added other solutions and so can be shared across multiple projects which can both read the resources and modify any of them and have the changes reflected in any other application that includes the resource (or more often control ) assembly.
I use ASP.NET resources a lot for a variety of purposes from images to scripts to css files and it's a great way to put everything into one place. But resources are not without their problems either. The worst issue by far is that they produce hideously long URLs that are non-transparent in the page and you can't easily tell what each script file points at. They also take up a fair bit of space (a typical WebResource.axd url can be 150 chars long). Additionally the API to actually load these script files is a code only API that needs to be handled in Codebehind of the page with no HTML page markup equivalent.
Resources are a must if you're building controls that have script dependencies in my opinion and that's how I've been using them. I use my own script library that has both client and server components and the server controls need to ensure that scripts are loaded appropriately into the page. Without resources these controls would have to explicitly require the developer to add scripts to the page which would be a bit hokey. So the control internally loads the appropriate resources itself and in the right order and so ensures that the component is self contained.
On the flip side using resources this way can be a problem if you also use the same library for other coding. The smaller problem is that it's very easy to double load resources when you forget that a component loads the resources internally. Further it's possible that the component loads the resources too late or in the wrong order from what the same page code might require if there are dependencies.
In worse cases there may be issues where there are load order problems where the page control order rather than manual ordering end up determining the order in which scripts load. Again, this can become tricky if you have libraries that depend on each other like say a jQuery and a jQuery plug-in, or my personal library that might depend also on jQuery in certain parts.
Resources also don't work well with Intellisense, except when using them with the ScriptManager control (see below). Otherwise the resource URLs aren't transparent so there's no easy way to embed scripts into other JavaScript resources as /// <script reference="" />.
The big problem is that ASP.NET natively doesn't have a native script loading API that works both for markup and code based script injection. Because the ClientScriptManager is a code only component it's very difficult to coordinate script loading both through markup and CodeBehind or component code and it can get very tricky to coordinate script dependencies loaded from resources if those same scripts also need to be loaded for other code.
ASP.NET Script Manager
Ah, but what about the new ScriptManager control that's part of Microsoft ASP.NET AJAX? It addresses a number of the issues that I've mentioned above. Indeed, ScriptManager provides a mirror interface for most of the ClientScript functions so you get the same functionality as the ClientScript for code manipulation and you get the actual control which can be stuck into a page and provide markup based script injection into the page. You can embed both physical url based scripts links as well as resource links so it's possible to consistently embed scripts from both. The control also detects script duplication and only embeds only a single script reference into the page so it's easier for Markup and Code injected scripts to not generate multiple script references. It's possible to add a script like jQuery into the page and ensure that it doesn't get embedded twice for example.
But ScriptManager is also very problematic. First it's a .NET 3.5 component so it's really and add-on and not universally available. There's also no easy way to detect whether ScriptManager is available in a page without using the ScriptManager in the first place (it's possible but it's a royal pain). ScriptManager also requires a control on the page - it's not a standalone API which complicates using it in generic solutions especially since only one ScriptManager is allowed per page. Finally last but not least the control arrogantly throws the Microsoft ASP.NET AJAX ClientLibraries into the page (at least 28k of compressed script for minmal support) with no option to turn that off. If you're not using ASP.NET AJAX like myself the last thing I want is to get a bunch of scripts I'm not using thrown into my page.
Custom Script Manager
My first thought when seeing ScriptManager's issue of AJAX script loading was to subclass the control. It's easy to turn off the AJAX script injection, but unfortunately ScriptManager can't be subclassed in such a way that it still provides Intellisense because apparently Visual Studio's Intellisense for script Intellisense is hard coded to look for ScriptManager's specific properties. Script Intellisense is quite useful so ontop of the other shortcomings of SM I decided to just build my own.
The control I ended up with is similar in concept, but with better focus on externally hosted scripts.
<ww:wwScriptContainer ID="scripts" runat="server" RenderMode="Header">
<Scripts>
<Script Src="Scripts/jquery.js" Resource="jquery"></Script>
<Script Src="Scripts/ui.core.js" AllowMinScript="true"></Script>
<Script Src="Scripts/ui.draggable.js" ></Script>
<Script Src="Scripts/wwscriptlibrary.js" Resource="wwscriptlibrary"></Script>
<Script Resource="ControlResources.Menu.js" ResourceControl="txtInput" />
</Scripts>
</ww:wwScriptContainer>
The control can either be embedded into the page or there's a .Current property that can be used from code that either finds an existing instance or creates a new control and adds it to the page dynamically which is useful for controls that want to embed resources. This gives a single API both for markup and code based embedding.
While working on this there were are a few other things that came up as useful: Optional automatic usage of minimized scripts (.min.js) extensions, abillity to embed all scripts into the page header (or inline, or as scripts) rather than adding into the page, the ability to order scripts by inserting at the end or beginning, as well as determining on a per script basis where it renders (Header, Script, Inline or InheritedFromControl) all of which gives some additional control both for markup and code behind. You can also embed resources but use reference a script on disk (for example in a central Web location) in order to still get Intellisense. The latter isn't as nice as ScriptManager's ability to just work off resource Urls, but at least it allows for some Intellisense of resource based scripts.
The format of the control is a bit ugly - I had to use the <scripts> and <script> tags in order to get Intellisense in pages to work because this is the only way I could figure out how to get Intellisense to work. Using custom prefixes (required for new controls) immediately breaks VS JavaScript Intellisense. The result is that I had to use HtmlControls for the script which means there's no Intellisense. Bummer, but the syntax is easy enough to look up.
This is a relatively new control for me and so far I've only used it with one project, but so far this looks promising. But of course this too is a non-standard way of working - it requires using this control instead of script embedding into headers, but at least the syntax of this control is pretty much based on standard Script tags. In fact, for basic scripts the syntax is identical (ie. like the draggable.js) above. If this sounds interesting to you let me know in comments and I can post more info on this control.
But this control is also not a generic solution. It works well for my internal purposes as well as for a few customers I'm working with but it's not totally satisfying either because of the inconsistencies in Visual Studio. And forcing developers to use a custom control is not my idea of a good design either, but for me at least this control addresses a few key scenarios like script compression and programmatic control over scripts and resources.
No Clear Winning Solution
So as you can see I have no clear answers that satisfy even my own requirements entirely. For the most part I'm still embedding scripts straight into the HTML header and hope for the best. The biggest issues I've run into have been conflicts between some of my controls that rely on script resources and scripts loaded through the header with duplicate loads. Most of this is mitigated by my controls which can disable script loading and defer to manually loaded scripts when needed (ie. all script resources are properties and can be loaded either from resources, a url or not all injected by the control). Tracking these things down though can be time consuming. I'm not there yet but I think using hte above control is likely to fix that problem, but for generic controls requiring this control container to be used is probably a hard sell.
Even the wwScriptContainer control doesn't address the versioning and update scenarios although it does help with managing resource and file based script injection both for markup and controls (at least for my own controls). So I'm still searching for sanity and some input on what works for others who are heavily using scripts.
So, dear reader, what are your thoughts on this issue? Are you also fighting with script management hell? Do you use scripts from raw files, or do you use resources? What about for controls that use these scripts? And how do you manage these scripts if they get used across solutions? There are lots of options available but which ones make the most sense to you and why?
I'd love to hear your thoughts.