Ok, here goes. I’ve had one hell of a frustrating day going through my code and moving it to Atlas Beta 1. I’ll get into specific issues in a minute, but my overall feeling is that this build is REALLY BUGGY. The previous ATLAS CTPs were reasonably stable and handled errors gracefully forwarding them to the client code. This build just crashes hard in many cases deep inside of client framework code. I’ve been at it for the better part of the day and while I managed to port all of the samples, just about all of them have one or two issues that I cannot seem to resolve completely without removing functionality. In fact, I've been doing that alot - skipping using framework features instead resorting to lower level JavaScript because it works.
Here’s more detail.
Script Framework Errors
With the code I have I have countless mysterious errors that are blowing up inside of the ATLAS client libraries which are throwing exceptions. If I step into these there with the VS JS Debugger there’s no call stack nothing to debug or check of.
Here’s an example. A call to creating a Sys.UI.Control is failing. I’m doing:
var lblStockname = new Sys.Preview.UI.Label($get('lblStockName'));
lblStockname.set_text(Result.Company + " (" + Result.Symbol + ")<hr>");
lblStockname.get_element().className = "errormessage";
lblStockname.get_element().style.fontSize = "8pt";
This code works the first call but will fail on a second call in the client library:
Sys.UI.Control = function Sys$UI$Control(element) {
/// <param name="element" type="Sys.UI.DomElement"></param>
var e = Function._validateParams(arguments, [
{name: "element", type: Sys.UI.DomElement}
]);
if (e) throw e;
if (typeof(element.control) != 'undefined') throw Error.invalidOperation(Sys.Res.controlAlreadyDefined);
Sys.UI.Control.initializeBase(this);
this._element = element;
element.control = this;
this._oldDisplayMode = this._element.style.display;
if (!this._oldDisplayMode || (this._oldDisplayMode == 'none')) {
this._oldDisplayMode = '';
}
}
The error is:
Error Sys.InvalidOperationException: Can’t create a control if the associated element already has a ‘control’ field.
The element in question is lblStockName. Even odder there’s no control property on it at least in the debugger or immediate window. I have NO IDEA why this is failing on the second pass through – certainly the objects passed in are being created new on every hit. I suspect there’s some glitch in the prototype logic that’s picking up a value somewhere in the prototype chain from another control…
I decided to bypass controls for this and just use plain DOM objects instead:
var lblStockname = $get('lblStockName');
lblStockname.innerHTML = Result.Company + " (" + Result.Symbol + ")<hr>";
lblStockname.className = "errormessage";
lblStockname.style.fontSize = "8pt";
The latter works reliably. The former fails on every second hit.
After some more investigation this seems to be happening ANYTIME a variable is assigned more than once with a new object assignment. This happens anywhere when I reassign the a variable name with a new object reference!
To duplicate all I have to do is assign an object twice in a row to the same variable like this:
var Progress = null;
function StartRequestProcessing()
{
Progress = new Westwind.UI.ProgressBar($get('ProgressBar'));
Progress.set_progress(0);
Progress = new Westwind.UI.ProgressBar($get('ProgressBar'));
Progress.set_progress(0);
}
This is of course not the real code, but in the application itself a new request creates a new instance of the control and the second time that happens it fails with the above error. Everytime. With my own custom control as above, or with built in controls like labels. This looks suspiciously like a problem in the prototype syntax and instance management. Somehow it appears that the instance is static even though a distinct separate local variable is used.
This is easy to fix on my end by simply making the control assignment globally which is more efficient anyway, but this seems like a pretty major and possibly fundamental bug in the framework.
This is only one of many other mysterious errors. I have another request on the same page that fires a simple timer that updates once a minute to display a small time timer display. This thing works once and on any subsequent calls it blows up with a generic null value exception. No callstack, no source, nothing… Where do I go from here?
There are a many null reference exceptions that pop up – they seem to be related to controls unloading in Dispose() calls which seems like a timing or circular reference issue.
This is one particular page I’m dealing with that mixes some Web Service callback functionality along with UpdatePanel functionality. I was actually able to convert the page and make it work relatively easily, but these mysterious client framework failures are not something that I seem to be able to fix.
DataSet/DataTable/DataRow results from Web Services don’t work
I use Web Services and PageMethods from client code quite a bit in my samples, because it's a nice lightweight way to get data from the server. One of the nicer features in ATLAS (and most other .NET based AJAX tools) is the ability to return ADO.NET objects like DataSet/DataTable/DataRow objects to the client.
Unfortunately in Beta 1 I can’t get Web services to return any data results anymore. The server framework throws serialization exceptions of varying messages from not being able to serialize the CultureInfo class to the Module class citing circular references.
I got a couple of messages in response to a post on the forums with others seeing the same thing.
I’m not sure if I’m missing some setting or configuration thing that needs to be done to make this work or what. Everything looks right in the proxy which includes reference to the right types. Other methods of the same service – including some methods that are returning IList based complex objects – are working just fine. But any attempt to return DataTable/DataSet results in these errors. Three out of my samples using separate services do this so it’s not something specific to one sample.
PageMethods Must be Static
PageMethods have changed. PageMethods are JSON based callbacks that go back to the current page. This functionality in the ATLAS CTPs varied from the Web Service functionality in that the callbacks to the page would passback full POST information of the form’s content so you could access controls on the client. Well, it turns out that this behavior has been changed. According to Shawn Burke’s blog PageMethods must be static now so you have no access to the page object.
Not only that but apparently due to a bug in this MS Ajax build the PageMethods must be defined in ASPX page, NOT IN THE CodeBeside:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="LongProgress.aspx.cs" Inherits="Atlas_LongProgress" %>
<%@Register Src="~/ShowCode.ascx" TagPrefix="uc1" TagName="ShowCode" %>
<%@ Import Namespace="ScriptCallbacks.BusinessObjects" %>
<%@ Import Namespace="System.Threading" %>
<%@ Import Namespace="System.Web.Services" %>
<script runat="server">
// *** Due to a bug in the beta PageMethods must be defined
// *** inside of the ASPX page and CANNOT BE IN CodeBehind
[WebMethod]
[ScriptMethod]
public static string StartRequestProcessing()
{
…
}
</script>
WTF?
Seriously, why even bother to put this feature in if the methods have to be static? If that’s the case this feature should be removed and only Web Service access should be there since the functionality is pretty much identical. At least then you're making it clear you're calling a service and 'data layer'.
I think it really bites that the ability to call PageMethods with Postback data has been removed as that’s one VERY handy feature. It allows you to not have to construct very complex method signatures or pass complex objects that have to be properly passed on the client but rather let you do that kind of value parsing on the server more efficiently. But this is now no longer possible – you have to pass the values as direct parameters. It's often so much easier to deal with this stuff on the server rather than on the client especially since the client doesn't have any sort of automated databinding (oh wait, neither does stock ASP.NET <g>).
Honestly I can’t understand this decision by the Microsoft people. I believe the reason is for page cycle issues and worries about how these PageMethod calls can affect the page content. But why couldn’t the PageMethods at least have retrieved this content READ ONLY. IOW, you can read values out of the page, but you wouldn’t be able to change anything? Or even failing that at least provide an option to send the POST data from the client to the server!
There's nothing in the migration guide about this either.
Error Handling
One cool feature of the previous version of ATLAS was the page level Error handler you could set up. There was an ErrorTemplate on the script manager and it had the ability to capture all output and display the errors in a consistent interface including the rather cool shadowed background with the template content shown above it. According to the migration document this was making the script manager to complex… hmmm… what could be easier than to implement a template.
Instead you now have to implement an event and deal with some obscure client event code, the place to the UI content on your own the page somewhere. Yes, it’s true you have more control but this new mechanism isn’t exactly intuitive.
<div id="ErrorDisplay" />
// *** Hook up an End Page EndRequest handler which allows us to check
// *** for callback hooking and errors
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(OnEndRequest);
function OnEndRequest(sender,args)
{
// *** Check for errors
if (args.get_error() != undefined /* && args.get_error().httpStatusCode == '500' */)
{
//debugger;
var errorMessage = args.get_error().message
args.set_errorHandled(true);
//alert("Custom Error Handling:\r\n" + args.get_error().message);
var ErrorDisplay = $get("ErrorDisplay_Message");
ErrorDisplay.innerHTML = "Custom Error Handling:\r\n" + args.get_error().message;
}
}
This code applies to UpdatePanel operations specifically. It doesn’t handle other errors from script or callback code so it’s not exactly a unified error handler.
The PageRequest manager provides a bunch of new functionality BTW. It has beginRequest and endRequest methods which can be used to hook into UpdatePanel callback operations for example and determine when they start and stop which is handy.
Aarrgh
As you can probably tell I'm pretty frustrated by all of this. I was expecting the Beta to be a big improvement over previous versions instead it seems like we've taken several steps back. It's doubly frustrating to me because at this point i have little choice but to make do with this build since I have to do a presentation with it in a couple of weeks at ASP.NET Connections. That's going to be a lot of fun <s>...
I really am bummed that Microsoft apparently rushed this build out before it was ready. This was supposed to be a beta product, but several of these issues seem to indicate that some pretty major issues were simply overlooked (there's really no excuse to have PageMethods requiring running in ASPX code, or ADO.NET objects not working in Web Services). To top things off the documentation is horrible and covers only the Ajax 1.0 Beta, and not the value add Preview... Even all the old documentation links are gone and haven't been updated with new content.