Skip to main content

Code First and WCF Data Services

Julie Lerman

http://thedatafarm.com

Published: Octoberl 2011

Download the code for this article


Microsoft’s RESTful Data Services, WCF Data Services, were built on top of Entity Framework and by default, have a reliance on an Entity Data Model. Entity Framework 4.1 Code First gives us a new way to create a model, simply by defining classes. You can create WCF Data Services based on a Code First model just as easily as if you were using a model based on and visual Entity Data Model, i.e. an EDMX file.

In this article, I’ll show you how to build WCF Data Services using Code First and a model for a modest customer sales application consisting of 10 classes. Once the service has been created, I’ll then consume a portion of the data in a Windows Phone 7 application.

The final solution (which you can download at the top of this page) consists of four projects plus some automated tests used along the way.


Figure 1

CustomerOrderModel contains the domain classes — Customer, Order, LineItem, Product and others.

DataLayer contains the Entity Framework 4.1 code that uses Code First to create a conceptual Entity Data Model from the domain classes and provide database interaction as well as other features of Entity Framework.

CustomerDataServices contains the WCF Data Service that exposes the customer data on the web as OData.

WindowsPhone7App contains a small application that uses the OData Client to consume the Customer Data Services for reading and writing. The application exposes some customer information for reading and writing and lets a customer view their orders along with the line items for each order.

Introducing the Domain – the Model Classes

The model classes are fairly simple for the sake of demonstrating the concepts.  Here are the classes that will be used in the WP7 application.

public class Customer: DateClass
{
  public Customer()
  {
    _addresses = new List<Address>();
    _orders = new List<Order>();
  }

  public int CustomerId { get; set; }
  public string Title { get; set; }
  public string FirstName { get; set; }
  public string MiddleName { get; set; }
  public string LastName { get; set; }
  public string CompanyName { get; set; }
  public string EmailAddress { get; set; }
  public string Phone { get; set; }
  public byte[] TimeStamp { get; set; }
  public string FullName
  {
    get {return FirstName + " " + LastName; }
  }
  public string FullNameReverse
  {
    get {return LastName + ", " + FirstName;}
  }
  private ICollection<Address> _addresses;
  public virtual ICollection<Address> Addresses
  {
    get { return _addresses; }
    set { _addresses = value; }
  }

  private ICollection<Order> _orders;
  public virtual ICollection<Order> Orders
  {
    get { return _orders; }
    set { _orders = value; }
  }
}

public class Order :DateClass
{
  public Order()
  {
    _lineItems = new List<LineItem>();
  }

  public int OrderId { get; set; }
  public DateTime OrderDate { get; set; }
  public Nullable<DateTime> DueDate { get; set; }
  public string SalesOrderNumber { get; set; }
  public string PurchaseOrderNumber { get; set; }
  public int CustomerId { get; set; }
  public Nullable<int> BillToAddressId { get; set; }
  public decimal SubTotal { get; set; }
  public string Comment { get; set; }
  public Nullable<DateTime> ShipDate { get; set; }
  public Customer Customer { get; set; }

  private ICollection<LineItem> _lineItems;
  public virtual ICollection<LineItem> LineItems
  {
    get { return _lineItems; }
    set { _lineItems = value; }
  }
}

using System;
using System.Collections.Generic;

public class LineItem : DateClass
{
  public int OrderId { get; set; }
  public int LineItemId { get; set; }
  public int OrderQty { get; set; }
  public int ProductId { get; set; }
  public decimal UnitPrice { get; set; }
  public Nullable<decimal> UnitPriceDiscount { get; set; }
  public decimal LineTotal
  {
    get
    {
      if (UnitPriceDiscount==null)
      {
        return (OrderQty*UnitPrice);
      }
      return (decimal) (OrderQty * (UnitPrice - UnitPriceDiscount));
    }
  }
  public virtual Product Product { get; set; }
  public Order Order { get; set; }
}

Notice that all three of these classes inherit from DateClass. DateClass provides two more properties to each class.

using System;

namespace CustomerOrder
{
  public class DateClass
  {
    public DateTime ModifiedDate { get; set; }
    public DateTime AddedDate { get; set; }
  }
}

Below you’ll see how ModifiedDate and AddedDate are automatically populated as needed.

The Data Layer – Using Entity Framework Code First

The DataLayer project consists of the CustomerOrderContext class which inherits from the Entity Framework 4.1 DbContext. The DbContext class is what brings the Entity Framework features on board. It is what gives you the ability to write LINQ to Entities queries that will be executed on the database and return populated objects. The DbContext provides change tracking and the ability to update data in the database. The DbContext uses EF 4.1 DbSets to manage particular classes. CustomerOrderContext contains DbSets for most of the classes defined in the model.

public class CustomerOrderContext : DbContext
  {

    public DbSet<Address> Addresses { get; set; }
    public DbSet<Customer> Customers { get; set; }

    public DbSet<Product> Products { get; set; }
    public DbSet<Category> ProductCategories { get; set; }
    public DbSet<ProductDescription> ProductDescriptions { get; set; }
    public DbSet<ProductModel> ProductModels { get; set; }
    public DbSet<Order> Orders { get; set; }
    public DbSet<LineItem> LineItems { get; set; }
    public DbSet<CustomerLite> CustomerLites { get; set; }

}

The DataLayer has a few more jobs.  First, this is where we’ll be sure that the ModifiedDate and AddedDate properties are populated for the relevant classes before changes are sent to the database. You can do this by overriding the DbContext.SaveChanges method, finding the appropriate objects that are being tracked by DbContext and applying the current date and time. When all of that is finished, it’s necessary to call the base SaveChanges method which you can see in the last line of code below.

public override int SaveChanges()
    {
      foreach (var entry in ChangeTracker.Entries()
        .Where(e => e.Entity is DateClass &&
          (e.State == EntityState.Added | e.State == EntityState.Modified)))
      {
        var dateClass = (DateClass)entry.Entity;
        if (entry.State == EntityState.Added)
        {
          dateClass.AddedDate = DateTime.Now;
          dateClass.ModifiedDate = DateTime.Now;

        }
        if (entry.State == EntityState.Modified)
        {
          dateClass.ModifiedDate = DateTime.Now;
        }
      }
      return base.SaveChanges();
    }

Code First uses Convention over Configuration to reason out a conceptual model and how it maps to the database. It starts with Convention — that is it’s assumptions of your intent based on what it finds in the classes. You can tweak that using configurations and Code First provides two mechanisms for this: declarative Data Annotations and code based Fluent API configurations. In the DataLayer project, Fluent API mappings are being used to provide additional configuration information to Code First so that it will properly build the model and mappings from the domain classes.

As an example, here is one of the mappings that tells Code First the TimeStamp field in the customer class should be treated by Entity Framework as a RowVersion property.

public class CustomerMap : EntityTypeConfiguration<Customer>
	{
		public CustomerMap()
		{
			Property(t => t.TimeStamp).IsRowVersion();
		}
	}

This configuration also impacts the mapping to the database. If Code First is creating the database (SQL Server in this case), it will be sure that the column is a RowVersion type. If you are simply mapping to an existing database, the configuration says to expect a RowVersion type in the database.

There are a number of other mappings in the Data Layer. It’s not important to see them all but it is important to note that even when used through, the WCF Data Service, these mappings will be honored.

Code First needs to be aware of the mappings just after it’s read through the classes and is about to build the model based on its findings. DbContext exposes the OnModelCreating method where you can inject configurations for code first to take into account. Since the configurations are defined in separate EntityTypeConfiguration classes, they can simply be added to the model builder’s configurations in order to be used when building the model. This method is also in the CustomerOrderContext class.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
      modelBuilder.Configurations.Add(new OrderShippingMap());
      modelBuilder.Configurations.Add(new CustomerLiteMap());
    }

This new domain does not have a pre-existing database. Therefore, the data layer provides the ability advantage of the Code First feature of creating a database based on the database schema it infers after working out the model and mappings. This is achieved through Code First Database Initialization. You can add to the initialization logic by providing seed data. Here is a class in the DataLayer that inherits from one of the three database initialization strategies and then overrides the Seed method to insert some default data. This is a useful way to provide sample data while testing your application. This particular class inherits from the strategy that will only perform the initialization when the database does not yet exist or when you have made a change to one of the classes in the model. The seed method in the data layer has been written to insert three product categories, three products and one customer. The customer has two orders, each with two line items. You can examine this class in the download sample.

This code will be triggered even when the model is being consumed by the WCF Data Service.

The CustomerOrder Data Service

With the Data Layer in hand, you can now create a WCF Data Service which lives in its own project. The data service needs some type of web host. I prefer to create a WCF Service Application project to host my WCF Data Services then the first thing I do after creating the project is to delete the IService1.cs/vb and Service1.svc files.

Here are the steps for creating the data service in this project.

1.       Add New Item to the project

2.       Select WCF Data Service, which you can find in the Web items or by using the Search feature.

3.       Name the service CustomerDataService.svc.

Next is an important step. Visual Studio will add references to the .NET 4 versions of the System.Data.Services and System.Data.Services.Client assemblies for the data service. These are not aware of Entity Framework 4.1 and the new DbContext. You’ll need to replace them with updated versions, Microsoft.Data.Services and Microsoft.Data.Services.Client, that are part of the Microsoft WCF Data Services March 2011 CTP 2 at http://www.microsoft.com/downloads/en/details.aspx?FamilyID=60fb0117-8cea-4359-b392-6b04cdc821be. Keep in mind that this is a preview and is subject to change. You should be sure to get the version that is most current.

4.       Manually delete the references to System.Data.Services and System.Data.Services.Client and replace them with Microsoft.Data.Services and Microsoft.Data.Services.Client from the WCF Data Services March 2011 CTP 2.

5.  Add a reference to the DataLayer project. This will allow your data service aware of the CustomerOrderContext.

6.       Because you are referencing a newer version of the services API, you must update the version reference in the service code. In the last line of code change the MaxProtocolVersion from DataServiceProtocolVersion.V2 to DataServiceProtocolVersion.V3.

The default code in the data service class provides some place holders and commented out sample code.  The first is in the class declaration which leaves you a spot to identify the context that the data service is exposing.

7.       Replace the context placeholder with DataLayer.CustomerOrderContext.

By default, the service exposes none of your DbSets from the context. You must explicitly give the service permission to share your data using the SetEntitySetAccessRule method. You can define common rights for all of your sets or provide granular rules.

8.       Replace the commented SetEntitySetAccessRule example with

config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);

This will let any consumer read all of the data exposed from the list of DbSets defined in the CustomerOrderContext class.

We also want users to be able to modify Customer information as well as read it and that rule is called WriteMerge. Since this will override the Customers rule, you also need to repeat the AllRead access.

1.       Add the following rule for the Customers set:

config.SetEntitySetAccessRule("Customers",
 EntitySetRights.AllRead |EntitySetRights.WriteMerge);

Notice that the enums are combined using a bitwise operator. Consuming applications will be able to read and modify Customers.

Because the data layer is using the Code First feature to automatically discover the database, you do not need to provide a database connection string in the Data Service layer’s web.config. So you’re all set.

You can verify the service by browsing the CustomerOrderService.svc file in your web browser.


Figure 2

If you are using Code First’s Database Initialization, Code First creates an in-memory set called EdmMetadatas and a corresponding database table. You can add a configuration right to hide that from the data service.

Here’s the data service code after all of the changes:

using System.Data.Services;
using System.Data.Services.Common;
using DataLayer;

namespace CustomerDataServices
{
  public class CustomerDataService : DataService<CustomerOrderContext>
  {
    public static void InitializeService(DataServiceConfiguration config)
    {
      config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
      config.SetEntitySetAccessRule("Customers", 
        EntitySetRights.AllRead |EntitySetRights.WriteMerge);
      config.SetEntitySetAccessRule("EdmMetadatas", EntitySetRights.None);
      config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
    }
  }
}

The browser lets you execute queries directly with URLs. Verify that you can view the Customers data [CustomerDataService.svc/Customers] or even Customers with their Orders [CustomerDataService.svc/Customers?$expand=Orders] .

The format of the data returned by the service and its query syntax is referred to OData as it follows the OData specification.

Consuming the Service with a Windows Phone 7 Application

Once the OData service is available, you can consume it with any software platform that gives you the ability to make HTTP calls to the internet. You could make the calls using knowledge of raw HTTP and OData’s Uri query syntax. And there is a growing array of client libraries that make coding against OData a simple task. You can find libraries on the Consumers page of www.OData.org. The Windows Phone 7.1 SDK includes the OData library for Windows Phone 7 development, an adaptation of the OData .NET Client library with similar coding patterns. This sample application will leverage this WP7 client library.

You can find many great resources for learning WP7 Development at App Hub, the MSDN Developer Center for Windows Phone (create.msdn.com). The following will focus on the portions of the application that interact with the data service; but you can see the complete code in the sample download.

The sample presumes that a Customer has logged in and therefore only presents information for a single customer. The Customer can view and edit their key information, browse their orders and then clicking on an order, view the details of that order.


Figure 3

Using Model View View Model for UI Specific Classes

Since the app doesn’t need to work with all of the properties for any of these three classes, it leverages the Model View View Model  (MVVM) pattern where the UI interacts with local classes that are designed for the needs of the UI. These classes are referred to as View Models, i.e. models designed for the view. As shown in Figure 4, the application queries for the Customer, Order and Line Items classes from the service, then uses the properties of these results to populate the local classes. Then, the application screens bind to the local classes, not to the classes coming from the service.


Figure 4

Here are the CustomerViewModel and OrderViewModel classes which you can compare to the relevant classes listed above. While the Windows Phone 7 Application project template creates View Model classes that implement INotifyPropertyChanged, this sample doesn’t require property changed notifications, so doesn’t bother with the implementation.

public class CustomerViewModel
  {
    public int CustomerId { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }
  }
public class OrderViewModel
  {
    public int OrderId { get; set; }
    public string OrderNumber{ get; set; }
    public DateTime OrderDate{ get; set; }
    public decimal SubTotal{ get; set; }
  }

Using the Data Service from the Application

In order to query the Data Service, you’ll need to use Visual Studio’s Add Service Reference feature to add a reference and proxy classes to the WP7 application. Note that the Windows Phone 7 SDK did not provide the ability to use the Add Service Reference wizard — you were forced to use the command line tool. But the newer Windows Phone 7.1 SDK does let you use the wizard in Visual Studio.

Once the service reference exists, you’re application can work with the CustomerOrderContext class that the reference exposes. The Add Service Reference created a local version of this class for you and because it’s coming from WCF Data Services, the proxy class that you’ll code against inherits from the OData client library’s DataServiceContext and therefore allows you to interact with the service. Here is that generated class.

public partial class CustomerOrderContext : 
   global::System.Data.Services.Client.DataServiceContext

One last class, DataAccess, acts as an agent between the UI, the data service and the view model classes. DataAccess makes calls to the Data Service using the CustomerOrderContext , populates instances of the CustomerViewModel, OrderViewModel and ListViewModel classes with the results, and then exposes the data to the UI.

Figure 5 displays the critical elements of the WP7 application – the Service Reference to the CustomerOrder Data Service, the View Model classes and the Data Access class.


Figure 5

Data Access first instantiates a new instance of the CustomerOrderContext, passing in the Uri where the service can be found. CustomerOrderContext is the proxy class that inherits from DataServiceContext.

_context = new CustomerOrderContext(new Uri("http://localhost:18049/CustomerDataService.svc/"));

Next, DataAccess queries the service using a pair of asynchronous methods. The first,  GetCustomerAndOrders, builds the query, asking for a single customer along with that customer’s orders. In a production application, the customer will have logged in and the customer’s information would be used in the query to locate the correct customer. For the sake of the demo, any customer will do.

DataServiceContext.BeginExecute fires off the asynchronous query. This is the same pattern that you may be familiar with if you have used Silverlight with WCF Data Services and is not specific to the Windows Phone application.

private void GetCustomerAndOrders()
    {
      var qry = (DataServiceQuery<Customer>)(from c in _context.Customers.Expand("Orders") select c).Take(1);

      try
      {
        qry.BeginExecute(CustomersAndOrdersRetrieved, qry);
      }
      catch (DataServiceQueryException ex)
      {
        throw new ApplicationException(
            "An error occurred during query execution.", ex);
      }
    }

CustomersAndOrderRetrieved uses EndExecute to finalize the query and receive the results. The objects returned from the service query are the full Customer and Order objects that the service exposes. This is where the application switches from the large classes to the View Model classes. The code instantiates a new CustomerViewModel and populates it’s few properties with values from the Customer, then builds a list of OrderViewModel objects using property values of the Orders returned from the service.

private void CustomersAndOrdersRetrieved(IAsyncResult result)
    {
      try
      {
        var query =
            result.AsyncState as DataServiceQuery<Customer>;

        _currentCustomerEntity = query.EndExecute(result).FirstOrDefault() as Customer;


        this.Customer = new CustomerViewModel
        {
          CustomerId = _currentCustomerEntity.CustomerId,
          Email = _currentCustomerEntity.EmailAddress,
          FirstName = _currentCustomerEntity.FirstName,
          LastName = _currentCustomerEntity.LastName,
          Phone = _currentCustomerEntity.Phone
        };

        var orders = new List<OrderViewModel>();
        foreach (var order in _currentCustomerEntity.Orders)
        {
          orders.Add(new OrderViewModel
                            {
                              OrderId = order.OrderId,
                              OrderDate = order.OrderDate,
                              OrderNumber = order.SalesOrderNumber,
                              SubTotal = order.SubTotal
                            });
        }

        this.Orders = new ObservableCollection<OrderViewModel>(orders);

        NotifyPropertyChanged("Customer");
      }
      catch (Exception ex)
      {
        NotifyPropertyChanged("Error:" + ex.Message);
      }
      this.IsDataLoaded = true;
    }

DataAccess exposes the customer and the orders with public properties, Customer and Orders. Notice that CustomersAndOrderRetrieved sets these properties with the newly populated view model classes. There is one other step — the customer that came back from the data service is stored away in the _currentCustomerEntity variable. This will be used later when the user makes changes the customer and needs to save them back to the data store.

Finally Data Access calls an event, NotifyPropertyChanged, to alert the UI that the customer has been populated, so let’s now take a look at the critical pieces of the UI.

The panel that displays the customer information shown in Figure 3 contains four TextBlocks that bind to properties (FirstName, LastName, Email, and Phone) of DataAccess.Customer (a CustomerViewModel instance).

<StackPanel DataContext="{Binding Path=Customer}"
            Height="496" HorizontalAlignment="Left" Margin="6,6,0,0"
            Name="stackPanel1" VerticalAlignment="Top" Width="449" Grid.ColumnSpan="2">
    <TextBlock Height="30" Text="First Name" />
    <TextBox Height="74" Name="txtFirst"  Width="460" 
                         Text="{Binding Path=FirstName, Mode=TwoWay}"  />
    <TextBlock Height="30"  Text="Last Name" />
    <TextBox Height="74" Name="txtKast"
                         Text="{Binding Path=LastName, Mode=TwoWay}"  Width="460" />
    <TextBlock Height="30"  Text="E-Mail" />
    <TextBox Height="74" Name="txtEmail" 
                         Text="{Binding Path=Email, Mode=TwoWay}" Width="460" />
    <TextBlock Height="30"  Text="Phone" />
    <TextBox Height="74" Name="txtPhone" 
                         Text="{Binding Path=Phone, Mode=TwoWay}" Width="460" />
    <Button Content="Save" Height="72" Name="btnSave" Width="160" Click="Save_Click" />
</StackPanel>

When the application starts up, the code behind for the Main.xaml page calls DataAccess.LoadData which in turn, calls GetCustomersAndOrders. When the asynchronous method complete, it tells DataAccess to notify the Main.xaml page that the data has been retrieved and Customer (and it’s Orders) have been populated. In response, Main.xaml re-binds the UI controls to ensure they are refreshed. To avoid threading issues, the code takes advantage of a dispatcher to invoke the UI binding.

public void PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
      var propChanged = e.PropertyName;
      switch (propChanged)
      {
        case "Customer":

          Dispatcher.BeginInvoke(() => stackPanel1.DataContext = App.ViewModel.Customer);
          Dispatcher.BeginInvoke(() => lstOrders.ItemsSource = App.ViewModel.Orders);
          break;
      }
    }

Then as you see in Figure 3, the “my info” and “orders” screens of the application will display the data that came back from the data service.

When you run the application, the data service and its subsequent call to CustomerOrderContext class defined in the DataLayer project, will trigger Code First to do its work. Code First will read the classes and then check for any additional model configurations to build its in-memory model. If the database does not yet exist, the context’s Database Initialization will also do its work — creating the database and seeding the database with the initial data provided in the Seed method.

Updating Data through the Service

You may recall that when building the Data Service earlier, you created AllRead rights for all of the entity sets, then composed additional rights on top of that. You removed all access to the EdmMetadatas EntitySet. And you explicitly set the Customers access permissions to AllRead and WriteMerge, which says that consumers can view customer data and update it, but still cannot insert or delete Customer data.

