Magazine > Issues > 2005 > May >  ASP.NET Performance, Notification, Keeping Sort...
Web Q&A
ASP.NET Performance, Notification, Keeping Sort Order, and More
Edited by Nancy Michell


Q I am developing ASP.NET sites, and I need to measure the performance of the pages. Will adding performance counters impact the performance? What about monitoring them? I've read that it slows down the application.
Q I am developing ASP.NET sites, and I need to measure the performance of the pages. Will adding performance counters impact the performance? What about monitoring them? I've read that it slows down the application.

A ASP.NET and the Microsoft® .NET Framework will publish to performance counters whether you read them or not. However, the act of retrieving those values will take up CPU cycles, introduce additional context switching, and so forth. In most situations, you are going to want to monitor performance once you hit production, so this should be considered part of the production environment you would want to measure anyway.
A ASP.NET and the Microsoft® .NET Framework will publish to performance counters whether you read them or not. However, the act of retrieving those values will take up CPU cycles, introduce additional context switching, and so forth. In most situations, you are going to want to monitor performance once you hit production, so this should be considered part of the production environment you would want to measure anyway.
Another alternative is to use a tool that measures TTLB (the number of milliseconds that passed before the last byte of the response was received), which is a client-side measurement. This could be used in conjunction with your server-side processing times to understand what percentage of the time is consumed on the server and what is caused by network latency.

Q I'm building a system that provides a stateless Web service to integrate with partners. Basically, the external partner calls my Web service once a day and the Web service forwards the request to my back-end server to handle. Handling the request takes quite a bit of time and, in the meantime, the partner's system wants to know if it's been handled successfully or not. How can I do this?
Q I'm building a system that provides a stateless Web service to integrate with partners. Basically, the external partner calls my Web service once a day and the Web service forwards the request to my back-end server to handle. Handling the request takes quite a bit of time and, in the meantime, the partner's system wants to know if it's been handled successfully or not. How can I do this?

A Sometimes it seems like the most intuitive way to address this is to make the call synchronized. But the question becomes: is a one or two hour timeout value for a Web service outrageous?
A Sometimes it seems like the most intuitive way to address this is to make the call synchronized. But the question becomes: is a one or two hour timeout value for a Web service outrageous?
You could return success immediately, and let the partner call later to get the result (based on some context token issued for that call). Or the partner could implement a callback.
Actually, those last two options are better than increasing the timeout. Because the operation is only happening once a day, it's already a batch-style call of sorts. These are best handled by asynchronous calls. If the partner doesn't have a means to provide a notification mechanism (your second option), then returning them a job/task ID in a context token makes complete sense. If they're going to be polling for the results, you may also consider putting a polling interval based on the current system load into the context token.

Q I've placed a Data-Grid on my Web Form which has both sorting and editing functionality. When I click on the Edit button after sorting on a column, rows from the original sort order are selected. How can I resolve this issue?
Q I've placed a Data-Grid on my Web Form which has both sorting and editing functionality. When I click on the Edit button after sorting on a column, rows from the original sort order are selected. How can I resolve this issue?
My Page_Load event looks like this:
private void Page_Load(object sender, System.EventArgs e)
{
    PubAdapter.Fill(publisherDataSet1);
    if (!IsPostBack)
    {
        MyGrid.DataSource = publisherDataSet1.publishers.DefaultView;
        MyGrid.DataBind();
    }
}
My sorting code looks like this:
private void MyGrid_SortCommand(object source, 
System.Web.UI.WebControls.DataGridSortCommandEventArgs e)
{
    DataView SortView  = publisherDataSet1.publishers.DefaultView;
    SortView.Sort = e.SortExpression;
    MyGrid.DataSource = SortView;
    MyGrid.DataKeyField = "pub_id";
    MyGrid.DataBind();
}
Finally, the following code is what I use to perform editing:
private void MyGrid_EditCommand(object source, 
System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
    MyGrid.DataSource = publisherDataSet1.publishers.DefaultView;
    MyGrid.DataKeyField = "pub_id";
    MyGrid.EditItemIndex = e.Item.ItemIndex;
    MyGrid.DataBind();
}

