ADO.Net Data Services Part 2 – Using Service Operations with WebGet
This post is part of a series of posts about ADO.Net Data Services, a part of the ASP.Net 3.5 Extensions. In ADO.Net Data Services Part 1 – Building a Simple Web Data Service I had a step by step guide for building a simple web data service, that exposed resources as IQueryable properties of the DataContext. I this post, I will add some Service Operations to the Data Service.
1. Open the Blog Data Service fService Operations rom Part 1. If you haven't had a chance to follow ADO.Net Data Services Part 1 – Building a Simple Web Data Service – this is you chance!
2. Add a Table Valued Function to your database (I will explain why I chose Table Valued Functions and not Stored Procedures later in this post). Run the following script in order to create the PostsByCategory sample Table Valued Function.
create function PostsByCategory
From Posts as p
Join PostCategories as pc
On p.PostID = pc.PostID
Where pc.CategoryID = @CategoryID
3. Add the Table Valued Function to the LINQ to SQL Data Model. Open the LINQ to SQL Designer, and drag the PostsByCategory function from the Server Explorer to the design surface.
This will add a new method, with a signature that matches the parameters of the database function.
Click on this method, and in the properties pane, change the return type of the function to be a list of Posts instead of the Auto-Generated Type.
Confirm the warning message Visual Studio shows up. It tells you that the auto generated type will be removed from the Model.
Click Save and let Visual Studio generate the code for the data entities and the data access. If we now open the generated code, and look for the PostsByCategory method in the DataContext, we will see the following code:
public IQueryable<Post> PostsByCategory([Parameter(Name="CategoryID", DbType="Int")] int categoryID)
Notice that this method is mapped to a Table Valued Function using the Function Attribute. the IsComposable=True tells LINQ to SQL runtime that this is indeed a Table Valued Function and not an ordinary Stored Procedure, which means that it can use it as part of the query sent to the database, and not perform additional filtering in memory. This is the fact that the generated code returns a IQueryable<Post> and not IEnumerable or something else. As you probably remember, ADO.Net Data Services works with IQueryable resources.
4. Add this method as a Service Operation. Open the Web Data Service code, and add the following method.
public class BlogData : WebDataService<BlogDataContext>
public IQueryable<Post> PostsByCategory(int categoryID)
This method is a public method that returns an IQueryable<Post>. It is decorated with the [WebGet] Attribute, which tells the ADO.Net Data Services runtime to expose this method as a Service Operation when building the metadata of the service.
As an implementation, it uses the CurrentDataSource property of the service, that returns the instance of the DataContext that was created in this session / scope, and calls the PostsByCategory method on the DataContext that we created in the previous step.
5. Enable access for the Service Operation. As we already know, ADO.Net Data Services do not allow accessing the service resources without explicitly allowing this in the code. Same goes for Service Operations. In the InitializeService method, similar to the way we allowed accessing the resources, we now allow calling the service operations:
public static void InitializeService(IWebDataServiceConfiguration config)
6. Run and test the service. If you run the project and navigate to the service (http://localhost:2445/BlogData.svc) you'll see the following output:
This output does not contain anything about the Service Operations, and they need to be called directly. To call a Service Operation, we should add the name of the operation with it parameters: http://localhost:2445/BlogData.svc/PostsByCategory?categoryID=2, which now returns the desired output.
Although it was very easy to do, I think that adding the method to the web data service could have done easier. Currently, there is no way to add Service Operations directly from the DataContext, which is only being searched for IQueryable properties. It would be nice to add a Table Valued Function to the Model, let Visual Studio generate a method that returns an IQueryable, and let the service work with it seamlessly.