Using Entity Framework POCOs in WCF Services

Create POCO classes and enhance them for use in a Windows Communication Foundation service

Following is an edited excerpt from Julia Lerman's book, Programming Entity Framework, Second Edition (O'Reilly Media). The excerpt comes from Chapter 18: Using POCO Entities and Self-Tracking Entities in WCF Services.

A common approach to building services in .NET 3.5 was to use Data Transfer Objects (DTOs) to carry an object's data between the client and the service. This approach provided some benefits, but converting the EntityObjects to DTOs and back again was time consuming. However, now that EF supports POCO classes, the need for using DTOs to carry an object's data between the client and the service is greatly reduced. You can do away with DTOs completely if you want, although your architecture may require them for different reasons unrelated to the EF. Without the EntityObject, the message is much smaller and much less complex, and you can use your code-generation template to inject state properties into your classes.

In addition to using your own Plain Old CLR Objects (POCO) classes in WCF services, Microsoft provides a specialized POCO template that creates what are called self-tracking entities. This template creates enhanced POCO objects, which include state properties and some other specialized interfaces and functionality that allow state information to easily move between the client and the server without the author of either the service or the client application having to work out the logic of maintaining state information. In this article, I'll explain how to create POCO classes based on a model, apply some enhancements to these POCO classes to make them friendlier for use in WCF services, then build a service that makes use of these POCO classes.

Creating WCF-Friendly POCO Classes
Before creating the services, you'll need an appropriate set of POCO classes to work with. Therefore, in this section you will walk through the following tasks:

  • You'll build a model based on a sample database, then switch its code generation to use a Microsoft-created code-generation template that builds POCO classes rather than EntityObjects.
  • You'll create a simple base class to provide state information to the entities and modify the T4 template so that the entities automatically inherit from that class.
  • You'll modify the T4 template one more time to remove the virtual keywords from the generated entity properties. This will prevent EF from creating dynamic proxies at runtime, helping you to avoid problems as entities are being serialized as they're sent from the service to the client.

Creating a model from the sample BreakAway database. To get to the fun part, you're going to have to fast forward through the creation of the model and its T4 template. The sample database is included in the  sample code for this article, which you can download at the Programming Entity Framework downloads page.

In a .NET Class Library Project, create a new ADO.NET Entity Data Model pointing to this database and bring in all of the database's tables. You'll end up with a model that contains Contact, Customer, Reservation, Trip and Destination entities. In the book, readers will have modified the model to make Customer inherit from Contact. I won't have you bother with that for the sake of this article, but be prepared to see a few references to this inheritance as you read on.

Once the model is created, add a code-generation item using the ADO.NET C# or VB POCO template that you can install via the Visual Studio 2010 Extension manager. An additional step in the book walks through ensuring that the POCO classes are in their own project, which is better architecture, but not critical to what follows in this article.

Adding custom logic to the POCO entities with a base class. The next step for preparing your entities for WCF involves providing them with some critical functionality. One of the biggest challenges when working with entities across processes is the loss of state information. In Chapter 17, you created explicit operations for inserting, updating, and deleting customers. For handling the reservations attached to a customer, you had to make assumptions regarding whether a reservation was new by checking if the ReservationID was equal to 0 (new) or was greater than 0 (existing). Then, to handle deleted reservations, you created a somewhat kludgey solution by forcing the consumer to pass in a collection of the ReservationID values of each reservation to be deleted.

With the simple addition of a new state property to the entities themselves, you can avoid all this unsettling code. This new state property will have no dependence on the state information that is managed by the context. You'll have access to it in the client application and have total control over its value.

Although we could modify the template yet again to insert the new property, a more flexible solution is to create a class with state information that the entities can inherit from. If you need to add additional state logic in the future, you can simply add it to this base class, and it will be inherited by the entities. Creating your own base classes to provide additional logic to your POCO classes remains in line with the goal of ensuring that your classes are not tightly bound to or dependent on the EF.

The StateObject class provides a State property that each entity will inherit. I've chosen to hand-code this class, but you can add code into your T4 template to have it automatically generated. The DataContract and DataMember attributes allow the object and property to be serialized by WCF.

I've created a separate project to contain the StateObject class, so that I can reuse it in other applications. Figure 1 shows the class, which we'll enhance further a bit later.

Modifying the template to apply the inheritance. The chapter walks readers through modifying the POCO template so that the entities inherit from the new StateObject class. An additional modification is made so that the entity properties do not have the virtual keyword applied. This prevents both lazy loading and the creation of runtime dynamic proxies that help the POCOs notify the ObjectContext about changes. Both the lazy loading and dynamic proxies create problems for the serialization that occurs as results are returned from a service. I choose to do without both behaviors rather than have to enable and disable them as needed in my code.

