Creating and Consuming Windows Communication Foundation (WCF) Data Services

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Introduction

Add a new ADO.NET Entity Data Model to your web application

Just to ensure that the data service is working correctly,
run the .svc file in the browser. Your browser window should resemble the
following figure.

Run the .svc file in the browser
Figure 4: Run the .svc file in the browser

Consuming a WCF Data Service

Now that you are ready with the data service, let’s create a
client application that will consume the service. The client application can be
a desktop application or another web application. For our example we will
create an ASP.NET web application and then consume the WCF data service. So,
add another ASP.NET web application to the same solution. Add a reference to
the WCF data service using "Add Service Reference" dialog.

Add Service Reference
Figure 5: Add Service Reference

Click on the Discover button so that Visual Studio will find
data services from the current solution. Change the namespace name if you wish
and click OK. This will add a service reference to EmployeeService as shown
below:

Add a service reference to EmployeeService
Figure 6: Add a service reference to EmployeeService

Open the default web form, import ServiceReference1
namespace and place a GridView control on it. Then add the following code in
Page_Load event handler.

protected void Page_Load(object sender, EventArgs e)
{
    Uri svcUri=new Uri("http://localhost:1031/EmployeeService.svc");
 
    NorthwindEntities db = new NorthwindEntities(svcUri);
 
    GridView1.DataSource = db.Employees;
    GridView1.DataBind();
}

The code creates an instance of Uri class by passing the URL
where the EmployeeService is located. In a more real world situation you will
store this URL in the web.config file for obvious reasons. The code then
instantiates NorthwindEntities and binds Employees to the GridView. If you run
the web form it should resemble the following figure.

Run the web form
Figure 7: Run the web form

The Employees property used in the code above is actually a
DataServiceQuery and represents a query being sent to the WCF data service. You
could have also written the above code in the following way:

DataServiceQuery<Employee> query = (DataServiceQuery<Employee>)
        from emp in db.Employees
        select emp;
 
QueryOperationResponse<Employee> items = (QueryOperationResponse<Employee>)query.Execute();
 
GridView1.DataSource = items;
GridView1.DataBind();

Here you created DataServiceQuery instance explicitly and then
specified a query (from…select…) that retrieves required employees (all in the
above code). The Execute() method of DataServiceQuery object executes the query
and returns the results as QueryOperationResponse object. The items retrieved
as the response are then bound with the GridView.

Executing Queries Asynchronously

The code that you examined in the preceding section uses
Execute() method to execute the query against the WCF data service. This
execution is synchronous in nature. In some cases you may want to execute the
queries in asynchronous fashion. To do so WCF data services follow the standard
BeginXXXX and EndXXXX pattern of .NET Framework. The following code will make
the usage of this pattern clear.

...
DataServiceQuery<Employee> query = (DataServiceQuery<Employee>)from emp in db.Employees
        select emp;
 
try
{
    query.BeginExecute(OnQueryComplete, query);
    System.Threading.Thread.Sleep(5000);
}
catch (DataServiceQueryException ex)
{
    throw new ApplicationException("An error occurred while executing query", ex);
}
...

Here, the DataServiceQuery instance defines the query to be
executed as before. To call the query, however, you use the BeginExecute()
method. The BeginExecute() method accepts a callback method that will be called
after the query completion and the DataServiceQuery instance. The code
deliberately puts a delay of 5 seconds for the sake of testing. The
OnQueryComplete callback method looks like this:

private void OnQueryComplete(IAsyncResult result)
{
    DataServiceQuery<Employee> query = result.AsyncState as DataServiceQuery<Employee>;
    QueryOperationResponse<Employee> items = (QueryOperationResponse<Employee>)query.EndExecute(result);
    GridView1.DataSource = items;
    GridView1.DataBind();
}

The OnQueryComplete method receives IAsyncResult object as a
parameter. The AsyncState property of the IAsyncResult object gives the
original DataServiceQuery instance (the one on which you invoked the
BeginExecute() method). We then call the EndExecute() method to complete the
operation and retrieve the results as a QueryOperationResponse. The response is
bound with the GridView as in the previous case.

