| Plain XmlHttp Object The winner by a long margin was the MS ASP.NET AJAX. Surprising! not really. MS ASP.NET AJAX framework might not be the slickest framework in the market but without any double it is one of the easiest one to use. The real power lies in the UpdatePanel control which can update a portion of the page using client side postbacks. Just put your control inside the UpdatePanel and it will do the rest (at least for most of the cases). Performance wise ASP.NET Ajax framework is not good. There are couple of reasons. First the size of the Client Framework JavaScript library is pretty huge. Second although it is asynchronous it does follow the complete life cycle of the page which kills the performance. The selling point of the MS ASP.NET AJAX is that it took away the complexity to build the updated user interface. Let's consider a real world scenario. The poll control which is displayed on the GridViewGuy is NOT using the ASP.NET AJAX framework. It uses ASP.NET 2.0 client callbacks to make XmlHttp requests to the server's method. Each time you mark a choice and submit your vote I build the user interface for the result of the poll on the server side. The processing can be done on the client but that requires lot of JavaScript. Anyway, MS ASP.NET AJAX helps you to forget about the updated interface as the UpdatePanel takes care of that and because the whole page life cycle is repeated with the ViewState and stuff. So, which Ajax framework should you use? My humble answer will be to mix it up. Use MS AJAX where the user interface update is complex and use ASP.NET client callbacks OR third party libraries when making simple calls and making simple updates. The reason I pressed on using ASP.NET AJAX when the interface is complex is because you definitely don't want to create the interface again. So, leave this job for ASP.NET AJAX UpdatePanel.
AJAX is a superb technology but it has some sharp edges. Here are couple of things that I came across when developing AJAX enabled applications. Let's say that you have a page which sends request to the server using ASP.NET 2.0 Client Callbacks (Behind the scenes the client callbacks calls the XmlHttp object but in a different way as the request is still processed on the server side). The request is send using a HTML input button control. protected void Page_Load(object sender, EventArgs e) { // register callbacks RegisterCallbacks(); } private void RegisterCallbacks() { string script = String.Empty; string cbRef = ClientScript.GetCallbackEventReference(this, "arg", "callback", String.Empty); if (!ClientScript.IsClientScriptBlockRegistered("CallServer")) { script = "function makeServerCall(arg,context) {" + cbRef + "}"; // inject the script ClientScript.RegisterClientScriptBlock(this.GetType(), "CallServer", script, true); } } public string GetCallbackResult() { System.Threading.Thread.Sleep(5000); // only for demonstration purposes return _argument; } public void RaiseCallbackEvent(string eventArgument) { _argument = eventArgument; } Here is the button code that actually makes the request: <input type="button" value="Call Server" onclick="callServer()" /> function callServer() { makeServerCall('Hello World',''); } function callback(response) { alert(response); } Everything works fine until the user got disconnected from the internet or the application server goes down. Now, the user presses the input button and nothing happens. No data is returned since the data is assigned at the server side and since there is no connection or server is down then there is no data returned. In this case the callback function is also not fired. The question is how will you notify the user that nothing happened? How will you tell the user that "Hey! the user interface is dead!". I guess the answer is to notify the user that something has happened instead of notifying that something has not happened. The scenario that I discussed might be too vague for this example but consider a "School Testing Application" where the students are taking an online exam. They have 50-100 questions (multiple choice) and they select the answer. As, soon as the answer is selected the choice is saved in the database and a message is displayed "Successfully Saved!". If the message is not displayed then it means that there is some problem. You can also reload the page after every 5 minutes to get the fresh copy out of the database. This will put an extra protection of loosing unsaved answers. On the other hand if the user clicks two answers very quickly then there is a possibility that only one of them got saved. I have discussed this problem below: Another common problem with using AJAX enabled applications is making two AJAX requests one after the other. This will cancel the first request and the later request will be made. In the code above I am purposely making the thread to sleep for 5 seconds now, if during that time another request comes then the old request will be killed. Dino Esposito also discussed this problem in his talk in DNR and he concluded that this is an architecture problem of ASP.NET framework. What problems did you faced when developing an AJAX application and how did you solved it?
I was working on my demo application when I needed to test a Custom Membership Provider. Here is a small piece of code that tests whether the user got created or not. [TestFixture] public class UserTestFixture { [Test] [RollBack] public void CanAddUser() { string userName = "JohnDoetest"; string password = "something123$"; string email = "johndoetest@gmail.com"; BolayToMembershipProvider provider = new BolayToMembershipProvider(); NameValueCollection config = new NameValueCollection(); config.Add("applicationName", "BolayTo"); config.Add("name", "BolayToMembershipProvider"); config.Add("connectionStringName", "BolayToConnectionString"); config.Add("requiresQuestionAndAnswer", "false"); provider.Initialize(config["name"], config); MembershipCreateStatus membershipStatus = new MembershipCreateStatus(); provider.CreateUser(userName, password, email, null,null, true, null, out membershipStatus); Assert.AreEqual(MembershipCreateStatus.Success, membershipStatus,"User not inserted"); } } The NameValueCollection variable "config" sets up the configuration settings for the Membership Provider. You can improve this by adding the Membership Provider settings in App.config and then reading the config block at runtime.
| Which AJAX Framework do you mostly use? | | ASP.NET AJAX | | AJAX PRO Library | | Anthem.NET | | Plain XmlHttp Object This is the new poll on GridViewGuy. Simply visit GridViewGuy and cast your vote. The poll control will be on the upper right hand side of the screen. | |
INPUT checkbox checked property is kind of funny. This is because the name of the property also becomes the value of the property as shown in the example below: input type="checkbox" checked = "checked" or simply input type="checkbox" checked So, if you want to dynamically generate the checkbox at runtime and not make the checkbox checked then simply remove the checked property as shown below: bool showChecked = true; Literal lit = new Literal(); lit.Text = "<input type=\"checkbox\" " + (showChecked == true ? "Checked" : String.Empty) +"/>" ; this.pn1.Controls.Add(lit); this.pn1.DataBind(); In my opinion the checked property should have boolean value. So, we can say something like the following: <input type=checkbox checked = true/false /> :)
In my opinion your client/user should never see the yellow screen of death. There are numerous ways to redirect the user to a custom error page. In this post I will discuss how to use Application_Error event in the Global.asax file. Let's check out the code that will throw the error. private string GetObjectFromSession() { if (Session["UserName"] == null) throw new ArgumentNullException("UserName is null","UserName is null. Your session has expired. <res> Please log on to the system to start a new session </res>"); return Session["UserName"] as String; } So, the above code will throw the error since I am not storing anything in the Session Object. You will notice that I am using the <res> ("res") tags inside for the exception message. The "res" means resolution of the problem. I think it is important to give users some information on how to solve the problem and get back on the right track. protected void Application_Error(object sender, EventArgs e) { HttpContext context = HttpContext.Current; Exception ex = context.Server.GetLastError(); Response.Write("<link href=\"Stylesheet1.css\" rel=\"stylesheet\" type=\"text/css\" />"); Response.Write(TableHelper.GenerateErrorTable(ex)); context.Server.ClearError(); } The Application_Error event uses the TableHelper and style sheets to make the error look better or prettier. Take a look at the result shown in the screen shot shown below: 
UPDATE: private static string ReplaceTags(string message) { return ((message.Replace("<res>","<span class=\"resolution\">")).Replace("</res>","</span>")); }
public static string GenerateErrorTable(Exception ex) { Table table = new Table(); table.CssClass = "pollTable"; TableHeaderRow headerRow = new TableHeaderRow(); headerRow.CssClass = "pollTitle"; TableHeaderCell headerCell = new TableHeaderCell();
headerCell.Text = "ERROR DETAILS"; headerRow.Cells.Add(headerCell);
Literal litText = new Literal(); litText.Text = ReplaceTags(ex.Message) + " " + ReplaceTags(ex.InnerException.Message);
TableRow row = new TableRow(); TableCell cell = new TableCell(); cell.Controls.Add(litText); row.Cells.Add(cell);
TableRow resRow = new TableRow(); TableCell resCell = new TableCell(); resCell.Text = ErrorResource.SessionNullError;
resRow.Cells.Add(resCell);
table.Rows.Add(headerRow); table.Rows.Add(row); table.Rows.Add(new TableRow());
StringWriter sw = new StringWriter(); HtmlTextWriter htw = new HtmlTextWriter(sw);
table.RenderControl(htw); return sw.ToString(); }
In this post I will demonstrate how to use WebClient.UploadFile to upload multiple files to the server. The attachment procedure is like gmail which allows you to select one file after the other.
<a href="#" onclick="attachFile()">Attach a file</a>
<script language="javascript" type="text/javascript">
var selectedFiles = '';
function ReceiveServerData(response)
{
alert(response);
}
function uploadFile()
{
var fileList = document.getElementById("fileDivBox").getElementsByTagName("INPUT");
for(i=0; i<fileList.length;i++)
{
selectedFiles += fileList[i].value + "|";
}
CallServer(selectedFiles,'');
}
function attachFile()
{
var fu = document.createElement("INPUT");
fu.type = "file";
var br = document.createElement("<BR>");
document.getElementById("fileDivBox").appendChild(fu);
document.getElementById("fileDivBox").appendChild(br);
}
</script>
The attachFile function creates a new input element of type "file" and appends it to the DIV element. When the user clicks the upload button the uploadFile function is fired which iterates through the DIV (fileDivBox) element and gets all the selected files and sends them to the server.
The ASP.NET page uses client callbacks. If you are interested to learn about client callbacks then visit this link.
public void RaiseCallbackEvent(string eventArgument)
{
string[] files = (eventArgument.TrimEnd('|')).Split('|');
WebClient client = new WebClient();
foreach (string file in files)
{
client.UploadFile("http://localhost:1566/FileServer.aspx", "POST", file);
}
}
The RaiseCallbackEvent is where the upload takes place. I am using WebClient class UploadFile method to upload the file. The file is posted to the FileServer.aspx page where I can retrieve it and save it in the server's folder.
FileServer.aspx:
protected void Page_Load(object sender, EventArgs e)
{
string path = @"C:\UploadedFiles\"; // server folder
string[] keys = Request.Files.AllKeys;
foreach(String key in keys)
{
HttpPostedFile file = Request.Files[key];
file.SaveAs(path + file.FileName);
}
}
I will be writing an article about this technique so stay tunned on GridViewGuy.
Just found a great link to Scott Guthrie LINQ to SQL articles. All the articles are compiled into a single PDF format.
Check it out using the link below:
http://it-box.blogturk.net/wp-content/themes/it-box/files/LINQToSql.pdf
Mike Bosch has an excellent post about using Fiddler with localhost. Originally, I used to use fiddler with localhost by replacing the localhost with the computer name. Mike describes that you can simply put a "." after the localhost and it will work just as good. So, instead of http://localhost:4034/MyApplication/Default.aspx you can write http://localhost.:4034/MyApplication/Default.aspx and http://COMPUTERNAME:4034/MyApplication/Default.aspx :)
I have just published an article on GridViewGuy in which I discussed how to create a Poll user control that can be used to collect and display the poll on the website. You can read the article using the link below: Creating AJAX Enabled Poll User Control Using Client Callbacks
|