General Purpose ASP.NET Data Source Control

OK, you already know about the ObjectDataSource control, so what’s wrong with it? Well, for once, it doesn’t pass any context to the SelectMethod, you only get the parameters supplied on the SelectParameters plus the desired ordering, starting page and maximum number of rows to display. Also, you must have two separate methods, one for actually retrieving the data, and the other for getting the total number of records (SelectCountMethod). Finally, you don’t get a chance to alter the supplied data before you bind it to the target control.

I wanted something simple to use, and more similar to ASP.NET 4.5, where you can have the select method on the page itself, so I came up with CustomDataSource. Here’s how to use it (I chose a GridView, but it works equally well with any regular data-bound control):

   1: <web:CustomDataSourceControl runat="server" ID="datasource" PageSize="10" OnData="OnData" />
   2: <asp:GridView runat="server" ID="grid" DataSourceID="datasource" DataKeyNames="Id" PageSize="10" AllowPaging="true" AllowSorting="true" />

The OnData event handler receives a DataEventArgs instance, which contains some properties that describe the desired paging location and size, and it’s where you return the data plus the total record count. Here’s a quick example:

   1: protected void OnData(object sender, DataEventArgs e)
   2: {
   3:     //just return some data
   4:     var data = Enumerable.Range(e.StartRowIndex, e.PageSize).Select(x => new { Id = x, Value = x.ToString(), IsPair = ((x % 2) == 0) });
   5:     e.Data = data;
   6:     //the total number of records
   7:     e.TotalRowCount = 100;
   8: }

Here’s the code for the DataEventArgs:

   1: [Serializable]
   2: public class DataEventArgs : EventArgs
   3: {
   4:     public DataEventArgs(Int32 pageSize, Int32 startRowIndex, String sortExpression, IOrderedDictionary parameters)
   5:     {
   6:         this.PageSize = pageSize;
   7:         this.StartRowIndex = startRowIndex;
   8:         this.SortExpression = sortExpression;
   9:         this.Parameters = parameters;
  10:     }
  11:  
  12:     public IEnumerable Data
  13:     {
  14:         get;
  15:         set;
  16:     }
  17:  
  18:     public IOrderedDictionary Parameters
  19:     {
  20:         get;
  21:         private set;
  22:     }
  23:  
  24:     public String SortExpression
  25:     {
  26:         get;
  27:         private set;
  28:     }
  29:  
  30:     public Int32 StartRowIndex
  31:     {
  32:         get;
  33:         private set;
  34:     }
  35:  
  36:     public Int32 PageSize
  37:     {
  38:         get;
  39:         private set;
  40:     }
  41:  
  42:     public Int32 TotalRowCount
  43:     {
  44:         get;
  45:         set;
  46:     }
  47: }

As you can guess, the StartRowIndex and PageSize receive the starting row and the desired page size, where the page size comes from the PageSize property on the markup. There’s also a SortExpression, which gets passed the sorted-by column and direction (if descending) and a dictionary containing all the values coming from the SelectParameters collection, if any. All of these are read only, and it is your responsibility to fill in the Data and TotalRowCount.

The code for the CustomDataSource is very simple:

   1: [NonVisualControl]
   2: public class CustomDataSourceControl : DataSourceControl
   3: {
   4:     public CustomDataSourceControl()
   5:     {
   6:         this.SelectParameters = new ParameterCollection();
   7:     }
   8:  
   9:     protected override DataSourceView GetView(String viewName)
  10:     {
  11:         return (new CustomDataSourceView(this, viewName));
  12:     }
  13:  
  14:     internal void GetData(DataEventArgs args)
  15:     {
  16:         this.OnData(args);
  17:     }
  18:  
  19:     protected virtual void OnData(DataEventArgs args)
  20:     {
  21:         EventHandler<DataEventArgs> data = this.Data;
  22:  
  23:         if (data != null)
  24:         {
  25:             data(this, args);
  26:         }
  27:     }
  28:  
  29:     [Browsable(false)]
  30:     [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
  31:     [PersistenceMode(PersistenceMode.InnerProperty)]
  32:     public ParameterCollection SelectParameters
  33:     {
  34:         get;
  35:         private set;
  36:     }
  37:  
  38:     public event EventHandler<DataEventArgs> Data;
  39:  
  40:     public Int32 PageSize
  41:     {
  42:         get;
  43:         set;
  44:     }
  45: }

Also, the code for the accompanying internal – as there is no need to use it from outside of its declaring assembly - data source view:

   1: sealed class CustomDataSourceView : DataSourceView
   2: {
   3:     private readonly CustomDataSourceControl dataSourceControl = null;
   4:  
   5:     public CustomDataSourceView(CustomDataSourceControl dataSourceControl, String viewName) : base(dataSourceControl, viewName)
   6:     {
   7:         this.dataSourceControl = dataSourceControl;
   8:     }
   9:  
  10:     public override Boolean CanPage
  11:     {
  12:         get
  13:         {
  14:             return (true);
  15:         }
  16:     }
  17:  
  18:     public override Boolean CanRetrieveTotalRowCount
  19:     {
  20:         get
  21:         {
  22:             return (true);
  23:         }
  24:     }
  25:  
  26:     public override Boolean CanSort
  27:     {
  28:         get
  29:         {
  30:             return (true);
  31:         }
  32:     }
  33:  
  34:     protected override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
  35:     {
  36:         IOrderedDictionary parameters = this.dataSourceControl.SelectParameters.GetValues(HttpContext.Current, this.dataSourceControl);
  37:         DataEventArgs args = new DataEventArgs(this.dataSourceControl.PageSize, arguments.StartRowIndex, arguments.SortExpression, parameters);
  38:  
  39:         this.dataSourceControl.GetData(args);
  40:  
  41:         arguments.TotalRowCount = args.TotalRowCount;
  42:         arguments.MaximumRows = this.dataSourceControl.PageSize;
  43:         arguments.AddSupportedCapabilities(DataSourceCapabilities.Page | DataSourceCapabilities.Sort | DataSourceCapabilities.RetrieveTotalRowCount);
  44:         arguments.RetrieveTotalRowCount = true;
  45:  
  46:         if (!(args.Data is ICollection))
  47:         {
  48:             return (args.Data.OfType<Object>().ToList());
  49:         }
  50:         else
  51:         {
  52:             return (args.Data);
  53:         }
  54:     }
  55: }

As always, looking forward to hearing from you!

                             

No Comments