Another modification to the template alters the collection type used for navigation properties. Without the modification, the default ICollection will be deserialized as an immutable array, creating problems when you need to add or remove items from that collection. The modified template is part of the sample code that you can download for this article. You can read more details about the problems being solved and exactly how to make the modifications to the template in the book.

Building a WCF Service That Uses POCO Classes
In Chapter 17, you built a WCF service to allow consuming applications to interact with Customers, Trips, and Reservations. Here we'll build a service that allows consuming applications to interact with Customers, Trips, and Reservations, designed to use the POCO entities you just created.

Begin by creating a new WCF Service Application project, and rename the service interface to ICustomerService and the service class to CustomerService. Our entities contain a State property, so we can use a single SaveCustomer operation, which will take a Customer type whether it is a sole entity or a graph that includes Reservations and more. Figure 2 lists the operations for the new ICustomerService.

Implementing the interface. Now you can implement this interface in the CustomerService class. Begin by using the Visual Studio editor's interface-generation capability, which allows you to automatically create the various methods defined in the interface.

At this point, you can fill in the logic for the various methods. Figure 3 lists the three query operations—GetCustomerPickList, GetUpcomingTrips, and GetCustomer—with their logic added. Since you are no longer using the dynamic proxies with your entities (because we prevented the template from making the properties virtual), there is no need to disable lazy loading. Lazy loading works only when the navigation properties are virtual.

Now you can add code to the SaveCustomer method. Let's first take a look at what needs to go in the method. Although each entity will have its State field populated by the consuming app, that property will not allow SaveChanges to build the appropriate database commands. You will need to add the incoming entity to the context, then set the EntityState property to the correct state in order for SaveChanges to do its work.

An explicit approach would be to use a switch statement to modify the EntityState based on State:

switch (customer.State)
\\{
  case State.Modified:
    context.ObjectStateManager.
      ChangeObjectState(cust,System.Data.EntityState.Modified);
 ...
\\}

A nicer approach was used by Rowan Miller, from the Entity Framework team, in his June 2009 blog post, where he encapsulates the switch statement into a StateObject helper method within a static class, as shown in Figure 4.

Figure 4: A method for replacing the POCO entity's State property with the relevant EntityState

 

public static class StateHelpers
\\{
  public static EntityState GetEquivalentEntityState(State state)
  \\{
    switch (state)
    \\{
      case State.Added:
        return EntityState.Added;
      case State.Modified:
        return EntityState.Modified;
      case State.Deleted:
        return EntityState.Deleted;
      default:
        return EntityState.Unchanged;
    \\}
  \\}
\\}

I've added this StateHelpers class to the project that contains the StateObject and I suggest that you do the same. This lets you keep the state logic code out of your service and simply call the method like this:

 

context.ObjectStateManager.ChangeObjectState(customer,
  StateHelpers.GetEquivalentEntityState(customer.State));

In the SaveCustomer method, when iterating through the Reservations, remember that they will already be attached to the context because you've attached the customer graph of which they are a part. But you'll still need to change the EntityState of each Reservation based on its State property. Figure 5 lists the SaveCustomer method in its entirety.

There is a possibility that a reservation being deleted might have payments in the database that you may not want to lose. In a production app, you'll likely want some additional code to ensure that reservations with payments are handled according to your business rules when the client has requested that they be deleted.

Using the Service
Now it's time to see the service we created in action. Figure 6 lists a GetandUpdateCustomer method in a console application that has a service reference to the WCF service. The method interacts with the service and emulates a user modifying some of the data, then passes the data back to the service to persist the database.

The interesting events happen in the service's SaveCustomer method. When you debug through that, you can watch the EntityState of the objects being modified, and finally, when profiling the database you can see the resultant activity when SaveChanges is called.

In Figure 7, you can see that three update commands are related to the modification of the customer.

 

Figure 7: Database commands generated by the SaveCustomer method
Figure 7: Database commands generated by the SaveCustomer method

 

Why three? Changing the object's EntityState to Modified renders every property as modified. The Customer inherits from Contact and maps to Customer and ContactDetails. EF is updating all properties in all three tables.

The next update command is updating the reservation that was marked as Modified. Then you see the delete command being executed for the reservation we deleted, and finally the new reservation is added. All the modifications we made in the client application were easily recognized, thanks to the simplicity of including a State property in our classes.

Additionally, the performance over the wire is greatly improved, thanks to the minimized payloads of serializing, transmitting, and deserializing data that is much smaller because we are using POCO classes and not EntityObjects. Another benefit is realized by consuming applications that are not using .NET. These developers will be much happier to work with the simple data structures than having to comb through the payload generated by an EntityObject.

Julia Lerman ([email protected]) is a Microsoft MVP, .NET mentor, and consultant. Julie presents on data access and other topics at user groups and conferences around the world, blogs at thedatafarm.com/blog, and is the author of Programming Entity Framework (O’Reilly). Follow Julie on Twitter at julielermanvt.

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish