Tuesday, April 17, 2012

Creating and Consuming RESTful WCF Data Services

I have been reading/researching REST WCF Data Services, OData, creation, consumption, etc.  This methodology is a very useful basic construct.  By using the entity framework within Visual Studio 2010, I created a basic blog/atom/rss feed project in order to get my hands around it. 
Here is an overview of what I found:

Entity Framework
The entity framework is very powerful out of the box.  It allows you to create models that are directly tied to your database and queryable using LINQ.  It basically infers your data objects as entities, and creates your basic CRUD operations coupled with them.
It will infer your schema by either creating from an existing database or by creating a database from your entity design.  For the purpose of this exercise, I created an entity and then created a database from the entity layout.  I started by creating a new ADO.Net Entity Data Model in my project.



Then, I laid out my elements with the needed relationships:


The cool part here was adding these values as scalar or navigation properties.  The navigation properties infer the foreign key relationship and allow you to do 1:1, 1:* or *:* for the multiplicity.  It also infers the referential relationship for integrity.

After completing the entity model, I simply RMC and selected 'Generate Database from Model...'.  This walked me through connecting to the serve instance and credentials.  I had the option of creating a new catalog or appending to an existing one.  I chose to create.  This step created the catalog and generated a SQL for creating the schema with all constraints.  At this point, I ran the SQL and the entire database was created.


Creating the WCF DataService
This part is where it gets crazy intuitive and easy.  First thing you have to do is create a WCFDataService.

Then, I replaced the TODO code with the basics to get lists


public class PTCFeedService : DataService<PTCFeedsContainer>  
{  
    // This method is called only once to initialize service-wide policies.  
    public static void InitializeService(DataServiceConfiguration config)  
    {  
      config.SetEntitySetAccessRule("Feeds", EntitySetRights.All);  
      config.SetEntitySetAccessRule("Entries", EntitySetRights.All);  
      config.SetEntitySetAccessRule("Tags", EntitySetRights.All);  
      config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;  
      config.UseVerboseErrors = true;  
     }  
}  

At this point, I am able to compile and run.  I have automatically generated some basic REST Services along the way.  What this gives me is the ability to query from a uri for a plural of all entities.  A quick informative query is to get the meta data from the service.  (http://localhost:62368/PTCFeedService.svc/$metadata)


<edmx:Edmx Version="1.0">  
    <edmx:DataServices m:DataServiceVersion="1.0">  
     <Schema Namespace="PTCFeeds">  
      <EntityType Name="Feed">  
       <Key>  
        <PropertyRef Name="FeedID"/>  
       </Key>  
       <Property Name="FeedID" Type="Edm.Int64" Nullable="false" p8:StoreGeneratedPattern="Identity" p9:SetterAccess="Public"/>  
       <Property Name="Name" Type="Edm.String" Nullable="false" MaxLength="40"/>  
       <NavigationProperty Name="Entries" Relationship="PTCFeeds.FeedEntry" FromRole="Feed" ToRole="Entry"/>  
      </EntityType>  
      <EntityType Name="Entry">  
       <Key>  
        <PropertyRef Name="EntryID"/>  
       </Key>  
       <Property Name="EntryID" Type="Edm.Int64" Nullable="false" p8:StoreGeneratedPattern="Identity"/>  
       <Property Name="Created" Type="Edm.DateTime" Nullable="false" p8:SetterAccess="Public"/>  
       <Property Name="Title" Type="Edm.String" Nullable="false"/>  
       <Property Name="Content" Type="Edm.String" Nullable="false"/>  
       <Property Name="FeedID" Type="Edm.Int64" Nullable="false"/>  
       <Property Name="Modified" Type="Edm.DateTime" Nullable="false" p8:SetterAccess="Public"/>  
       <NavigationProperty Name="Feed" Relationship="PTCFeeds.FeedEntry" FromRole="Entry" ToRole="Feed"/>  
       <NavigationProperty Name="Tags" Relationship="PTCFeeds.TagEntry" FromRole="Entry" ToRole="Tag"/>  
      </EntityType>  
      <EntityType Name="Tag">  
       <Key>  
        <PropertyRef Name="TagID"/>  
       </Key>  
       <Property Name="TagID" Type="Edm.Int64" Nullable="false" p8:StoreGeneratedPattern="Identity"/>  
       <Property Name="Name" Type="Edm.String" Nullable="false"/>  
       <NavigationProperty Name="Entries" Relationship="PTCFeeds.TagEntry" FromRole="Tag" ToRole="Entry"/>  
      </EntityType>  
      <Association Name="FeedEntry">  
       <End Role="Feed" Type="PTCFeeds.Feed" Multiplicity="1"/>  
       <End Role="Entry" Type="PTCFeeds.Entry" Multiplicity="*"/>  
       <ReferentialConstraint>  
        <Principal Role="Feed">  
         <PropertyRef Name="FeedID"/>  
        </Principal>  
        <Dependent Role="Entry">  
         <PropertyRef Name="FeedID"/>  
        </Dependent>  
       </ReferentialConstraint>  
      </Association>  
      <Association Name="TagEntry">  
       <End Role="Tag" Type="PTCFeeds.Tag" Multiplicity="*"/>  
       <End Role="Entry" Type="PTCFeeds.Entry" Multiplicity="*"/>  
      </Association>  
     </Schema>  
     <Schema Namespace="PattersonTechCenterFeeds">  
      <EntityContainer Name="PTCFeedsContainer" p7:LazyLoadingEnabled="true" m:IsDefaultEntityContainer="true">  
       <EntitySet Name="Feeds" EntityType="PTCFeeds.Feed"/>  
       <EntitySet Name="Entries" EntityType="PTCFeeds.Entry"/>  
       <EntitySet Name="Tags" EntityType="PTCFeeds.Tag"/>  
       <AssociationSet Name="FeedEntry" Association="PTCFeeds.FeedEntry">  
        <End Role="Feed" EntitySet="Feeds"/>  
        <End Role="Entry" EntitySet="Entries"/>  
       </AssociationSet>  
       <AssociationSet Name="TagEntry" Association="PTCFeeds.TagEntry">  
        <End Role="Tag" EntitySet="Tags"/>  
        <End Role="Entry" EntitySet="Entries"/>  
       </AssociationSet>  
      </EntityContainer>  
     </Schema>  
    </edmx:DataServices>  
   </edmx:Edmx>  


Binding to asp.net
At this point, your service is ready to go.  Add a reference to the service using the uri as you would add a normal WCF service. Do not use web reference in this case.  Binding it to a grid control is almost too simple.


PTCFeedSvcRef.PTCFeedsContainer feedSvc =
  new PTCFeedSvcRef.PTCFeedsContainer(new Uri(@"http://localhost:62368/PTCFeedService.svc/"));   
GridView1.DataSource = feedSvc.Feeds;  
GridView1.DataBind();  


This is a simple example, but should illustrate how powerful these concepts can be.  Feel free to shoot me any questions or comments.