Asynchronous data access to data source controls in ASP.NET2.0

  • 2020-05-05 11:08:03
  • OfStack

In parts 1 and 2, you created the WeatherDataSource control, which runs against the XML API provided by weather.com (English) and USES WebRequest and WebResponse to access data through HTTP. So far, the service has been accessed synchronously. Therefore, page processing is blocked until the Web request completes. This approach works on test pages and can work on small sites, but it fails miserably on sites that receive a lot of traffic. For example, a portal page, where the weather module might be very common.

introduction

There is a fixed number of threads in the thread pool that can be used to service requests, and unfortunately, this solution does more than just raise the limit (it also increases thread footprint and CPU footprint). Therefore, when a page is blocked waiting for another server, it is still hogging threads, which can cause other incoming requests to wait longer in the queue. This causes slow access to the site and reduces the utilization of CPU. In Visual Studio 2005, we introduced asynchronous pages, which allow controls to define what they want to do asynchronously, i.e., without blocking the thread used to process the request. The details of the asynchronous page itself will not be covered here; Dmitry and Fritz Onion have been covered before. Here's how to leverage this functionality in the data source control, using the add-in framework to implement asynchronous data sources.

background

In part 1, we alluded to the somewhat bizarre design of the DataSourceView class:

public abstract class DataSourceView {
public virtual void Select(DataSourceSelectArguments arguments,
DataSourceViewSelectCallback callback);
protected abstract IEnumerable ExecuteSelect (
DataSourceSelectArguments arguments);
.
}
You'll notice that the public Select method doesn't actually return any data. Instead, it accepts a callback and returns data through that callback. It only calls the protected ExecuteSelect (which always performs synchronous data access) to retrieve the data to be returned to the data-bound control. The default implementation of the DataSourceView class does virtually nothing asynchronously. The reason is that there are no out-of-the-box asynchronous data source controls. But OM's design does allow asynchronous data access, in which the data is not available until the asynchronous work is done. Therefore, we have a model based on callback.

Those familiar with asynchronous API in the framework will notice the lack of asynchronous modes: the public Select, BeginSelect, and EndSelect methods, in which data-bound controls choose which methods to invoke. However, the data-bound control cannot determine whether to choose synchronous API or asynchronous API. In addition, adding properties to data-bound controls doesn't help either. The data source control encapsulates the details about how to access the data store, and whether access to the data store occurs synchronously or asynchronously should be determined based on whether the data source is semantically based or based on custom properties. The correct location of the potential "bool PerformAsyncDataAccess" property is appropriate for the data source control itself. This also allows the data source control to use a method to perform data access, even if multiple data-bound controls are bound to the same data source. Having explained the subtle concepts underlying the architecture many times, I hope to clarify the design.

The last thing to note about asynchronous tasks is that it is entirely up to the page developer to decide whether the page should perform any asynchronous work (via the Async attribute of the Page directive). Therefore, any well-written data source control must degenerate to perform synchronous data access as needed.

framework

In this framework (illustrated with the rest of the examples at the end of this series), the AsyncDataSource and AsyncDataSourceView base classes have been put together, which can be used to implement data source controls that can perform asynchronous data access. The following is an overview of the framework and some comments to help clarify what it means:

public abstract class AsyncDataSourceControl : DataSourceControl,
IAsyncDataSource {
private bool _performAsyncDataAccess;

protected AsyncDataSourceControl() {
_performAsyncDataAccess = true;
}

public virtual bool PerformAsyncDataAccess {
get; set;
}

bool IAsyncDataSource.IsAsync {
get & # 123; return _performAsyncDataAccess && Page. IsAsync; The & # 125;
}
}

