Pixata Custom Controls
For Lightswitch

Recent Posts

Popular tags (# posts in brackets)

Anonymous types (3) ASP.NET (5) C# (3) C# tricks and tips (2) Computers (6) Design patterns (3) DomainDataSource (3) Dynamic data (4) Entity framework (3) Entity model framework (5) F# (3) LightSwitch (12) Linq (6) Microsoft (2) MVP (2) MVVM (2) Project Euler (2) RIA services (5) Silverlight (2) SQL Server (4) Unit testing (4) Visual Studio (7) WCF (3) WPF (3)

Gratuitous link to StackExchange




The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Actually, as I'm self-employed, I guess that means that any views I expressed here aren't my own. That's confusing!


Theme modified from one by Tom Watts
C#/F# code styling by Manoli (for posts pre-2016) and Google code prettify (for post from Jan 2016 and beyond)

My rambling thoughts on exploring the .NET framework and related technologies

Please note that this blog has ben retired. Most of the material has been migrated to my new blog at www.pixata.co.uk. Please update your bookmarks to point to the new blog.

# Tuesday, 17 August 2010

Having been a major cynic as far as Mcrosoft's previous ABCDataSource controls were concerned, I have to admit to being very impressed with the DomainDataSource (DDS), which is used to pass data between a RIA service and an ASP.NET page. If you want to use this control, you need to download the WCF RIA Services Toolkit April 2010, which contains this control, and a domain validator control (more on that another time).

My main criticism of the previous data source controls was that they merged the layer boundaries, putting raw SQL into the presentation layer and so on. This prevented you from writing a clearly defined n-tier application, which led many developers to believe that they were only invented for people who didn't understand how to construct an application, or for doing quick demos to impress the management!

Not so the DDS, which it is little more than an interface (with a small "i") to the RIA service behind it. It doesn't hold any code, and merely passes the queries between the layers. The code for the control is very simple...

   1:  <cc1:DomainDataSource ID="ddsUsers" DomainServiceTypeName="ActionsProject.ActionsService" QueryName="GetUsers" runat="server" />

This creates a DDS that is associated with the ActionsService RIA service in the ActionsProject project. The RIA method used for the query is GetUsers.

Now you can bind controls to this data source really easily...

   1:  <asp:DropDownList ID="drpUser" DataSourceID="ddsUsers" DataTextField="UserName" DataValueField="ID" AppendDataBoundItems="true" runat="server">
   2:  <asp:ListItem Value="-1" Text="(any user)" />
   3:  </asp:DropDownList>

This creates a DropDownList control, and uses the data from the DDS. The AppendDataBoundItems property tells it to preserve the one ListItem added in manually above. This allows you to have a drop-down whose first (and by default, selected) item says "(any user)" and has a full list of users as well.

You can use parameters with a DDS, so if you had a list of actions assigned to users, and you used the above drop-down to choose a user, then the DDS to select the actions could look like...

   1:  <cc1:DomainDataSource ID="ddsActionsFiltered" DomainServiceTypeName="ActionsProject.ActionsService" QueryName="GetActionsFiltered" runat="server">
   2:    <QueryParameters>
   3:      <asp:ControlParameter Name="AssignedToUserID" ControlID="drpUser" PropertyName="SelectedValue" />
   4:    </QueryParameters>
   5:  </cc1:DomainDataSource>

This DDS will return all the actions for the selected user. Notice that the single query parameter is set to pull the SelectedValue from the drop-down we created earlier. The value is passed to the RIA service in a parameter named AssignedToUserID.

The query method in the RIA service could look like...

   1:  public IQueryable<Action> GetActionsFiltered(int AssignedToUserID) {
   2:    var actions = this.ObjectContext.Actions.Include("AssignedToUser");
   3:    if (AssignedToUserID != -1) {
   4:      actions = (ObjectQuery<ActionsProject.Action>)actions
   5:                 .Where(a => a.AssignedToUserID == AssignedToUserID);
   6:    }
   7:    return actions;
   8:  }

Notice that the single parameter to this method has the same name as the one identified in the QueryParameters section of the DDS above.

The first thing this method does is create a basic query that would pull back all actions. Remember that Linq queries aren't executed when they are defined by default, so we haven't actually selected any data yet.

As we added the "(any user)" item to the drop-down, we have to account for the fact that we may get a user ID of -1 passed in. If this ID is -1, we can just ignore it, as it means we want all actions. If it is any other value (which we assume is positive, but could validate if we wanted to be extra careful), we need to modify the query to restrict it to actions with that user ID.

Lines 3-6 do that. Note that we need the explicit cast, as the Where() method returns a superclass of our object query, and a compiler error would (and indeed did) occur without the cast.

That's it! Pretty simple stuff, but very powerful. All logic is handled in the business layer (RIA service in this case), giving a neat separation, without requiring any extra coding. There's actually nothing needed in the .aspx.cs file for this example.

Apart from adding more query parameters, that's all you need for a large percentage of cases. I did come across one other point today that might be useful (although not that often). The DDS raises events when it selects, updates, etc, and you can capture these events if you want. It's hard to see why you might want to do this, as validation can be done in the RIA service itself and passed back to the presentation layer really easily (see Validating your RIA services data with the DomainValidator control in ASP.NET for more details). but the facility is there.

I did use it to peek a look at the information being sent back to the RIA service with each query. I added a Literal control named litMsg to my page, and then captured the OnQuerying event of the DDS...

   1:  protected void ddsActionsFiltered_Querying(object sender, 
   2:      Microsoft.Web.UI.WebControls.DomainDataSourceQueryingEventArgs e) {
   3:    IDictionary<string, object> myDictionary = e.QueryParameters;
   4:    litMsg.Text += "<p>" + DateTime.Now.ToLongTimeString() + "</p><ul>";
   5:    foreach (KeyValuePair<string, object> kvp in myDictionary) {
   6:      string o = (kvp.Value == null ? "" : kvp.Value.ToString());
   7:      litMsg.Text += "<li>" + kvp.Key + " == " + o + "</li>";
   8:    }
   9:    litMsg.Text += "</ul>";
  10:  }

All I'm doing here is looping through the parameter collection, which is in the QueryParameters property of the DomainDataSourceQueryingEventArgs, and corresponds to the QueryParameters section of the DDS, and dumping the values to the Literal. I had to account for the fact that values were sometimes null, but other than that, it's pretty simple stuff.

The output looked like this...

Select executed at 15:56:01

  • AssignedToUserID == -1

Select executed at 15:56:27

  • AssignedToUserID == 4

Useful for debugging, but I'm not sure what else!

In summary, the DDS is an excellent addition to the framework, and one that makes creating RIA-based ASP.NET pages much easier. The only real shame is that there doesn't seem to be any documentation on it anywhere. It may be that there are more features that I haven't seen, but without any docs, it's hard to tell. I assume that at some point, the DDS and the DomainValidator control will become part of the framework proper, at which time we can look forward to some documentation. In the meantime, it's well worth using based on what I've explained above.