This means that the WP7 application will be allowed to update customer data. Let’s see how that works.

Revisiting the XAML for the TextBox controls in the UI, notice that the Mode for the TextBoxes is set to “TwoWay”. That will ensure that the CustomerViewModel will update any of its properties that are modified by the user, so the DataAccess.Customer will contain the latest data.

And remember that  _currentCustomerEntity variable where the customer returned from the data service was squirreled away? That will make updating the customer very easy.

Here are the two asynchronous methods in the DataAccess class that perform the update to Customer.

public void Save()
    {
      _currentCustomerEntity.EmailAddress = Customer.Email;
      _currentCustomerEntity.FirstName = Customer.FirstName;
      _currentCustomerEntity.LastName = Customer.LastName;
      _currentCustomerEntity.Phone = Customer.Phone;

      _context.UpdateObject(_currentCustomerEntity);
      _context.BeginSaveChanges(FinishSaveChanges, _context);
    }

    private void FinishSaveChanges(IAsyncResult result)
    {
      try
      {

        _context = result.AsyncState as CustomerOrderContext;
        _context.EndSaveChanges(result);
        NotifySaveStatusChanged("Saved");

      }
      catch (Exception ex)
      {

        NotifySaveStatusChanged("Error:" + ex.Message);
      }
    }

Why is it important to use the original Customer for updating? Let’s take a look at the SQL command that gets run on the database in response to the user modifying a single field on the screen — the email address.

exec sp_executesql N'update [dbo].[Customers]

set [Title] = @0, [FirstName] = @1, [MiddleName] = @2, [LastName] = @3, [CompanyName] = @4,

[EmailAddress] = @5, [Phone] = null, [ModifiedDate] = @6, [AddedDate] = @7

where (([CustomerId] = @8) and ([TimeStamp] = @9))

select [TimeStamp]

from [dbo].[Customers]

where @@ROWCOUNT > 0 and [CustomerId] = @8',N'@0 nvarchar(max) ,@1 nvarchar(max) ,

@2 nvarchar(max) ,@3 nvarchar(max) ,@4 nvarchar(max) ,@5 nvarchar(max) ,@6 datetime2(7),

@7 datetime2(7),@8 int,@9 binary(8)',

@0=N'Head Geek',@1=N'Julie',@2=N'H',@3=N'Lerman',@4=N'The Data Farm',

@5=N'julie@somedomain.com',@6='2011-06-26 13:24:01.1831555',

@7='2011-06-26 13:23:03.0100000',@8=1,@9=0x00000000000007D1

Because the Save method begin with the original customer that came from the service, that customer had the database original values, not just for the properties used by the app, but the others as well, e.g., Title and CompanyName. When the data service and subsequently Entity Framework, constructed the database UPDATE command it used all of the available values for the update. If you did not have the original values that were available in the original Customer for properties not in the CustomerViewModel, you would only have had nulls for those properties, and the UPDATE command would have updated the customer setting Title to null, Company to null and so forth. If the application did not use the ViewModels and worked directly with the instances returned from the data service, you could take a different approach to the update. You have to keep track of all of those unused properties. This is not specific to WP7 or to the fact that the solution uses Code First and you’ll find plenty of resources discussing patterns for dealing with state when consuming OData.

Retrieving the Line Items On-Demand for Display

You’ll see in the sample download that when a user selects a particular order, the DataAccess class runs through a similar workflow, executing an asynchronous pair of methods to retrieve that order’s LineItems from the data service, pushing the results into LineItemViewModel classes then notifying the UI to rebind the controls of the Details.xaml page to the newly populated DataAccess.LineItems collection.

Conclusion

In this article you saw how to create a WCF Data Service from domain classes and Entity Framework 4.1 Code First. Given that your model and the Code First logic have already been implemented, the experience of exposing it through the data service is fairly simple. The data service is able to trigger all of the code first activities such as the model building, configuration and even database creation.

With the service in place, you can use one of the many OData Client Libraries to consume the service in your applications. Here you saw how simple it can be to consume and even update data through the data service when building a data driven Windows Phone 7 application.

Microsoft is conducting an online survey to understand your opinion of the MSDN Web site. If you choose to participate, the online survey will be presented to you when you leave the MSDN Web site.

Would you like to participate?