April 2010

Volume 25 Number 04

Visual Studio - Entity Framework 4.0 and WCF Data Services 4.0 in Visual Studio 2010

By Elisa Flasko | April 2010

Among its many new improvements, Visual Studio 2010 introduces the much-anticipated Entity Framework 4.0 and WCF Data Services 4.0 (formerly ADO.NET Data Services), which together simplify how you model, consume and produce data.

Entity Framework 4.0 (EF 4.0) focuses on enabling and simplifying two primary scenarios: domain-centric application development and the traditional data-centric “forms over data.” It introduces features such as model first development, which allows you to create a model and have customized T-SQL generated for you; support for persistence ignorance; foreign keys; lazy loading and custom code generation for entities.

WCF Data Services 4.0 focuses on enabling updates to the Open Data Protocol (odata.org) and on its new features, including two-way data binding for Windows Presentation Foundation (WPF) and Silverlight, row count, server-driven paging, enhanced binary large object support and support for projections.

Using a sample weblog application (MyBlog), I will explore the new features in both EF and WCF Data Services and explain how the technologies work together to simplify the way data is modeled and consumed. This sample application will have both an ASP.NET Web application that provides a read-only view of blog posts, and a Silverlight blog administrator client that allows the blog owner to edit posts. I will begin the application using model first to create an Entity Data Model (EDM), and then generate a database and the code to interact with that database. This sample will also make use of the ADO.NET Data Services Update for Silverlight 3 CTP 3.

Getting Started with EF 4.0

I’ll begin with the ASP.NET Web Application project. (My application is called BlogModel; you can download the accompanying code at code.msdn.microsoft.com/mag201004VSData.) To get started with EF, I use the Add New Item wizard to add an ADO.NET EDM, and select an Empty model that I’ll call BlogModel as well. By right-clicking on the empty Designer surface and choosing Properties, you can see the default Entity Container name, BlogModelContainer, in this case. First I’ll change the name to BlogContext, and then I’ll create the model.

MyBlog requires three Entities I’ll call Blog, Post and Tag, as shown in Figure 1. To create them, I drag an Entity from the toolbox to the design surface, then right-click and select Properties to edit the entity properties. I’ll also need a few scalar properties on each of the entities (right-click on the entity and select Add | Scalar Property).

image: Blog, Post and Tag Entities and Associated Property Settings

Figure 1 Blog, Post and Tag Entities and Associated Property Settings

Foreign Key Support in EF 4.0

Next, I’ll add relationships between these entities. Right-click on the design surface and select Add | Association, as shown in Figure 2. EF now supports foreign keys, allowing the inclusion of foreign key properties on an entity. Notice that adding the relationship added a BlogBlogID property (the foreign key) to the Post entity.

image: Associations Between Blog, Post and Tag Entities

Figure 2 Associations Between Blog, Post and Tag Entities

The inclusion of foreign key properties on entities simplifies a number of key coding patterns, including data binding, dynamic data, concurrency control and n-tier development. For example, if I’m databinding a grid that shows products and I have the CategoryID (a foreign key value) in a grid but I don’t have the corresponding Category object, the foreign key support in EF means I no longer need to take on the cost of a query to separately pull back the Category object.

Model First with EF 4.0

Now that the model is built (see Figure 3), the application needs a database. In this case, MyBlog is a new application and doesn’t yet have a database. I don’t want to create the database myself; I’d rather just have it done for me—and I can. With model first in EF 4.0, Visual Studio can now generate not only custom code for the entities, but also a database based on the model just created.

image: Blog Model

Figure 3 Blog Model

First, I need to create the empty database to which I will apply the generated schema. To do this, I open Server Explorer, right-click on the Data Connections node and select Create New SQL Server Database (see Figure 4). With the empty database created, I right-click on the model design surface and select Generate Database from Model. Walking through the Generate Database wizard creates a BlogModel.edmx.sql file. With this new file open, it’s simple to right-click on the file and execute the SQL script to create the schema for my database.

image: Create a New Empty Database and Generate Database Schema from EDM

Figure 4 Create a New Empty Database and Generate Database Schema from EDM

Custom Code Generation with EF 4.0

At this point, there are a number of next steps possible, one of which is to customize the code that EF generates, using T4 Templates. In Visual Studio 2008 SP1, although EF provided some hooks for customizing code generation, it was relatively inflexible and hard to use. EF 4.0 now leverages T4 Templates, providing a much simpler, more flexible and more powerful way to customize generated code.

To add a new T4 Template to the project, right-click on the Entity Designer surface and select Add Code Generation Item. From here, choose any currently installed template to use as a starting point, or view the templates available in the Online Gallery. To use the default EF template as the starting point for this project, I’ll choose the ADO.NET EntityObject Generator template; by default the template is called Model1.tt. By adding a code generation template in this manner, EF automatically disables default code generation for the model. The generated code is removed from BlogModel.Designer.cs and now exists in Model1.cs. At this point, the template can be edited to customize the entities it will generate, and every time the .tt file is saved, the dependent code will be regenerated. For more information on editing and using T4 Templates with EF 4.0, check out the ADO.NET Team blog at blogs.msdn.com/adonet.

Using POCO Entities with EF 4.0

Visual Studio 2008 SP1 imposed a number of restrictions on entity classes that made it difficult, to say the least, to build classes that were truly independent of persistence concerns. One of the most requested features in EF 4.0 is the ability to create Plain Old CLR Object (POCO) types for entities that work with EF and don’t impose the type of restrictions in Visual Studio 2008 SP1.

Let’s go back to the MyBlog sample. I’m going to create POCO objects for the three entities—Blog, Post and Tag. First, code generation needs to be turned off and I need to remove the .tt file I added in the last section. To check out the properties of the model, right-click on the Entity Designer surface. As shown in Figure 5, there is a property named Code Generation Strategy that needs to be set to None to turn off code generation.

image: Code Generation Strategy Property

Figure 5 Code Generation Strategy Property

Note that this property is set to None automatically when you add a Code Generation Item (T4 Template) to a project. If the project currently has a .tt file included, you will need to remove it before using POCO objects. From here, the classes for the POCO objects can be added—Blog.cs, Post.cs and Tag.cs, as shown in Figures 6, 7 and 8.

Figure 6 POCO Object for Blog Entity

public class Blog

{

  public intBlogID

  {

    get;

    set;

  }

  public string Name

  {

    get;

    set;

  }

  public string Owner

  {

    get;

    set;

  }

  public List<Post> Posts

  {

    get { return _posts; }

    set { _posts = value; }

  }

  List<Post> _posts = new List<Post>();

}

Figure 7 POCO Object for Tag Entity

public class Tag

{

  public int TagID

  {

    get;

    set;

  }

  public string Name

  {

    get;

    set;

  }

  public List<Post> Posts

  {

    get { return _posts; }

    set { _posts = value; }

  }

  List<Post> _posts = new List<Post>();

}

Figure 8 POCO Object for Post Entity

public class Post

{

  public int PostID

  {

    get;

    set;

  }

  public DateTime CreatedDate

  {

    get;

    set;

  }

  public DateTime ModifiedDate

  {

    get;

    set;

  }

  public string Title

  {

    get;

    set;

  }



  public string PostContent

  {

    get;

    set;

  }

  public Blog Blog

  {

    get;

    set;

  }

  public int BlogBlogID

  {

    get;

    set;

  }

  public Boolean Public

  {

    get;

    set;

  }

  public List<Tag> Tags

  {

    get { return _tags; }

    set { _tags = value; }

  }

  private List<Tag> _tags = new List<Tag>();

}

Last, I need to create the context class, which is much like the ObjectContext implementation generated with default code generation, but I’ll call it BlogContext. It will inherit from the ObjectContext class. The context is the class that is persistence-aware. It will allow the composition of queries, materialize entities and save changes back to the database (see Figure 9).

Figure 9 BlogContext

public class BlogContext : ObjectContext 

{

  public BlogContext() 

    : base("name=BlogContext", "BlogContext") 

      { 

      }

  public ObjectSet<Blog> Blogs 

  {

    get 

    {

      if (_Blogs == null) 

      { 

         _Blogs = 

           base.CreateObjectSet<Blog>("Blogs"); 

      }

    return _Blogs; 

  } 

}

private ObjectSet<Blog> _Blogs;

public ObjectSet<Post> Posts 

{

  get 

  {

    if (_Posts == null) 

    { 

      _Posts = 

        base.CreateObjectSet<Post>("Posts"); 

    }

  return _Posts; 

  } 

}

private ObjectSet<Post> _Posts;

public ObjectSet<Tag> Tags 

{

  get 

  {

    if (_Tags == null) 

  { 

    _Tags = base.CreateObjectSet<Tag>("Tags"); 

  }

  return _Tags; 

  } 

}

private ObjectSet<Tag> _Tags; 

}

Lazy Loading

In Visual Studio 2008 SP1, EF supported two basic ways of loading related entities, both ensuring that the application hits the database only when explicitly told to do so, using either the Load method to explicitly load the related entities, or the Include method to eagerly load related entities within a query. Another of the most requested features now in EF 4.0 is lazy loading. When performing explicit loads is not required, you can use lazy loading (also known as deferred loading) to load related entities when a navigation property is accessed for the first time. In Visual Studio 2010, this is done by making the navigation properties virtual properties.

In the MyBlog sample, the public List<Post> Posts property in both Blog.cs and Tag.cs would become public virtual List<Post> Posts, and the public List<Tag> Tags property in Post.cs would become public virtual List<Tag> Tags. EF would then create a proxy type at runtime that knows how to perform a load so that no additional code changes are necessary. However, because the MyBlog sample uses WCF Data Services to expose entities over an Open Data Protocol (OData) service, the application doesn’t make use of lazy loading.

Creating a WCF Data Service in Visual Studio 2010

MyBlog takes advantage of the near-turnkey solution supplied by WCF Data Services to provide an OData service over an EDM, and includes a Silverlight blog administrator client that uses the OData service. The Open Data Protocol is a data-sharing standard that breaks down silos and fosters a powerful, interoperative ecosystem for data consumers (clients) and producers (services), enabling more applications to make sense of a broader set of data.

With an EDM and database set up, adding a new WCF Data Service to the application is simple; using the Add New Item wizard, I add a WCF Data Service (I’ll call it BlogService). This generates a BlogService.svc file that represents the skeleton of the service and is pointed at the EDM by providing it with the context created earlier. Because the service is fully locked down by default, access to the entity sets that are to be made available over the service must be explicitly allowed using config.SetEntitySetAccessRule. To do this, a rule is set for each EntitySet that is made available, as seen in Figure 10.

Figure 10 BlogService.svc

public class BlogService : DataService<BlogContext> 

{ 

    // This method is called only once to initialize service-wide policies. 

    public static void InitializeService(DataServiceConfiguration config) 

    { 

       // TODO: set rules to indicate which entity sets and service 

       // operations are visible, updatable, etc. 

       // Examples: 

       config.SetEntitySetAccessRule("Blogs", EntitySetRights.All); 

       config.SetEntitySetAccessRule("Posts", EntitySetRights.All); 

       config.SetEntitySetAccessRule("Tags", EntitySetRights.All); 

       // config.SetServiceOperationAccessRule("MyServiceOperation", 

       // ServiceOperationRights.All); 

       config.DataServiceBehavior.MaxProtocolVersion =      

       DataServiceProtocolVersion.V2; 

    } 

}

* (Note: If you download the sample code for this article, you’ll notice it uses a very simple Forms Authentication scheme to secure the site, and the remaining examples will also use this scheme to filter data based on the currently logged-in user. Because implementing Forms Authentication is beyond the scope of this article, however, I’m going to skip the details here.)*

With the service up and running, the next step is to filter the results based on the user currently logged in so that only Blogs owned by that user are returned. You can accomplished this by adding Query Interceptors as seen in Figure 11 to restrict the entities returned by a query.

Figure 11 Query Interceptor

// returns only public posts and posts owned by the current user 

[QueryInterceptor("Posts")]

public Expression<Func<Post, bool>>OnPostQuery() 

{

  return p =>p.Public == true ||

  p.Blog.Owner.Equals(HttpContext.Current.User.Identity.Name); 

}

// returns only the blogs the currently logged in user owns 

[QueryInterceptor("Blogs")]

public Expression<Func<Blog, bool>>OnBlogQuery() 

{

  return b =>

  b.Owner.Equals(HttpContext.Current.User.Identity.Name); 

}

Consuming a WCF Data Service in Silverlight

The details of building a Silverlight UI are beyond the scope of this article, so I’ll gloss over some of them. But before digging in to how to hook up the data service to a Silverlight app, I’ll add a new Silverlight application to the project that contains the default Silverlight page MainPage.xaml. To that I add a basic DataGrid, ComboBox, Button and a couple of labels. With a skeleton Silverlight app ready (see Figure 12), we can hook up the data service.

image: Basic Layout of MyBlog Silverlight

Figure 12 Basic Layout of MyBlog SilverlightAdministrator Application

To start, the Silverlight application needs objects that represent each of the entities defined by the data service. To do this, you use the Add Service Reference wizard in Visual Studio to automatically generate client classes for the data service. (Note that to use Add Service Reference, I need to temporarily disable the authorization checks implemented on the service so that Add Service Reference has full access to the service. I point the Add Service Reference wizard at the base URI for the service, which in MyBlog is localhost:48009/BlogService.svc).

Data Binding in WCF Data Services 4.0

Improved support for data binding in WCF Data Services 4.0 adds a new collection type, DataServiceCollection, to the client library, extending ObservableCollection. However, in Silverlight 3, data binding is turned off by default when a service reference is added to the project. So to take advantage of the new data binding functionality in WCF Data Services, data binding needs to be turned on and the service reference needs to be updated. From the Solution Explorer, click the Show All Files button and expand the BlogService item under the Service References node.  Double-click the Reference.datasvc map file and replace the Parameters element with the XML snippet shown here:

<Parameters>

  <Parameter Name="UseDataServiceCollection" Value="true" />

  <Parameter Name="Version" Value="2.0" />

</Parameters>

Setting the UseDataServiceCollection parameter to true auto-generates client-side types that implement the INotifyPropertyChanged and INotifyCollectionChanged interfaces. This means that any changes made to contents of a DataServiceCollection or the entities in the collection are reflected on the client context. It also means that if an entity in the collection is requeried, any changes to that entity are reflected in the entities in the DataServiceCollection. And it means that because the DataServiceCollection implements the standard binding interfaces, it can be bound as the DataSource to most WPF and Silverlight controls.

Going back to the MyBlog sample, the next step is to create a connection to the service by creating a new DataServiceContext and use it to query the service. Figure 13 includes MainPage.xaml and MainPage.xaml.cs and shows the creation of a new DataServiceContext, querying the service for all blogs—in this case the service returns all blogs owned by the logged-in user—and binding the blogs to a ComboBox on the Silverlight application. 

Figure 13 MainPage.xaml and MainPage.xaml.cs

MainPage.xaml

<Grid x:Name="LayoutRoot" Background="White" Width="618">

  <data:DataGrid Name="grdPosts" AutoGenerateColumns="False" 

  Height="206" HorizontalAlignment="Left"Margin="17,48,0,0"

  VerticalAlignment="Top" Width="363" ItemsSource="{Binding Posts}">

    <data:DataGrid.Columns>

      <data:DataGridTextColumn Header="Title" Binding="{Binding Title}"/>

      <data:DataGridCheckBoxColumn Header="Public" 

      Binding="{Binding Public}"/>

      <data:DataGridTextColumn Header="Text" 

      Binding="{Binding PostContent}"/>

    </data:DataGrid.Columns>

  </data:DataGrid>

  <Button Content="Save" Height="23" HorizontalAlignment="Left" 

  Margin="275,263,0,0" Name="btnSave" VerticalAlignment="Top"

  Width="75" Click="btnSave_Click_1" />

  <ComboBox Height="23" HorizontalAlignment="Left" 

  Margin="86,11,0,0" Name="cboBlogs" VerticalAlignment="Top"

  Width="199" ItemsSource="{Binding}" DisplayMemberPath="Name" 

  SelectionChanged="cboBlogs_SelectionChanged" />

  <dataInput:Label Height="50" HorizontalAlignment="Left" 

  Margin="36,15,0,0" Name="label1" 

  VerticalAlignment="Top"Width="100" Content="Blogs:" />

  <dataInput:Label Height="17" HorizontalAlignment="Left" 

  Margin="17,263,0,0" Name="lblCount" VerticalAlignment="Top"

  Width="200" Content="Showing 0 of 0 posts"/>

  <Button Content="Load More Posts" Height="23" HorizontalAlignment="Left" Margin="165,263,0,0" Name="btnMorePosts"

VerticalAlignment="Top" Width="100" Click="btnMorePosts_Click" />

</Grid>

MainPage.xaml.cs

public MainPage() 

{

  InitializeComponent(); 

  svc = new BlogContext(new Uri("/BlogService.svc", UriKind.Relative)); 

  blogs = new DataServiceCollection<Blog>(svc);

  this.LayoutRoot.DataContext = blogs;

  blogs.LoadCompleted += 

  new EventHandler<LoadCompletedEventArgs>(blogs_LoadCompleted);

  var q = svc.Blogs.Expand("Posts");

  blogs.LoadAsync(q); 

}

void blogs_LoadCompleted(object sender, LoadCompletedEventArgs e) 

{

  if (e.Error == null) 

  {

    if (blogs.Count> 0) 

    {

      cboBlogs.SelectedIndex = 0; 

    } 

  } 

}

To bind the DataGrid, a cboBlogs_SelectionChanged() method is added:

private void cboBlogs_SelectionChanged(object sender, SelectionChangedEventArgs e) 

{

  this.grdPosts.DataContext = ((Blog)cboBlogs.SelectedItem); 

}

This method will be called every time the item currently selected in the ComboBox is changed.

The last item to be hooked up on the Silverlight application is the Save Button, which is enabled by adding a btnSave_Click method that calls SaveChanges on the DataServiceContext, as seen in Figure 14.

Figure 14 Saving Changes Back to the Database

private void btnSave_Click_1(object sender, RoutedEventArgs e) 

{

  svc.BeginSaveChanges(SaveChangesOptions.Batch, OnChangesSaved, svc); 

}

private void OnChangesSaved(IAsyncResult result) 

{

  var q = result.AsyncState as BlogContext;

  try 

  {

    // Complete the save changes operation

    q.EndSaveChanges(result); 

  }

  catch (Exception ex) 

  {

    // Display the error from the response.

    MessageBox.Show(ex.Message); 

  } 

}

Server-Driven Paging

Often there’s a need to limit the total number of results a server will return for a given query, to avoid having an application accidentally pull back an extremely large amount of data. Server-driven paging in WCF Data Services 4.0 allows a service author to set per-collection limits on the total number of entities a service returns for each request by setting the SetEntitySetPageSize property in the InitializeService method for each collection of entities. In addition to limiting the number of entities returned for each request, the data service provides the client with a “next link”—a URI specifying how the client is to retrieve the next set of entities in the collection, in the form of the AtomPub<link rel=”next”> element.

Going back to the MyBlog example, I’ll set the SetEntitySetPageSize property on my service for the Posts EntitySet to five results:

config.SetEntitySetPageSize("Posts", 5);

This will limit the number of entities returned when the service is queried for Posts. I’m setting the SetEntitySetPageSize property to a small number here to illustrate how the feature works; generally an application would set a limit that most clients would not run into (rather the client would use $top and $skip to control the amount of data requested at any time).

I’ll also add a new button to the application to allow the user to request the next page of Posts from the service. This snippet shows the btnMorePosts_Click method accessing the next set of Posts:

private void btnMorePosts_Click(object sender, RoutedEventArgs e) 

{

  Blog curBlog = cboBlogs.SelectedItem as Blog;

  curBlog.Posts.LoadCompleted += new   

    EventHandler<LoadCompletedEventArgs>(Posts_LoadCompleted);

  curBlog.Posts.LoadNextPartialSetAsync(); 

}

Row Count

One of the most requested features after the release of ADO.NET Data Services in Visual Studio 2008 SP1 was the ability to determine the 
total number of entities in a set without the need to pull them all back from the database. In WCF Data Services 4.0, we added the Row Count feature to do this.

When creating a query on the client, the IncludeTotalCount method can be called to include the count tag in the response. The value can then be accessed, as shown in Figure 15, using the TotalCount property on the QueryOperationResponse object.

Figure 15 Using Row Count

private void cboBlogs_SelectionChanged(object sender, SelectionChangedEventArgs e) 

{

  Blog curBlog = this.cboBlogs.SelectedItem as Blog;

  this.grdPosts.DataContext = curBlog;

  var q = (from p in svc.Posts.IncludeTotalCount()

  where p.BlogBlogID == curBlog.ID

  select p) as DataServiceQuery<Post>;

  curBlog.Posts.LoadCompleted += new     

  EventHandler<LoadCompletedEventArgs>(Posts_LoadCompleted);

  curBlog.Posts.LoadAsync(q); 

}

void Posts_LoadCompleted(object sender, LoadCompletedEventArgs e) 

{

  if (e.Error == null) 

  {

    Blog curBlog = cboBlogs.SelectedItem as Blog;

    totalPostCount = e.QueryOperationResponse.TotalCount;

    string postsCount = string.Format("Displaying {0} of {1} posts",

    curBlog.Posts.Count, totalPostCount);

    this.lblCount.Content = postsCount;

    curBlog.Posts.LoadCompleted -= Posts_LoadCompleted; 

  } 

}

Projections

Another of the most requested features in WCF Data Services 4.0 is Projections, the ability to specify a subset of an entity’s properties to be returned from a query, allowing applications to optimize for bandwidth consumption and memory footprint. In Visual Studio 2010, the Data Services URI format has been extended to include the $select query option, enabling clients to specify the subset of properties to be returned by the query.  For example, with MyBlog, I could query for all Posts and project only Title and PostContent using the following URI:  BlogService.svc/Posts?$select=Title,PostContent. On the client side, you can also now use LINQ to query with projections.

Learning More

This article covers just enough to get you started with Entity Framework 4.0 and WCF Data Services 4.0 in Visual Studio 2010. There are a number of other topics and new features you may be interested in. For more information, check out the MSDN Data Development center at msdn.microsoft.com/data.  


Elisa Flasko is a program manager in the Data Programmability team atMicrosoft, working on the ADO.NET Entity Framework, WCF Data Services, M, Quadrant and SQL Server Modeling Services technologies. She can be reached at blogs.msdn.com/elisaj.

Thanks to the following technical experts for reviewing this article: Jeff Derstadt and Mike Flasko