public abstract class AsyncDataSourceView : DataSourceView {

protected abstract IAsyncResult BeginExecuteSelect (
DataSourceSelectArguments arguments
AsyncCallback asyncCallback
object asyncState);

protected abstract IEnumerable EndExecuteSelect (
IAsyncResult asyncResult);

protected override IEnumerable ExecuteSelect (
DataSourceSelectArguments arguments) & # 123;
// implement
inherited from DataSourceView // abstract ExecuteSelect method,
// the method is to use BeginExecuteSelect and EndExecuteSelect,
// in order to
by blocking // synchronize data access.
The & # 125;

private IAsyncResult OnBeginSelect(object sender,
EventArgs e, AsyncCallback asyncCallback
object extraData);
private void OnEndSelect (IAsyncResult asyncResult);

public override void Select(DataSourceSelectArguments arguments,
DataSourceViewSelectCallback callback) & # 123;
if (_owner IsAsync) & # 123;
// use OnBeginSelect and OnEndSelect
// as BeginEventHandler and EndEventHandler methods,
// to call Page.RegisterAsyncTask,
// to indicate that
is required // work asynchronously. These methods will in turn
// calls specific
// data source implementation by calling
//
which has been introduced in this class // abstract BeginExecuteSelect and EndExecuteSelect
/ / method.
The & # 125;
else & # 123;
// perform synchronous data access to
base. Select (arguments callback);
The & # 125;
The & # 125;
.
}

example

The new AsyncWeatherDataSource will now be derived from AsyncDataSourceControl, while AsyncWeatherDataSourceView will be derived from AsyncDataSourceView.

public class AsyncWeatherDataSource : AsyncDataSourceControl {

// same as WeatherDataSource }

private sealed class AsyncWeatherDataSourceView : AsyncDataSourceView {
private AsyncWeatherDataSource _owner;
private WeatherService _weatherService;

public AsyncWeatherDataSourceView(AsyncWeatherDataSource owner,
string viewName)
: base(owner, viewName) {
_owner = owner;
}

protected override IAsyncResult BeginExecuteSelect(DataSourceSelectArguments arguments,
AsyncCallback asyncCallback,
object asyncState) {
arguments.RaiseUnsupportedCapabilitiesError(this);

string zipCode = _owner.GetSelectedZipCode();
if (zipCode.Length == 0) {
return new SynchronousAsyncSelectResult(/* selectResult */
null,
asyncCallback, asyncState);
}

_weatherService = new WeatherService(zipCode);
return _weatherService.BeginGetWeather(asyncCallback, asyncState);
}

protected override IEnumerable EndExecuteSelect(IAsyncResult asyncResult) {
SynchronousAsyncSelectResult syncResult =
asyncResult as SynchronousAsyncSelectResult;
if (syncResult != null) {
return syncResult.SelectResult;
}
else {
Weather weatherObject =
_weatherService.EndGetWeather(asyncResult);
_weatherService = null;

if (weatherObject != null) {
return new Weather[] { weatherObject };
}
}

return null;
}
}
The key thing to note is that when using the framework, you only need to implement BeginExecuteSelect and EndExecuteSelect. In their implementation, the BeginXXX and EndXXX methods (SqlDataCommand in Visual Studio 2005, SqlDataCommand) that are exposed by various objects in the framework, such as WebRequest or IO streams, are typically called and IAsyncResult is returned. In this example, there is an WeatherService helper class that encapsulates the underlying WebRequest object.

For those frameworks that actually lack asynchronous patterns, you'll see valid asynchronous patterns here; And BeginExecuteSelect and EndExecuteSelect that you want to implement, and Begin and End methods that you want to call to return an instance of IAsyncResult.

Perhaps the most interesting is the SynchronousAsyncSelectResult class (which is somewhat of a paradox). This class comes with the framework. It is basically an IAsyncResult implementation that makes the data immediately available and reports true from its IAsyncResult.CompletedSynchronously properties. So far, this has only worked if the zip code is not selected and you need to return null (it does not make sense to start an asynchronous task and only return null), but as you will see below, it is also useful in other scenarios.

The page infrastructure hides most of the details of performing asynchronous work in the context of Microsoft ASP.NET. The framework you want to provide here enables you to write data sources that use this infrastructure with minimal effort. However, implementing asynchronous behavior is complex by its nature. Sometimes, the first time you read this article, you have some questions, and the second time you read it, you may understand. You can use the "my comments" form below to send a question or discussion.

Related articles: