During the PDC release milestone of RIA Services, we discovered an error from DomainDataSource when using AutoLoad="True" and when using a DataPager bound to the DomainDataSource’s Data property.  We found that when the PageSize property is specified on the DataPager and not on the the DomainDataSource itself, an exception would often (but not always) be thrown from DomainDataSource, indicating that a load could not be invoked while another load was in progress.  The reason for this will actually interest you most if you use AutoLoad set to False.

The issue is easiest to explain with a series of events that occurs when auto-loading.  Consider the following XAML:

<dds:DomainDataSource x:Name=”myDDSQueryName=”GetCustomers> 
  <dds.DomainDataSource.DomainContext>
  <my:CustomerContext />
  </dds:DomainDataSource.DomainContext> 
</dds:DomainDataSource>
 
<data:DataPager x:Name=”myPagerSource=”{Binding Data, ElementName=myDDS}” PageSize=”10/>


With this XAML, here’s what was happening before we fixed this bug:

  1. myDDS gets instantiated and its properties get set
  2. myPager gets instantiated and its properties get set (except for its Dependency Properties)
  3. myDDS gets Loaded, during which it sees that it’s in AutoLoad mode, and it begins loading data
  4. myPager gets loaded, at which time its Source gets bound, which causes the PageSize property to get applied to myDDS.Data.PageSize
  5. The DomainDataSource.PageSize property gets set, and with AutoLoad set to True, this invokes a new load
  6. Exception is thrown because a load cannot be invoked while through the DomainDataSource while another load is being performed (unless the original load has already completed, in which case a second load is successfully invoked)

In addition to the intermittent exception that this causes, an ostensibly worse problem also hides here.  The first load gets invoked without a PageSize.  That means that no paging is applied and all records are requested from the server.  Needless to say, that could be extremely expensive for both the client and the server.

Mitigation

We needed to prevent the first load from beginning until after the DataPager has been loaded and the element-to-element bindings have been applied.  But there’s no clean and safe way to know (from the DomainDataSource’s perspective) that the DataPager even exists, let alone when it has loaded.

From within an event handler for the DomainDataSource’s Control.Loaded event, we use Dispatcher.BeginInvoke to initiate the first auto load.  The dispatched call allows the control loading to finish and the PageSize property to be reflected on the DomainDataSource before our load is invoked.  To be honest, I’m not thrilled with this solution, but it does the trick.  We have been unable to recreate the double-load scenario since applying this fix.  And the alternative was to always advise everyone to specify the PageSize property on the DomainDataSource rather than on the DataPager, and that would have been very unfortunate.

When AutoLoad is False

I mentioned above that the explanation of this issue would interest you most when you have AutoLoad set to false.  With AutoLoad set to true, we address the issue for you.  But when you are calling DomainDataSource.Load() yourself, you must be aware of this PageSize pitfall.  Here are three ways it can bite you if you have PageSize set on a DataPager:

  1. In your page’s constructor, you call InitializeComponent, and then call DomainDataSource.Load();
  2. In the DomainDataSource.Loaded event (not LoadedData), you call DomainDataSource.Load();
  3. In your page’s Loaded handler, you call DomainDataSource.Load().

With any of these three approaches, you will see probably see the double-load.  You will quite possibly get an exception when the DataPager is loaded and the PageSize property gets applied.  Even if you don’t see the exception though, you’ll be hitting your server twice, with the first hit requesting all records.  Not good.

When using AutoLoad set to false, you should utilize the same mechanism I have used for AutoLoad="True" and call Dispatcher.BeginInvoke(() => myDDS.Load()).  This will defer the load until the UI thread has completed the control loading and your PageSize has been set.  If you are stubborn and pushy and want to invoke the load without dispatching it, then you can always set the PageSize on the DomainDataSource instead.

I hope this information helps you avoid the DomainDataSource DataPager PageSize Pitfall.  And as always, please feel free to ping me with questions, comments, or concerns.  I love gathering your feedback!