Using ADO.NET Data Services

Published 09 January 09 07:52 PM

This week I worked through a good article on getting started with ADO.NET Data Services (a.k.a Astoria) called Using ADO.NET Data Services. If you're not familiar with the technology it's basically a framework for exposing your data models via RESTful web services. So if you are building a remote CRUD data access layer then this is a technology that you'll probably want to look into. It should save you some time especially if you're trying to expose your data over REST.

(Note: We're in the process of adding Visual Basic code snippets to the article but you can also download the VB sample projects here on Code Gallery.)

I'm building a few different Windows clients (WPF and Office) to see how we could use ADO.NET Data Services in our n-tier business applications and I'll blog about that soon. But for this post I'd like to highlight the getting started parts of the article that I found most helpful when trying to understand how to work with our database over the web.

File > New Project > ASP.NET Web Application....

Creating an ADO.NET Data Service is easy if you are using Visual Studio 2008 SP1. First create a new ASP.NET Web Application:

astoriaStep1

This will create a new ASP.NET project with a default.aspx page. If you're just building the service then you can delete this file right away. Next you need to add a new item to the project and select ADO.NET Data Service.

astoriaStep2

For this example I want to expose data in the Northwind sample database so I'm naming the service Northwind.svc. This will add the System.Data.Services and System.Data.Services.Client (the server and client pieces of the framework) to your project. Right-click on the Northwind.svc in the solution explorer and select Set as Start Page.

This process spits out a code template that sets up your data service by creating a class called Northwind that inherits from DataService.

Imports System.Data.Services
Imports System.Linq
Imports System.ServiceModel.Web

Public Class Northwind
    ' TODO: replace [[class name]] with your data class name
    Inherits DataService(Of [[class name]])

    ' This method is called only once to initialize service-wide policies.
    Public Shared Sub InitializeService(...
End Class

Notice however that you have some work to do. The DataService acts upon a data model, more specifically, a data model that exposes IQueryable properties. (Take a look at the CustomDataService example that is explained in the MSDN article if you're interested in creating a compatible data source yourself.) If you're lazy like me then you can use an Entity Framework data model which implements IQueryable by default.

Adding an Entity Data Model

So in order to fill out the [[class name]] of our IQueryable data source we need to create one first. Add a new item to the project and select ADO.NET Entity Data Model. I named the model NorthwindModel.

\astoriaStep3

Now you can run through the wizard to create the model. I'll just generate it from the database, select all the tables and then hit Finish. This process creates an IQueryable data source called NorthwindEntities. Now we can go back into the data service and fill out the [[class name]].

Public Class Northwind
    Inherits DataService(Of NorthwindEntities)

Setting Access to Tables in the Data Model

We also want to control access to the tables in our data model. It's probably a really bad idea to allow read/write access to every table in our database but it's possible by configuring an EntitySetAccessRule on the DataServiceConfiguration that is passed into the InitializeService method. Note that this is a Shared method so it will only run once no matter how many instances of the Northwind DataService are created. You may need to keep that in mind when debugging via Visual Studio and force some rebuilds if you change the code in there.

Public Class Northwind
    Inherits DataService(Of NorthwindEntities)

    ' This method is called only once to initialize service-wide policies.
    Public Shared Sub InitializeService(ByVal config As IDataServiceConfiguration)
        ' Everyone can party on my data!
        config.SetEntitySetAccessRule("*", EntitySetRights.All)
    End Sub

End Class

This allows anyone access to update, insert, delete and query the tables in the model. Probably a bad idea. So you can allow just read-only:

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

You can also write additional web methods for querying and updating the data with some additional processing. I'll get to that in a follow-up post or you can jump into that section of the article. First let's see how we query our data via an HTTP GET in IE.

Querying the Data via HTTP GET

Hitting F5 on the project will now automatically start the service and open the browser to the Northwind.svc which lists all the entities in which we're allowing access in our data model.

astoriaStep4

When we want to query our data, say all the customers, then we can do that via an HTTP GET by typing in the browser address bar: http://localhost:1234/Northwind.svc/Customers and a feed of all the customers in Northwind will be returned. If you're browsing using IE then it will attempt to put a style sheet on the feed, just right-click and view source to see the actual data returned (I'm just displaying the first two in the feed for clarity):

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="http://localhost:1308/Northwind.svc/" 
      xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" 
      xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" 
      xmlns="http://www.w3.org/2005/Atom">
  <title type="text">Customers</title>
  <id>http://localhost:1308/Northwind.svc/Customers</id>
  <updated>2009-01-10T02:17:37Z</updated>
  <link rel="self" title="Customers" href="Customers" />
  <entry>
    <id>http://localhost:1308/Northwind.svc/Customers('ALFKI')</id>
    <title type="text"></title>
    <updated>2009-01-10T02:17:37Z</updated>
    <author>
      <name />
    </author>
    <link rel="edit" 
          title="Customers" 
          href="Customers('ALFKI')" />
    <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Orders" 
          type="application/atom+xml;type=feed" 
          title="Orders" 
          href="Customers('ALFKI')/Orders" />
    <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/CustomerDemographics" 
          type="application/atom+xml;type=feed" 
          title="CustomerDemographics" 
          href="Customers('ALFKI')/CustomerDemographics" />
    <category term="NorthwindModel.Customers" 
              scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
    <content type="application/xml">
      <m:properties>
        <d:CustomerID>ALFKI</d:CustomerID>
        <d:CompanyName>Alfreds Futterkiste</d:CompanyName>
        <d:ContactName>Maria Anders</d:ContactName>
        <d:ContactTitle>Sales Representative</d:ContactTitle>
        <d:Address>Obere Str. 57</d:Address>
        <d:City>Berlin</d:City>
        <d:Region m:null="true" />
        <d:PostalCode>12209</d:PostalCode>
        <d:Country>Germany</d:Country>
        <d:Phone>030-0074321</d:Phone>
        <d:Fax>030-0076545</d:Fax>
      </m:properties>
    </content>
  </entry>
  <entry>
    <id>http://localhost:1308/Northwind.svc/Customers('ANATR')</id>
    <title type="text"></title>
    <updated>2009-01-10T02:17:37Z</updated>
    <author>
      <name />
    </author>
    <link rel="edit" 
          title="Customers" 
          href="Customers('ANATR')" />
    <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Orders" 
          type="application/atom+xml;type=feed" 
          title="Orders" 
          href="Customers('ANATR')/Orders" />
    <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/CustomerDemographics" 
          type="application/atom+xml;type=feed" 
          title="CustomerDemographics" 
          href="Customers('ANATR')/CustomerDemographics" />
    <category term="NorthwindModel.Customers" 
              scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
    <content type="application/xml">
      <m:properties>
        <d:CustomerID>ANATR</d:CustomerID>
        <d:CompanyName>Ana Trujillo Emparedados y helados</d:CompanyName>
        <d:ContactName>Ana Trujillo</d:ContactName>
        <d:ContactTitle>Owner</d:ContactTitle>
        <d:Address>Avda. de la Constitución 2222</d:Address>
        <d:City>México D.F.</d:City>
        <d:Region m:null="true" />
        <d:PostalCode>05021</d:PostalCode>
        <d:Country>Mexico</d:Country>
        <d:Phone>(5) 555-4729</d:Phone>
        <d:Fax>(5) 555-3745</d:Fax>
      </m:properties>
    </content>
  </entry>
.
.
.

Notice that this also returns the relationship information from the customer to the other tables in the model. So if we wanted to return all the orders for customer ALFKI then we would use the relative URL in this feed like so:

http://localhost:1234/Northwind.svc/Customers('ALFKI')/Orders

BTW, if you're going to get serious about developing RESTful web services I'd highly suggest you download Fiddler which will allow you to inspect your HTTP traffic and payloads. It's a lot easier to view this data in Fiddler than in IE.

Building a Simple Client

This stuff is all fun and games until we want to write a real client against our service. I don't think anyone would be too happy with IE as their client against this service, no matter how cool we wrote it. So let's take a look at a simple client that queries a product, makes a change, and submits it back to the service. This is an example that is illustrated in the article that helped me understand how to make changes to the data.

First thing we need to do is configure our service so we can query and update the product table. I'm also going to open up the Categories table for read access:

Public Class Northwind
    Inherits DataService(Of NorthwindEntities)

    ' This method is called only once to initialize service-wide policies.
    Public Shared Sub InitializeService(ByVal config As IDataServiceConfiguration)
        config.SetEntitySetAccessRule("Products", EntitySetRights.All)
        config.SetEntitySetAccessRule("Categories", EntitySetRights.AllRead)
    End Sub

End Class

Now let's add a simple console application (real clients soon, I promise!) to illustrate the client-side code it takes to query and update a product in our database. Once you add a new console application to the solution we need to add the service reference to our Northwind Data Service. Right click on the client console application and select "Add Service Reference". When the dialog opens click the Discover button and it should find the data service.

astoriaStep5

Once we add this service reference to our client this will automatically generate the client-side proxy that inherits from DataServiceContext and the necessary entities to work with our data service. It also adds a reference to the System.Data.Services.Client assembly which has the client-side framework where the DataServiceContext object lives. This is another added benefit than you creating a remote data access layer yourself. You tell the DataServiceContext how to track changes to the entities and it can can batch up requests and send them at once to the service.

One of cool things about having an IQueryable data source exposed through a data service is that you can use LINQ over the entities generated on the client-side and the framework will handle translating that to HTTP requests. You can dive deeper into the article on the exact query syntax it can support but it's pretty full featured.  For instance back on the IE client, if we just want to return the top 2 customers we could type: http://localhost:1234/Northwind.svc/Customers?$top=2

But when we're working with a managed client we can write a LINQ query against the data service instead. Here I'm returning all the products in the Beverages category:

Imports MyDataServiceClient.ServiceReference1

Module Module1
    Private ServiceURI As Uri = New Uri("http://localhost:1308/Northwind.svc/")

    Sub Main()
        Dim ctx As New NorthwindEntities(ServiceURI)

        Dim results = From p In ctx.Products _
                      Where p.Categories.CategoryName = "Beverages" 

        For Each p In results
            Console.WriteLine(p.ProductName)
        Next
        Console.ReadLine()
    End Sub

End Module

Pretty slick. Notice that since I'm using the Categories in the query, I have to enable access to that on the service side otherwise I'll get an error when running the client.

Saving Changes through the ADO.NET Data Service

In order to update the database we tell the DataServiceContext that we updated the object and then tell it to pass the change to the server by calling SaveChanges.

Imports MyDataServiceClient.ServiceReference1

Module Module1
    Private ServiceURI As Uri = New Uri("http://localhost:1308/Northwind.svc/")

    Sub Main()
        Dim ctx As New NorthwindEntities(ServiceURI)

        Dim product = (From p In ctx.Products _
                      Where p.ProductName = "Chai").FirstOrDefault()

        If product IsNot Nothing Then

            product.ProductName = "Chai Tea"
            ctx.UpdateObject(product)

            Try
                'You can batch up changes on the DataServiceContext
                ' before calling SaveChanges. This will hit the service.
                ctx.SaveChanges()
                Console.WriteLine("Saved Product")
            Catch ex As Exception
                Console.WriteLine(ex.ToString())
            End Try
        End If
    End Sub

End Module

Nice. In the next post I'll dig a little deeper into how we can intercept queries so that we can do some server-side processing first as well as how we can add additional processing methods to our service in order to perform custom querying and validation. I'm also building a WPF client for doing batch editing that I'll introduce. For now, please read the article on MSDN for more information. Until next time....

Enjoy!

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Using ADO.NET Data Services | Coded Style said on January 9, 2009 11:03 PM:

PingBack from http://www.codedstyle.com/using-adonet-data-services/

# Frank said on January 11, 2009 8:23 PM:

Hi Beth,

Can you show us some examples of updating data when the entity is detached?  And in this example please show how to update entities that have entity references.

I've found the whole experience of detaching and reattaching entities great if the entity has no references.

Thanks

# Alessandro Del Sole said on January 12, 2009 12:20 PM:

Hi Frank,

while waiting for Beth to reply, I'd like to mention the AttachTo method for attaching entities and the SetLink method for updating entities that have references. Both methods are exposed by the ObjectContext instance, in this case NorthwindEntities.

I hope this helps,

Alessandro

# ASPInsiders said on January 15, 2009 9:26 PM:

I've given a number of presentations on ADO.NET Data Services (formerly codenamed: &quot;Astoria&quot;)

# Beth Massi - Sharing the goodness that is VB said on January 15, 2009 11:30 PM:

In my last post I introduced ADO.NET Data Services and how you can easily expose your data model via

# VB Feeds said on January 16, 2009 1:45 AM:

In my last post I introduced ADO.NET Data Services and how you can easily expose your data model via

# DotNetShoutout said on January 16, 2009 10:59 PM:

Thank you for submitting this cool story - Trackback from DotNetShoutout

# Emmanuel Nuyttens said on January 18, 2009 7:28 AM:

Hi Beth,

Indeed, nice article again. But i'm getting a bit confused about those ADO.NET DataServices.

... in former articles, you wrote some dataservices using WCF, which could also be hosted on a IIS server (or any other webserver ...), and used the HTTP protocol.

So, my question, what's the differents then between a WCF DataServices hosted in an http enabled environment (IIS) and ADO.NET DataServices as explained here above.

Thx for any response,

Emmanuel NUyttens

.NET Architect

BERCO NV Belgium.

# Emmanuel Nuyttens said on January 18, 2009 7:32 AM:

Hello Beth,

Indeed, nice article again ... but i'm a bit confused here ... in former articles you used WCF as your dataservice layer ghost.

As i could understand, you can ghost a WCF service within a webserver like IIS, as you can do with ADO.NET DataServices ...

So , my question is quit obvious, what's the difference between hosting a http enabled WCF service and ADO.NET DataServices over the web ?

thx for any response,

Emmanuel Nuyttens

.NET Architect

BERCO NV Belgium

# Emmanuel Nuyttens said on January 19, 2009 2:46 AM:

Hello Beth,

I've tried to post this a couple of times ... hope it succeeds this time.

So, i'm a bit confused here .... In former articles you implemented WCF services for getting the data out of the datalayer, now we have ADO.NET DataServices which seems to do the same thing (... hosted on a weberser, using http) ...

So my question, what's the difference, and when use what ?

Thx for any response,

Emmanuel Nuyttens

.NET Architect

BERCO NV.

# Beth Massi said on January 20, 2009 10:39 PM:

Hi Emmanuel,

I think I answered your question on this post: http://blogs.msdn.com/bethmassi/archive/2009/01/15/ado-net-data-services-building-a-wpf-client.aspx#comments

Let me know if you still have questions.

Thanks,

-Beth

# Emmanuel Nuyttens said on January 21, 2009 4:36 AM:

Hi Beth,

Thx for the quick response.

The answer as mentioned on the link was not the same as the one asked here above.

The question above is about why & when we should use ADO.NET DataServices in favour of http enabled WCF Services ?

Thx for any response.

Emmanuel Nuyttens

.NET Architect

BERCO NV

# Beth Massi said on January 21, 2009 12:14 PM:

Hi Emmanuel,

I actually give my opinion on that in the post http://blogs.msdn.com/bethmassi/archive/2009/01/15/ado-net-data-services-building-a-wpf-client.aspx#comments

"ADO.NET Data Services are not meant to replace a real business object layer (yet). So if you have complex business rules you want to share on the client and server you are better off writing your own WCF services and data contracts. However, if you have simple CRUD and validation requirements or are looking for a remote data access layer for applications where business rules and validations are processed predominantly on the server (like web or reporting or query-heavy applications) then ADO.NET Data Services are a great fit. And no one is stopping you from using both your own WCF services in addition to ADO.NET data services in your client applications. "

HTH,

-B

# Emmanuel Nuyttens said on January 21, 2009 3:03 PM:

Good Evening Beth ! (here in Belgium it's evening now)

While paying some invoices (brrr !!!), i've got time to show your answer. Indeed you put the answer in a way in the "linked" mail. Sorry for repeating ...

But ok, it's all clear now to me, thx a lot.

Emmanuel Nuyttens

.NET Architect

BERCO NV

# Beth Massi - Sharing the goodness that is VB said on January 21, 2009 11:53 PM:

Last few posts I've been building a WPF client against ADO.NET Data Services, if you missed them: Using

# VB Feeds said on January 22, 2009 4:00 AM:

Last few posts I&#39;ve been building a WPF client against ADO.NET Data Services, if you missed them

# Beth Massi - Sharing the goodness that is VB said on January 23, 2009 1:46 AM:

Last few posts I've been building a WPF client against ADO.NET Data Services, if you missed them: Using

# VB Feeds said on January 23, 2009 4:32 AM:

Last few posts I&#39;ve been building a WPF client against ADO.NET Data Services, if you missed them

# Beth Massi - Sharing the goodness that is VB said on February 2, 2009 10:09 PM:

The last week I've been working with a couple teammates on an Office Business Application (OBA) demonstration

# VB Feeds said on February 3, 2009 2:28 AM:

The last week I&#39;ve been working with a couple teammates on an Office Business Application (OBA) demonstration

# STEFANO DEMILIANI said on February 3, 2009 4:02 AM:

An interesting OBA scenario...

# Beth Massi - Sharing the goodness that is VB said on February 3, 2009 5:23 PM:

Last post I talked about the high-level architecture of our O ffice B usiness A pplication for the new

# Beth Massi - Sharing the goodness that is VB said on February 7, 2009 5:36 PM:

In the last couple posts we've been talking about an Office Business Application (OBA) architecture for

# VB Feeds said on February 7, 2009 11:13 PM:

In the last couple posts we&#39;ve been talking about an Office Business Application (OBA) architecture

# Mandiola said on February 10, 2009 1:09 PM:

Beth you are a master, i just want to tell you thanks for tis greats articles.

# Beth Massi - Sharing the goodness that is VB said on February 12, 2009 8:55 PM:

In my last few posts we've been building a simple Office Business Application (OBA) for the new Northwind

# VB Feeds said on February 13, 2009 2:53 AM:

In my last few posts we&#39;ve been building a simple Office Business Application (OBA) for the new Northwind

# Palle Thorup said on February 25, 2009 6:42 AM:

Hi Beth,

Thank you very much for the great articles. Very interesting subject and a great inspiration.

Based on your articles and samples (ADO.NET Data Services) I have created the entities from the Microsoft Business Solutions XAL system and the server is running.

In the client application, I am trying to create a new record in a single table through the server, but receive an error since some fields are NOT NULLABLE and I created a new object, which initializes the string fields = null. To solve that problem I have used <Entity>.Create<Entity> entering a lot of "" per field (as parameters for the constructor). I would expect that the code for the constructor would take the NOT NULLABLE into account, when the code for the entity constructor is generated, but it doesn't. Is there any way I can force the program that generates the code to do that, so I can simply create a new object and get all fields to contain the default value (e.g. "" for strings) instead of null?

Is there an easier solution?

Thanks in advance.

# Josh K. said on March 14, 2009 2:49 PM:

"The ADO.NET data service" is not available in my project template. Using Visual Studio 2008 SP1 & framework 3.5 on Windows XP sp3. Uninstalled & reinstalled a number of times but still not found... Any ideas??

# Venkata Raghavan said on March 17, 2009 12:41 PM:

Thanks Beth,

Great article and well presented. Just following through the instructions im able to create my first data service. This article had helped me to quickly jump onto the ADO.NET Data Service band wagon and im sure to do more on it.

# Singa said on April 23, 2009 6:12 PM:

I thank you for you professionalism. Your notice gives me idea on what I'm looking for. I want use Excel interface with Data in DataGridview.

I would like to have a tutorial or a guide to do it.

Sincerely, yours

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required

About Beth Massi

Beth is a Program Manager on the Visual Studio Community Team at Microsoft and is responsible for producing and managing content for business application developers, driving community features and team participation onto MSDN Developer Centers (http://msdn.com), and helping make Visual Studio one of the best developer tools in the world. She also produces regular content on her blog (http://blogs.msdn.com/bethmassi), Channel 9, and a variety of other developer sites and magazines. As a community champion and a long-time member of the Microsoft developer community she also helps with the San Francisco East Bay .NET user group and is a frequent speaker at various software development events. Before Microsoft, she was a Senior Architect at a health care software product company and a Microsoft Solutions Architect MVP. Over the last decade she has worked on distributed applications and frameworks, web and Windows-based applications using Microsoft development tools in a variety of businesses. She loves teaching, hiking, mountain biking, and driving really fast.

This Blog

Syndication

Page view tracker