A The only place where you are setting the Sort in the DataView is in the sort event. This means that next time the user does a postback, the DataView will be filled again from your data store, and then when you call DataBind, the sort is gone because you are rebinding to the unsorted data. So it is up to you to maintain the proper sort in your DataView. The way to do that is to essentially reapply the sort expression and sort order to your DataView. Note that if the amount of data is fairly small, it may be better to use ViewState to store the sorted data rather than Session, since it will work on Web Forms without a state server and in scenarios where session state is disabled. Just note that storing values in the ViewState might be a security risk since ViewState is not encrypted, just encoded. Therefore someone could see the content (although they will not be able to tamper with it).
A The only place where you are setting the Sort in the DataView is in the sort event. This means that next time the user does a postback, the DataView will be filled again from your data store, and then when you call DataBind, the sort is gone because you are rebinding to the unsorted data. So it is up to you to maintain the proper sort in your DataView. The way to do that is to essentially reapply the sort expression and sort order to your DataView. Note that if the amount of data is fairly small, it may be better to use ViewState to store the sorted data rather than Session, since it will work on Web Forms without a state server and in scenarios where session state is disabled. Just note that storing values in the ViewState might be a security risk since ViewState is not encrypted, just encoded. Therefore someone could see the content (although they will not be able to tamper with it).
You could create a property called SortExpression like this:
private string SortExpression {
    get {
        object o = ViewState["SortExpression"];
        return o == null ? "" : (string)o;
    }
    set {
        ViewState["SortExpression"] = value;
    }
}
Then create and use a helper method that actually binds the data (see Figure 1).
private void Bind() {
    PubAdapter.Fill(publisherDataSet1);
    DataView dataView = publisherDataSet1.publishers.DefaultView;
 
    string sortExpression = SortExpression;
    if (sortExpression.Length != 0) { 
        dataView.Sort = sortExpression;
    }
    MyGrid.DataKeyField = "pub_id";
    MyGrid.DataSource = dataView;
    MyGrid.DataBind();
}
 
private void Page_Load(object sender, System.EventArgs e) {
    if (!IsPostBack) {
        Bind();
    }
}
 
private void MyGrid_SortCommand(object source,
  DataGridSortCommandEventArgs e) {
    SortExpression = e.SortExpression;
    Bind();
}
 
private void MyGrid_EditCommand(object source, 
  DataGridCommandEventArgs e) {
    MyGrid.EditItemIndex = e.Item.ItemIndex;
    Bind();            
}

Q I've got an ASP.NET application that attempts to connect to an OLAP cube on another machine. If the user is logged onto the Web server (locally or through terminal services), my application can connect. Otherwise the connection fails, even if I explicitly impersonate the user in code before trying the connection. This all worked well when the database and cubes were on the same machine as IIS. What's going wrong here?
Q I've got an ASP.NET application that attempts to connect to an OLAP cube on another machine. If the user is logged onto the Web server (locally or through terminal services), my application can connect. Otherwise the connection fails, even if I explicitly impersonate the user in code before trying the connection. This all worked well when the database and cubes were on the same machine as IIS. What's going wrong here?

A This is a common double-hop problem. You can't just jump off a machine to another machine as an impersonated user. You need to use Kerberos Delegation, or you need to stop impersonating for a bit (falling back to the identity of the process, assuming it has permissions), make the connection, and then pop back. Otherwise you need to log onto the Web Server as another user and make the connection.
A This is a common double-hop problem. You can't just jump off a machine to another machine as an impersonated user. You need to use Kerberos Delegation, or you need to stop impersonating for a bit (falling back to the identity of the process, assuming it has permissions), make the connection, and then pop back. Otherwise you need to log onto the Web Server as another user and make the connection.
So your options are:
  1. Use Kerberos Delegation.
  2. Stop impersonation and connect to the remote resource as the identity of the worker process.
  3. Call LogonUser, passing credentials for a user with permissions to the remote resource, and then impersonate using the returned token.
A great resource on the topic is an MSDN® webcast called Getting Delegation to Work with IIS and ASP.NET: The Ins and Outs of Protocol Transition.

Q My WebMethods randomly disappear from my Web service description page, with the error: "Server did not recognize the value of HTTP Header SOAPAction." This also happens when the application is completely inactive. What should I do?
Q My WebMethods randomly disappear from my Web service description page, with the error: "Server did not recognize the value of HTTP Header SOAPAction." This also happens when the application is completely inactive. What should I do?

A Check to see if you're using the same namespace and class in your Web service as in other reference dependencies or bin folders. Do they share the same name? If so, then the duplicate class can be loaded before the real Web service class and you end up without WebMethods.
A Check to see if you're using the same namespace and class in your Web service as in other reference dependencies or bin folders. Do they share the same name? If so, then the duplicate class can be loaded before the real Web service class and you end up without WebMethods.
You should check to see if there's a class defined in a dependent assembly with the same name. This can happen when the Web service project has a reference to some utility assembly, which has a Web reference back to the Web service—so it gets the proxy class for the Web service created with the same class name but different namespace ("localhost"). Then if you change the namespace to the same namespace as the Web service-derived class, you have a conflict. It won't be detected at compile time if the projects are compiled separately, but since they are both loaded into the appdomain (all assemblies in the \bin directory will be), and since the .asmx file just references the class name if it gets the proxy object, you'll have a problem.

Q I am attempting to access an Excel spreadsheet using OLE DB. I can display the data from the spreadsheet using a GridView, but I can't update the data and insert it into the spreadsheet. I always get error message: "Operation must use an updateable query." Is this a problem in Excel?
Q I am attempting to access an Excel spreadsheet using OLE DB. I can display the data from the spreadsheet using a GridView, but I can't update the data and insert it into the spreadsheet. I always get error message: "Operation must use an updateable query." Is this a problem in Excel?

