It's time to talk about the abstract class DataSourceView.
The DataSourceView class exposes the capabilities of the data source control (if it can update, insert, delete, page, sort…) and has the methods to do CRUD operations (select, insert, update and delete). It's the element with most functionality in the new data binding infrastructure by far.
It exposes the following properties:
public string Name { get; }
public virtual bool CanDelete { get; }
public virtual bool CanInsert { get; }
public virtual bool CanPage { get; }
public virtual bool CanRetrieveTotalRowCount { get; }
public virtual bool CanSort { get; }
public virtual bool CanUpdate { get; }
protected EventHandlerList Events { get; }
If you remember what I said in a previous post, a data source control can have many views, and each view has got a unique name. You can retrieve the name with the Name property.
The others public properties are just for getting information about the supported capabilities. If you check them you'll see that there isn't a property called CanSelect, because that capability has to be provided by the control. The default implementation for the CanXXX methods is to return false.
If you want to implement your own DataSourceView your implementation of those methods will depend on your data source. For example, if you're making a DataSourceView for a web service that gives you information about traffic load in a street, you only get data and the default implementation of the CanXXX methods is good for you. However, if you want to have a DataSourceView to expose your own domain logic objects, you'll probably support at least select, insert, update and delete operations. However the implementation of the CanXXX methods should have some logic in order to see if the user has supplied enough information to enable a capability because you can support update but if the user hasn't supplied and UpdateMethod to call, you can't update the underlying data source.
The protected property Events is used to save space. If you add an event without specifying the add and remove methods, the compiler creates a private field to store the event. As a derived class can have a lots of events, if a client of the class doesn't use more than one or two events, the class will have lots of wasted space. A recommended practice in this situation is to have an EventHandlerList to store the events in order to save space for the unused events, explicitly adding or removing the delegates in the event.
DataSourceView has a public event:
public event EventHandler DataSourceViewChanged;
You should fire this event whenever the view changes the underlying data source to notify data bound controls that it's time to get fresh data (see previous posts for more information about this).
Note that when you create a DataSourceView, you should capture the IDataSource DataSourceChanged event and update the DataSourceView, and probably fire the DataSourceViewChanged.
The class has the following methods:
public virtual void Select(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback);
public virtual void Insert(IDictionary values, DataSourceViewOperationCallback callback);
public virtual void Update(IDictionary keys, IDictionary values, IDictionary oldValues, DataSourceViewOperationCallback callback);
public virtual void Delete(IDictionary keys, IDictionary oldValues, DataSourceViewOperationCallback callback);
protected internal abstract IEnumerable ExecuteSelect(DataSourceSelectArguments arguments);
protected virtual int ExecuteInsert(IDictionary values);
protected virtual int ExecuteUpdate(IDictionary keys, IDictionary values, IDictionary oldValues);
protected virtual int ExecuteDelete(IDictionary keys, IDictionary oldValues);
protected virtual void OnDataSourceViewChanged(EventArgs e);
protected internal virtual void RaiseUnsupportedCapabilityError(DataSourceCapabilities capability);
The first four methods will be called by data bound controls to perform CRUD operations. Those methods can support asynchronous data access but its default implementation just call protected ExecuteXXX method providing synchronous data access instead of asynchronous. ExecuteInsert, ExecuteUpdate and ExecuteDelete aren't abstract because as the default DataSourceView only has select capabilities, any call to those methods should throw a NotSupportedException. If you really want to use the asynchronous capabilities take a look at Nikhil helper classes as a good starting point.
When you change the underlying data, you can use the OnDataSourceViewChanged method to fire the DataSourceViewChanged event.
The last method, RaiseUnsupportedCapabilityError, needs more explanation. When the DataSource view is requested to do a Select operation, the first parameter that is passed to the Select method is an instance of the DataSourceSelectArguments class. The data bound control that requested the select operation creates this (usually in the PerformSelect method) with the required capabilities the DataSourceView needs to get the data. In the Select method, the DataSourceView should add all the capabilities that the view supports calling DataSourceSelectArguments's AddSupportedCapabilities method. Before making the data access, the DataSourceView should call to another method of the DataSourceSelectArguments class called RaiseUnsupportedCapabilitiesError. This method will compare the required capabilities that the data bound control requested and the capabilities exposed by the data source view. If a requested capability isn't found, RaiseUnsupportedCapabilitiesError will call the DataSourceView's RaiseUnsupportedCapabilityError method that will throw an exception.
In part 4 I'll talk about binding expressions.