# Friday, June 05, 2009

Last time, I talked about how to build a Domain Service from scratch using whatever POCO you have lying around.  Now it’s time to talk about how that works on the client side…

One of the coolest things about RIA Services is that you don’t even have to “Add Service Reference…” to get a reference to the Domain Service.  If your Silverlight project is linked to the ASP.NET project correctly (see the RIA Services doc for how this works) the build steps will take care of generating the right code in your Silverlight project, and away you go.  There are several ways of accessing the service client-side, from dead-easy to a bit more involved.  We’ll start with dead easy. 

The very easiest way to get things hooked up is to use the DomainDataSource control.  It wraps your DomainDataContext (the client-side generated bit) with a data source you can bind directly against in XAML.

<ria:DomainDataSource x:Name="PeopleDataSource" LoadMethodName="LoadPersons" AutoLoad="True">
  <ria:DomainDataSource.DomainContext>
      <services:PeopleDomainContext/>
  </ria:DomainDataSource.DomainContext>
</ria:DomainDataSource>
<dataControls:DataForm x:Name="dfPeople" CanUserAddItems="True" CanUserDeleteItems="true"
                     ItemsSource="{Binding ElementName=PeopleDataSource, Path=Data}" 
                     ItemEditEnded="dfPeople_ItemEditEnded">
</dataControls:DataForm>

The LoadMethodName attribute on the DomainDataSource tells it what method on the DomainContext to call in order to correctly populate the data context.  You can also pass parameters defined in XAML to the Load method if you only need to load a subset.  The DataForm control is bound to the Data property of the DomainDataSource, and away you go.  You get this

image

Because the Insert/Update/Delete methods are implemented on the server-side DomainService, the DataForm automagically enables the edit, add and delete buttons at the top.  If I edit or add a record, the save button shows up…

image

Pressing either the Save or Cancel button fires the ItemEditEnded event, which we can grab to submit the changes back to the server

private void dfPeople_ItemEditEnded(object sender, DataFormItemEditEndedEventArgs e)
{
  if (e.EditAction == DataFormEditAction.Commit)
      PeopleDataSource.SubmitChanges();
}

this is the very simplest case.  Calling SubmitChanges() here will send the edits/inserts up to the server right away.  For the sake of bandwidth, etc. I might want to implement my own “Save” button that batches up a whole set of change to the server rather than committing each edit individually.  You would still call PeopleDataSource.SubmitChanges, but not in response to the DataForm’s events. 

One of the great things about the way this works on the client side is that way the data validation attributes we set on the server-side POCO objects get propagated to the client.  For example, the server side LastName property looks like this (at least in the metadata class…)

[Required]
[RegularExpression("[a-zA-z]*")]
public string LastName;

The property that gets generated on the client side is

[DataMember()]
[RegularExpression("[a-zA-z]*")]
[Required()]
public string LastName
{
  get
  {
      return this._lastName;
  }
  set
  {
      if ((this._lastName != value))
      {
          this.ValidateProperty("LastName", value);
          this.RaiseDataMemberChanging("LastName");
          this._lastName = value;
          this.RaiseDataMemberChanged("LastName");
      }
  }
}

It maintains the [Required] and [RegularExpression] attributes.  Plus, in the property setter, it calls ValidateProperty, which uses reflection to examine those attributes and throw validation exceptions if necessary.  By default, then, I get UI on the Silverlight client for validation.

image

The DataForm provides the UI around both the field in question and the summary at the bottom.  In this case, I probably don’t want to tell the user what RegEx I’m validating against, so I need to add the ErrorMessage property to the validation attribute

[Required]
[RegularExpression("[a-zA-z]*", ErrorMessage="Alpha characters only please!")]
public string LastName;

image

When that error is corrected and I press the Save button, the Custom Validation routine is applied (see last post for details)

image 

When all the errors are corrected, and the save happens (and gets committed back to the server) the DataForm is updated to show the correct ID value (from the server) as well as the calculated Age property.

image

For next to no work, I get a pretty good user experience for browsing, editing, inserting and deleting records.  If I wanted a bit more control, I could load the data into the DataContext myself, and then setup the databinding, rather than using the DomainDataSource in XAML

private PeopleDomainContext _context = new PeopleDomainContext();

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
  if (_context.Persons.Count == 0)
  {
      _context.Loaded += (s, arg) => loadPeople();
      _context.LoadPersons();
  }
  else
      loadPeople();
}

private void loadPeople()
{
  dfPeople.ItemsSource = _context.Persons;
}

This would give me an opportunity to only load some people based on parameters, or whatever else I wanted to do to affect the loading of data before the data binding happens. 

Next time… more cool things you can do with the DomainDataContext

Friday, June 05, 2009 2:47:59 PM (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
Comments are closed.