A You are going to have big problems trying to do this from a Web site. When you open the spreadsheet with OLE DB the result is an exclusive lock, so only one user at a time will be able to write anyway. Plus, you would need to disable pooling because pooling tries to keep the connection open, causing exclusive lock problems as well. Excel is not a multiuser database system.
A You are going to have big problems trying to do this from a Web site. When you open the spreadsheet with OLE DB the result is an exclusive lock, so only one user at a time will be able to write anyway. Plus, you would need to disable pooling because pooling tries to keep the connection open, causing exclusive lock problems as well. Excel is not a multiuser database system.
You are better off switching to SQL Server if you can, as it will offer you much more success. But in any case, try the code in Figure 2. It will help if you still want to use Excel. Note that opening the file book1.xls in Excel also puts an exclusive lock on the file, breaking this code, so Excel only works for one user at a time.
private void button1_Click(object sender, System.EventArgs e)
{
  OleDbConnection conn = new OleDbConnection();
  conn.ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;Data 
    Source=C:\Book1.xls;Extended Properties='Excel 
    8.0;HDR=Yes;ReadOnly=0;'";

  conn.Open();
  OleDbCommand cmd = conn.CreateCommand();
  cmd.CommandText = "create table [Table1] (f1 long, f2 text)";
  cmd.ExecuteNonQuery();

  for ( int i=1; i<=10; i++ )
  {
    cmd.CommandText = "insert into [Table1] (f1,f2) values (" + 
      i.ToString() + ",'Value')";
    cmd.ExecuteNonQuery();
  }

  cmd.CommandText = "update [Table1] set f2 = 'Changed' where f1 = 5";
  cmd.ExecuteNonQuery();

  cmd.Dispose();
  conn.Close();
}

Q I have a DLL that's not unloading when I create an object using JavaScript and the ActiveX® control method. Setting the object to NULL is not causing the class to be unloaded. Why is It only unloaded when I exit Microsoft Internet Explorer?
Q I have a DLL that's not unloading when I create an object using JavaScript and the ActiveX® control method. Setting the object to NULL is not causing the class to be unloaded. Why is It only unloaded when I exit Microsoft Internet Explorer?

A Consider what the world would be like if COM actually loaded and unloaded components the way you want it to:
for (x = 1; x < 1000 ; ++x)
{
    var wuwebObj = new ActiveXObject("SoftwareDistribution.WebControl");
    wuwebObj = null;
}
That would load and unload the DLL a thousand times. Aggressive unloading is a performance nightmare, which is exactly why COM doesn't do it. The assumption in COM is that you loaded a DLL because you intended to use the code in it! COM keeps the DLL around until the apartment is torn down or you instruct COM to unload the DLLs.
A Consider what the world would be like if COM actually loaded and unloaded components the way you want it to:
for (x = 1; x < 1000 ; ++x)
{
    var wuwebObj = new ActiveXObject("SoftwareDistribution.WebControl");
    wuwebObj = null;
}
That would load and unload the DLL a thousand times. Aggressive unloading is a performance nightmare, which is exactly why COM doesn't do it. The assumption in COM is that you loaded a DLL because you intended to use the code in it! COM keeps the DLL around until the apartment is torn down or you instruct COM to unload the DLLs.
Also, you're using JScript®, a language with a nondeterministic garbage collector. Even if COM did aggressively tear down DLLs, setting an object to null does not release an object in JScript. It marks it as releasable, which is a completely different thing. That object is not actually going to be released until the garbage collector runs, which might be some time later.
In short, you are using the wrong language. If you require deterministic control over the lifetime of COM DLLs, you need to be using a language such as C++, where you can ensure that all instances of the objects have known lifetimes, and where you can call CoFreeUnusedLibraries whenever you feel like it. (See CoFreeUnusedLibraries for more information on CoFreeUnusedLibraries, and when it releases objects, and for the rest of the COM reference see COM (Component Object Model).)
There are a number of ways that you could solve this problem. One way would be to write two DLLs, one that contains the object and one that contains its class factory. COM creates the class factory DLL, and that DLL is never unloaded. The class factory DLL can then manage the lifetime of the object DLL—when the class factory is asked for an instance, it loads the object's DLL and creates an instance. The object DLL should probably communicate with the class factory DLL so that the class factory DLL knows when the last outstanding object is released. The class factory can then aggressively unload the object DLL.
There are probably some more elegant ways of doing this. One way might be with shadow-copied files. Or, you could call the CollectGarbage function to force a collection—there are very few situations in which you ought to call CollectGarbage, but this might be one of them.

Send your questions and comments to  webqa@microsoft.com.

Thanks to the following Microsoft developers for their technical expertise: Francisco Arreciado, Sanjiv Banerjee, Jamie Brigden, Todd Carter, Jim Cheshire, Michael Evans, Alan Gao, Mark Haas, Zhaojie Huang, Bas Hugenholtz, Chris Jackson, Rajive Kumar, Eric Lippert, Christer Ljung, David Lovell, Carlos Aguilar Mares, Wade Mascia, Matt Neerincx, Nikola Penkov, Don Smith, Josh Twist, Murugesan Vivekananthan, and Peter Williams.


Page view tracker