Executing Paged Queries

In the above examples, executing a query returned all the
Employee entities in the response. If your query is dealing with a large number
of entities you may want to restrict the number of entities returned at a time
in the response. Luckily, WCF data services allow you to page the data returned
from the queries. By default, paging is disabled but you can enable it in the
InitializeService() method as shown below:

public static void InitializeService(DataServiceConfiguration config)
{
    ...
    config.SetEntitySetPageSize("Employees", 3);
    ...
}

The SetEntitySetPageSize() method allows you to set the page
size of an entity set. Setting page size will cause only the specified number
of items to be returned in the response at a time. Further items can be
retrieved as and when required. The following code fragment illustrates how
multiple pages can be retrieved in the client application using the technique
just mentioned.

Uri svcUri=new Uri("http://localhost:1031/EmployeeService.svc");
 
NorthwindEntities db = new NorthwindEntities(svcUri);
DataServiceQueryContinuation<Employee> pageFlag = null;
DataServiceQuery<Employee> query = (DataServiceQuery<Employee>)from emp in db.Employees
        select emp;
QueryOperationResponse<Employee> items = (QueryOperationResponse<Employee>)query.Execute();
do
{
    if (pageFlag != null)
    {
        items = db.Execute<Employee>(pageFlag) as QueryOperationResponse<Employee>;
        Response.Write("<hr>");
    }
 
    foreach (Employee emp in items)
    {
        Response.Write(emp.FirstName + " " + emp.LastName + "<br>");
    }
}
while ((pageFlag = items.GetContinuation()) != null);

The code declares an instance of
DataServiceQueryContinuation class. The DataServiceQueryContinuation class
encapsulates a URI that returns the next page of a paged query result. When you
call the Execute() method on the DataServiceQuery object it will return only 3
records since we have configured the page size to be 3. A do…while loop
iterates through the returned items and outputs them on the response stream.
Notice the while condition. We call the GetContinuation() method that returns
an instance of DataServiceQueryContinuation (if applicable). Accordingly we
output <HR> element to indicate a page of data. The following figure
shows a sample run of the web form:

A sample run of the web form
Figure 8: A sample run of the web form

Modifying Data from Client Application

For the sake of completeness let’s add some code in the
client application that allows us to update and delete employees. This code
goes primarily in the RowUpdating and RowDeleting event handlers of the
GridView control. Have a look at the following code fragments:

protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
    ...
    var query = (from emp in db.Employees
                where emp.EmployeeID == Convert.ToInt32(e.Keys[0])
                select emp).Single();
    query.FirstName = e.NewValues[0].ToString();
    query.LastName = e.NewValues[1].ToString();
    db.UpdateObject(query);
    db.SaveChanges();
    ...
}
protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
    ...
    var query = (from emp in db.Employees
                    where emp.EmployeeID == Convert.ToInt32(e.Keys[0])
                    select emp).Single();
 
    db.DeleteObject(query);
    db.SaveChanges();
    ...
}

The RowUpdating event handler performs the update operation
on the data service. It does so by first filtering the employee to be modified
and then calling the UpdateObject() method with the modified Employee instance.
The SaveChanges() method actually persists the changes to the database.
Similarly, the RowDeleting event handler uses DeleteObject() method to delete
an Employee from the database.

Though the above example doesn’t insert any new employees,
doing so is easy as illustrated below:

Employee emp = new Employee();
emp.FirstName = "Tom";
emp.LastName = "Jerry";
db.AddToEmployees(emp);
db.SaveChanges();

The above code creates a new Employee entity instance and
sets its properties. The Employee is then added to the entity set using the
AddToEmployees() method.

Summary

WCF data services make use of REST style communication
coupled with OData protocol. They allow you to expose data over a network that
can be manipulated using HTTP verbs such as GET, PUT, POST and DELETE. WCF data
services make the development easy by leveraging Entity Framework data models.
WCF data services also provide client libraries that allow object based access
to the underlying data service.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read