I made the fairly bold statement at my PDC09 talk that a DomainService IS A WCF Service.  That is, everything you know about a WCF service should be true of a DomainService.  I didn’t have time to get into this in my talk, so I thought I’d hit the highlights here.  And in the process show how to consume a DomainService from a WinForms.    You can also see more examples at: http://code.msdn.microsoft.com/RiaServices 

You need:

 

You can download the completed solution as well.  and be sure to check out the full talk

 

1. Getting to the Service

The first thing we need to do is get at the data underlying service.  In the mainstream Silverlight case this is all handled for you by the implicit link between the Silverlight client and the ASP.NET server.  However, in the vanilla WCF case, you get the full control.  The URL to the service is of the following format:

http://[hostname]/[namespacename]-[classname].svc

so in my case that is:

http://localhost:30335/Services/MyApp-Web-DishViewDomainService.svc

Hitting that URL in the browser gives you the very familiar WCF proxy help screen:

image

And tacking on the ?wsdl gives you the WSDL for this service

http://localhost:30335/Services/MyApp-Web-DishViewDomainService.svc?wsdl

 

image

 

The rest is easy for anyone halfway familiar with WCF… Create a new WinForms project and select Add Service Reference.  Enter the URL (note discover doesn’t work for this sort of service yet)…

image

The you have a service! 

 

 

2. Querying for the Data

Now, we have a service, let’s look at actually getting data out of it.  In this case I already have a WinForms DataGridView on my form.  So getting data into it should be no problem. 

  1. private void Form1_Load(object sender, EventArgs e)
  2. {
  3.     var context = new DishViewDomainServiceClient("BasicHttpBinding_DishViewDomainService");
  4.     var plates = context.GetPlates(4);
  5.     this.dataGridView1.DataSource = plates.RootResults;
  6.     foreach (DataGridViewRow row in dataGridView1.Rows)
  7.     {
  8.         PlatesListOriginals.Add(ToPlate(row));
  9.     }
  10.     dataGridView1.CellEndEdit += dataGridView1_CellEndEdit;
  11.     dataGridView1.SelectionChanged += dataGridView1_SelectionChanged;
  12. }
  13.  

In line 3, we create a new instance of the web service client and point it at the right binding.    The service exposes a couple of different bindings as you can see in the app.config file for the WinForms app:

  1. <endpoint address="http://localhost:30335/Services/MyApp-Web-DishViewDomainService.svc/soap"
  2.     binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_DishViewDomainService"
  3.     contract="ServiceReference1.DishViewDomainService" name="BasicHttpBinding_DishViewDomainService" />
  4. <endpoint address="http://localhost:30335/Services/MyApp-Web-DishViewDomainService.svc/binary"
  5.     binding="customBinding" bindingConfiguration="BinaryHttpBinding_DishViewDomainService"
  6.     contract="ServiceReference1.DishViewDomainService" name="BinaryHttpBinding_DishViewDomainService" />
  7.  

In line 4, we call the service to get our list of Plates… in this case we are doing things synchronously.. you could of course do it async if you’d like.

In line  5, we bind the DataGridView to the results of this call.

In lines 6-9, we are saving off the “original” values..  for each item we got.. this will help us when we do updates.

In line 10, we handle the cell edit event, we will come back to look at that later.

in line 11, we sign up for the selection changed event so we can initialize the picture…

  1. void dataGridView1_SelectionChanged(object sender, EventArgs e)
  2. {
  3.      Plate currentPlate = ToPlate(dataGridView1.CurrentRow);
  4.      this.pictureBox1.ImageLocation = "http://hanselman.com/abrams/Images/Plates/" + currentPlate.ImagePath;
  5. }
  6.  

Be patient with this one… sometimes it takes a while load a picture.  it is using hanselman’s server which gets slammed sometimes ;-)

image

Now we have our data, we can scroll through it and view the pretty pictures. 

 

3. Updating the Data

But how do we update the data… well, let’s take a look at CellEditEnd event handler…

  1. void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
  2. {
  3.     var context = new DishViewDomainServiceClient("BasicHttpBinding_DishViewDomainService");
  4.     Plate currentPlate = ToPlate(dataGridView1.Rows[e.RowIndex]);
  5.     ChangeSetEntry[] changeSet = new[] {
  6.         new ChangeSetEntry{
  7.          OriginalEntity = PlatesListOriginals[e.RowIndex],   
  8.          Entity = currentPlate,
  9.          Operation = DomainOperation.Update
  10.         }
  11.     };
  12.     context.SubmitChanges(changeSet);
  13. }
  14.  

 

In line 3, we are creating a new context.  we could be sharing with the load method, but I thought this would be cleaner to follow.

In line 4, we save off the currently selected plate.

In lines 5-10 we are building up a changeset to send to the server. 

Notice we need to give it the original values we saved off in the load method.    Getting the original values right is the likely the hardest part here.  Keep in mind that assignment in C# (and VB) is by default by reference.  So you can’t just store off a reference, you must actually make a copy of the original values.

Then in line 12, we submit the changes. 

Make a change, tab off it..  This will call the server and post your update.   Re run the app to see that it took.

image

Notice here we are sending one item in the change set.  You could of course build up a change set on the client with many entries and then send them as a batch. 

 

I hope that helps to make it clear how a DomainService IS A WCF Service… You can download the completed solution as well.  and be sure to check out the full talk