I’ve spent the last couple weeks wrestling with WCF4 and Silverlight4.

Here is some code that does a GET/POST/PUT/DELETE with a Silverlight4 client.

The Restful WCF Service

    [ServiceContract]
    public interface IInvoiceService
    {
        [WebGet(UriTemplate = "/invoices")]
        [OperationContract]
        List GetAllInvoices();

        [WebGet(UriTemplate = "/invoices/{invoiceId}")]
        [OperationContract]
        Invoice GetInvoice(string invoiceId);

        [WebInvoke(UriTemplate = "/invoices/{invoiceId}", Method = "PUT")]
        [OperationContract]
        Invoice UpdateInvoice(string invoiceId, Invoice newInvoice);

        [WebInvoke(UriTemplate = "/invoices", Method = "POST")]
        [OperationContract]
        Invoice AddNewInvoice(Invoice newInvoice);

        [WebInvoke(UriTemplate = "/invoices/{invoiceId}", Method = "DELETE")]
        [OperationContract]
        void DeleteInvoice(string invoiceId);
    }

Here is the implementation:

  public class InvoiceService : IInvoiceService
    {
        protected static List<Invoice> invoices = new List<Invoice>
        {
            new Invoice(1, "reciever1"),
            new Invoice(2, "reciever2"),
            new Invoice(3, "reciever3"),
        };

        public List<Invoice> GetAllInvoices()
        {
            return invoices;
        }

        public Invoice GetInvoice(string invoiceId)
        {
            Invoice invoice = invoices.Find(x => x.Id == int.Parse(invoiceId));

            if (invoice == null)
            {
                WebOperationContext ctx = WebOperationContext.Current;
                ctx.OutgoingResponse.StatusCode = HttpStatusCode.NotFound;
            }

            return invoice;
        }

        public Invoice UpdateInvoice(string invoiceId, Invoice newInvoice)
        {
            Invoice current = invoices.Find(x => x.Id == int.Parse(invoiceId));

            if (current == null)
            {
                WebOperationContext ctx = WebOperationContext.Current;
                ctx.OutgoingResponse.StatusCode = HttpStatusCode.NotFound;
                return null;
            }

            invoices.Remove(current);
            invoices.Add(newInvoice);

            return newInvoice;
        }

        public Invoice AddNewInvoice(Invoice newInvoice)
        {
            Debug.WriteLine("AddNewInvoice");

            Invoice existingInvoice = invoices.Find(x => x.Id == newInvoice.Id);
            WebOperationContext ctx = WebOperationContext.Current;

            if (existingInvoice != null)
            {
                ctx.OutgoingResponse.StatusCode = HttpStatusCode.Conflict;
                return null;
            }

            invoices.Add(newInvoice);

            ctx.OutgoingResponse.StatusCode = HttpStatusCode.Created;
            ctx.OutgoingResponse.Location = PluginFactory.INSTANCE.InvoiceServiceUri() + newInvoice.Id;

            return new Invoice(newInvoice.Id, newInvoice.Receiver);
        }

        public void DeleteInvoice(string invoiceId)
        {
            var id = Convert.ToInt32(invoiceId);
            invoices = invoices.FindAll(invoice => invoice.Id != id);
        }
    }

The Silverlight Client

You have two options when consuming services on the client. You can use WebClient (which is easy but weak). Or you can use the more rich HttpWebRequest which you need to consume Status Codes. This example users the later. Here is the Silverlight client.

GET

        private void GetClick(object sender, RoutedEventArgs e)
        {
            var request = (HttpWebRequest)WebRequest.Create(url + IdTextBox.Text);
            request.BeginGetResponse(GetCompleted, request);
        }

        private void GetCompleted(IAsyncResult ar)
        {
            // necessary for concurrency
            Dispatcher.BeginInvoke(delegate
            {
                try
                {
                    var request = (HttpWebRequest)ar.AsyncState;
                    var response = (HttpWebResponse)request.EndGetResponse(ar);

                    using (var streamReader = new StreamReader(response.GetResponseStream()))
                    {
                        ParseComplex(response, streamReader);
                    }
                }
                catch (WebException)
                {
                    outputTextBlock.Text = "This id does not exist.";
                }
            });
        }

POST

private void PostComplexClick(object sender, RoutedEventArgs e)
        {
            ClearTextBoxes();

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(postUrl);
            request.Method = "POST";
            request.ContentType = postContentType;
            request.BeginGetRequestStream(PostComplexRequestReady, request);
        }

        private void PostComplexRequestReady(IAsyncResult asyncResult)
        {
            HttpWebRequest request = asyncResult.AsyncState as HttpWebRequest;
            Stream postStream = request.EndGetRequestStream(asyncResult);

            Dispatcher.BeginInvoke(delegate
            {
                Invoice newInvoice = new Invoice(int.Parse(IdTextBox.Text), RandomReceiver());
                byte[] byteData = Encoding.UTF8.GetBytes(newInvoice.AsXml());
                postStream.Write(byteData, 0, byteData.Length);
                postStream.Flush();
                postStream.Close();

                request.BeginGetResponse(PostComplexResponseReady, request);
            });
        }

        private void PostComplexResponseReady(IAsyncResult asyncResult)
        {
            Dispatcher.BeginInvoke(delegate
            {
                try
                {
                    var request = asyncResult.AsyncState as HttpWebRequest;
                    var response = (HttpWebResponse)request.EndGetResponse(asyncResult);
                    using (var streamReader = new StreamReader(response.GetResponseStream()))
                    {
                        ParseComplex(response, streamReader);
                    }
                }
                catch (WebException e)
                {
                    dynamic exceptionResponse = e.Response;
                    if (exceptionResponse.StatusCode == HttpStatusCode.Conflict)
                        outputTextBlock.Text = "This user already exists.";
                    else
                        outputTextBlock.Text = e.Message;
                }
            });

        }

PUT

  private void PutClick(object sender, RoutedEventArgs e)
        {
            ClearTextBoxes();

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url + IdTextBox.Text);
            request.Method = "PUT";
            request.ContentType = postContentType;
            request.BeginGetRequestStream(PostComplexRequestReady, request);

            // Note: PUT is very similar to POST - only difference is url
        }

DELETE

   private void DeleteClick(object sender, RoutedEventArgs e)
        {
            ClearTextBoxes();

            var request = (HttpWebRequest)WebRequest.Create(url + IdTextBox.Text);
            request.Method = "DELETE";
            request.BeginGetResponse(GetCompleted, request);

            // Note: DELETE is very similar to GET
        }