Partager via


How to open a WCF RIA Services application to other type of clients: the OData endpoint (2/5)

The purpose of this article is to show you how to open the application demonstrated in the previous article via an OData endpoint. We will then discover the limitations of this endpoint in the current release of WCF RIA Services and its differences with the features offered by the WCF Data Services framework. At last, we will see how to consume this endpoint from the Office Excel application and from a web application written with WebMatrix.

This article is the second one out of 5: 

1 – Review of the initial application
2 – How to expose the service as an OData stream consumed by Excel and from a web application built with WebMatrix (this article)
3 – How to expose the service as a “standard” WCF Service used from a WPF application and a Windows Phone 7 application
4 – How to expose the service as a JSON stream to be used by a HTML5/jQuery web application 
5 – Step by step procedure to publish this WCF RIA Services application to Windows Azure & SQL Azure 

Introduction to OData

First of all, we need to have a basic knowledge of what OData is. If you don’t know anything about this protocol/technology yet, here are some articles and webcast I’m suggesting you to read/watch:

1 – The official website OData.org
2 – WCF Data Services and OData At-a-Glance 
3 – The MIX10 session : OData: There's a Feed for That
4 – Beginner's Guide to WCF Data Services

Lastly, we’ve then seen some increasing usage of OData inside our Microsoft products. Developers can easily expose their data by using the .NET 4.0 WCF Data Services framework for instance. This framework will then consume a LINQ source, it will analyze it and it will expose it via HTTP REST. You will then have a CRUD support of your entities. You will have also a LINQ provider on the client side (Silverlight, WPF) to remotely query your data source.

On the producers’ side, SharePoint 2010 can expose its lists through OData. SQL Azure can expose its databases directly through an OData feed by simply enabling a checkbox (currently only available through the beta program of SQL Labs). At last, Windows Azure exposes its data tables via OData also.

On the consumers side, anybody and anything can eat OData whatever the language you’d like to use: .NET (Silverlight, Windows Phone 7 or the rich client experience), PHP, Java, JavaScript, Objective C: https://www.odata.org/developers/odata-sdk . Using OData to expose your data is then the guarantee of a perfect interoperability!

SchemaOData 

Enabling the OData endpoint on a WCF RIA Services DomainService

There is 2 ways to add an OData endpoint to your RIA Services. During the assisted generation, one of the wizard options offers you to add it via a simple checkbox “Expose OData endpoint”:

BookClubScreen008

And you’re done. If you have an existing solution, you can also add the OData endpoint after the wizard process. For that, the first thing to do is to declare it inside your web.config file with the following piece of XML:

 <domainServices>
  <endpoints>
    <add name="OData" type="System.ServiceModel.DomainServices.Hosting.ODataEndpointFactory, 
               System.ServiceModel.DomainServices.Hosting.OData, Version=4.0.0.0, 
               Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  </endpoints>
</domainServices>

Just after the following tag:

 <system.serviceModel>

After that, you need to specify in your DomainService’s code which method will be called by default on the URL /NameOfYourEntity thanks to the following attribute:

 [Query(IsDefault = true)]

For instance, if you’re taking the source code of the Book Club application shared in the previous article, you need to review these 2 following methods:

 [Query(IsDefault = true)]
public IQueryable<Book> GetBooks()
{
    return this.ObjectContext.Books.OrderBy(b => b.Title);
}
 [Query(IsDefault = true)]
public IQueryable<Category> GetCategories()
{
    return this.ObjectContext.Categories.OrderBy(c => c.CategoryName);
}

Once done, the endpoint will be available through this kind of formatted URL:

https://nameofyourserver:port/ClientBin/NameOfYourSolution-Web-NameOfYourDomainService.svc/OData/

This formatting works if you’ve generated your solution by creating it with the “Silverlight Business Application” template. If you added your DomainService manually to your ASP.NET project, this will probably more look like this:

https://nameofyourserver:port/NameOfYourSolution-NameOfYourDomainService.svc/OData/

For instance, with our Book Club application, after adding the OData endpoint, here is the URL to use:

https://localhost:62206/ClientBin/BookShelf-Web-Services-BookClubService.svc/OData/

Because our DomainService is being hosted inside the BookShelf.Web project inside the Services directory. With my current hosting in Azure, you can then freely play with this public URL:

https://bookclub.cloudapp.net/ClientBin/BookShelf-Web-Services-BookClubService.svc/OData/

Thus, if you’re using the Silverlight OData Explorer application with this public URL in Azure, you’ll have this kind of output:

BookClubScreen009

Note: you’ll need to install & run the Silverlight OData Explorer application in the elevated trust “Out of Browser” mode to by-pass the native cross-domains calls limitations of Silverlight as I haven’t set a cross domain policy file on the root of my hosting.

Limitations of the WCF RIA Services OData endpoint and differences with WCF Data Services

Well, OData seems to be wonderful! If I’m exposing my WCF RIA Services layer with OData, am I the king of the world? Well, not yet…

The OData version exposed by WCF Data Services is indeed very powerful and can be used perfectly in open & interoperable scenarios. However, the OData endpoint of WCF RIA Services is unfortunately today less powerful. For instance, if you need to remotely control the WCF RIA Services layer in a complete way from a WPF, Objective-C or even an Android client like a customer recently asked me, I would rather suggesting you to use the SOAP endpoint (more or less like a classic WebService then) or to use the JSON endpoint like we will see in the next 2 following articles.

So why is it less powerful? Simply because the OData endpoint only supports read-only access to the entities exposed by RIA Services and it doesn’t support query operations neither. This is something we will maybe change in the next version of RIA Services but in the meantime you will have to deal with those limitations of this v1.0.

Still, if those limitations aren’t a problem for you, that’s perfect! You will then be able to cover these needs:

1 – The main need in your project is to build a Web RIA application and you’ll then need to have the maximum productivity on this point

2 – You need to expose the same data in read-only mode to other type of clients without having the need to remotely drive the server data source to add filtering or sorting logic 

The main need will be cover by the very high SL4 – WCF RIA Services – ASP.NET 4.0 productivity chain. The second need will be cover by the OData endpoint. You will then be able to connect mobile devices like Windows Phone 7, Android or iPhone iOS on your services without problems.

odatasdkforiphomeobjectivec

Review of the OData support of WCF Data Services

In order to better understand the RIA Services OData limitations, let’s first review the native features offered by WCF Data Services. For that, we will use the famous NorthWind SQL Server sample publicly available via this OData feed: https://services.odata.org/Northwind/Northwind.svc/

We can consume this database with the following queries (you can test these URLs inside your browser to see the output or inside a tool like the Silverlight OData Explorer):

https://services.odata.org/Northwind/Northwind.svc/Customers/ to return the list of all customers available in the database

https://services.odata.org/Northwind/Northwind.svc/Customers('ALFKI')/ to return a specific customer based on its Id (‘ALKFI’ here)

https://services.odata.org/Northwind/Northwind.svc/Customers('ALFKI')/Orders to return the list of all linked orders to the ‘ALKFI’ customer.

https://services.odata.org/Northwind/Northwind.svc/Customers('ALFKI')/Orders?$filter=OrderID gt 10700 to query among all the orders done by the ‘ALKFI’ customer and to return only the orders having an OrderID greater than 10700.

By the way, if you have a .NET client (Silverlight or WPF) connecting to this OData feed, you will be able to enjoy the power of the LINQ provider and the WCF proxy on the client side. For instance, the previous HTTP request/query could be rather translated into this nice LINQ query:

 var ordersQuery = from o in context.Orders
    where o.Customers.CustomerID == "ALFKI" && o.OrderID > 10700
        select o;

An expression tree will then be built in memory, will be analyzed and transformed into the appropriate HTTP request when we’ll need it.

As a reminder also, don’t forget that you can add, update or delete items on the OData feed available with WCF Data Services.

Review of the OData support of WCF RIA Services

On the WCF RIA Services side, let’s review the kind of requests we can do:

https://bookclub.cloudapp.net/ClientBin/BookShelf-Web-Services-BookClubService.svc/OData/BookSet to return the list of all books available in my SQL Azure database… and… that’s all!

Indeed, if you tries more complex requests for instance by selecting a specific item:

https://bookclub.cloudapp.net/ClientBin/BookShelf-Web-Services-BookClubService.svc/OData/BookSet(39)

or by trying to set a filter:

https://bookclub.cloudapp.net/ClientBin/BookShelf-Web-Services-BookClubService.svc/OData/BookSet?$filter=BookID gt 39

You will respectively receive the 2 following exceptions: “Requests that attempt to access a single element using key values from a result set are not supported. ” and “Query options are not allowed. ”. Well, you won’t say for this time that our error messages aren’t clear enough. Clignement d'œil

Note : if you need further explanations on the alignment between both framework, you can read this article : WCF, Data Services and RIA Services Alignment Questions and Answers

Otherwise, you can still use both!

Another option is to simply to use both frameworks. What would be the benefits? Again, let’s talk about a specific scenario I’ve seen with one of my customers:

1 – You need to create a Web RIA application based on Silverlight and you need to have the optimum productivity on this application. This will be for instance the main back office application used internally by your company. 
2 – You need to expose the same data on the public internet with support of read/write operations as well as queries operations for other types of clients like the iPad/iPhone/Windows Phone 7/Android devices.

For the first point, this is still the Silverlight 4 & WCF RIA Services couple that will help you the most. You will then benefit from the tiedly .NET 4.0/Silverlight 4 couple for a high productivity chain offering a lot of the logic for you on the client side: high level controls, client-side validation, etc.

For the second point, if you’re enabling the SOAP endpoint on your RIA Services DomainServices (like we’ll see together on the next article), you will offer read/write operations on your entities for the mobile devices specified above. However, you won’t have the native support of query operations like we just see in the full OData support of WCF Data Services. We could then imagine injecting the data model used by your RIA Services layer into WCF Data Services.

Indeed, if you’re using an Entity Framework model like we’re currently using in the Visual Studio 2010 solution shared in the previous article, the WCF Data Services support will be added in a few mouse clicks. For that, add a new “WCF Data Service” element inside the “Services” directory and named it “BooksDataService”:

BookClubScreen015

Ask to the WCF Data Services framework to analyze the BookShelf.Web.Models.BookClubEntities model. This is the same model currently used by our RIA Services layer. You then have to choose which entity you’d like to expose with which level of rights (read, write, etc.). For instance, this piece of code is exposing all entities available in our model using the full level of rights (read/write):

 public class BooksDataService : DataService<BookShelf.Web.Models.BookClubEntities>
{
    public static void InitializeService(DataServiceConfiguration config)
    {
        config.SetEntitySetAccessRule(" * ", EntitySetRights.All);
        config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
    }
}

You will then have a new URL available exposing this time a full OData feed (read, write, queries, filtering, etc.). In my case, here is the Azure URL:

https://bookclub.cloudapp.net/Services/BooksDataService.svc/

Thus, we will be able to execute more complex search operations. If we’re taking as a base the sample demonstrated in the previous article, we were querying the books members of the “Technology” category which the author contains the keyword “Papa”. We can then now build the following corresponding HTTP request with our full OData feed:

https://bookclub.cloudapp.net/Services/BooksDataService.svc/Categories(1)/Books?$filter=substringof('Papa', Author) eq true 

Updating or deleting entities will be possible also thanks to the level of rights you will set with the SetEntitySetAccessRule() method. If you’re looking for more query operators, you can have a look here: https://www.odata.org/developers/protocols/uri-conventions#QueryStringOptions

Drawbacks: of course, there is some inconvenient to use the 2 frameworks in the same time in your project. First of all, this implies a double maintenance of the code as you’re rewriting some of the same logic in 2 different areas. Moreover, you have to handle the security access on the 2 frameworks also. Indeed, if you’re allowing updates only for a specific set of users using the security attributes of WCF RIA Services, you will have to reflect these security constraints on the WCF Data Services layer also. You will then see in the next article the benefits of using a unique framework (RIA Services) to expose our data on which we’re setting the appropriate set of rights.

Consuming the OData endpoint of RIA Services from Excel

After this relatively long – but important – introduction, let’s now see how to consume our RIA Services layer from Excel.

Note: we will use here the native features of RIA Services and its limited OData endpoint. We won’t use here the full OData logic offered by WCF Data Services.

The first thing to do is to download the PowerPivot Add-in for Excel 2010: https://www.powerpivot.com/download.aspx 

Once you’ve installed the add-in, you’ll notice a new section named PowerPivot in the Excel ribbon. Click on the “PowerPivot window” button:

BookClubScreen010

Then click on the “import from a data feeds” button:

BookClubScreen011

Enter the URL pointing on the books returned by my OData endpoint hosted in Azure: https://bookclub.cloudapp.net/ClientBin/BookShelf-Web-Services-BookClubService.svc/OData/BookSet :

BookClubScreen012

You should have this kind of window indicating that the import process worked as expected:

BookClubScreen013

You can now manipulate the data in Excel to do sorting operations, generating some charting, etc.:

BookClubScreen014

At this stage, we can already load a consequent amount of data inside the Excel client that will load all the entities in memory for further operations.

Consuming the OData endpoint of RIA Services from an application written with WebMatrix

If you don’t know WebMatrix yet, you can discover this new free tool that helps you to simply build web applications via this set of nice tutorials & walkthroughs: https://www.asp.net/webmatrix/tutorials . If you’re an ASP developer who hasn’t moved to ASP.NET yet or if you’re a PHP developer curious to see what’s going on in the Microsoft space, this tool should interest you.

Ok, let’s go. Let’s discover that thanks to the powerful helpers shipped for WebMatrix (that you can re-use in ASP.NET MVC 3 applications by the way) and thanks to the new Razor syntax, we’re going to build a nice little web application in a couple of steps to display our books stored in SQL Azure and available via our OData endpoint.

Note: if you’re an ASP.NET developer and you don’t know the Razor syntax yet, you have to read Scott Guthrie’s posts on this topic! The introduction post is here: Introducing “Razor” – a new view engine for ASP.NET

1 – Run WebMatrix, create a new site from the “Starter Site” template and named it “WebMatrixBookClub”:

WebMatrix001

Change the master page “ _SiteLayout.cshtml” to find & replace the string “My ASP.NET Web Page” referenced 3 times by “WebMatrix BookClub Application”.

Then change the “Default.cshtml” page by this content:

 @{  
    Layout = "~/_SiteLayout.cshtml";
    Page.Title = "List of available books";
}
<p>
    Display here the list of books from the OData endpoint
</p>

You can already start testing the result by pressing on the “Run” button or the “F12” key:

WebMatrix002

You’ll obtain this ultra-simple page:

WebMatrix003

2 – To get access to the data from the OData endpoint, we need a specific helper which is not installed by default with WebMatrix. Fortunately, it’s available online and it’s very simple to install. Navigate to the same URL by just adding “ /_Admin”:

WebMatrix004

Create a new password as requested and display the packages available online to download & install the OData helper:

WebMatrix005

WebMatrix006

If everything goes ok, you will have this success message:

WebMatrix007

3 – Now, we can start coding the logic that will get the data and format it in our HTML page. For that, open the “Default.cshtml” page and replace its content by this one:

 @{  
 Layout = "~/_SiteLayout.cshtml";
 Page.Title = "List of available books";
    
 var result = OData.Get("https://bookclub.cloudapp.net/ClientBin/BookShelf-Web-Services-BookClubService.svc/OData/BookSet");
 var grid = new WebGrid(source: result,                           
            defaultSort: "Author",                            
            rowsPerPage: 10);            
}

<p>
    @grid.GetHtml(
        columns: grid.Columns(
            grid.Column("Author"),
            grid.Column("Title"),
            grid.Column("ASIN")))
</p>

We’re using here the OData helper to send the request to our WCF RIA Services endpoint thanks to the OData.Get() method. After that, we’re providing the result to another helper named WebGrid. We ask it to sort the data by “Author” and to display it on pages with 10 elements maximum.

At last, rather than displaying all the columns, we’re asking to the helper to only display the “Author”, “Title” & “ASIN” columns via the GetHtml() method .

If you now re-launch the application, you’ll have this nice output:

WebMatrix008

Very easy to code, isn’t it? In only 3 steps, we’re getting the data from the OData endpoint and we’re formatting them in a nice high level WebGrid control!

If you’re curious about the other features available inside the WebPages technology used by the WebMatrix tool, you can navigate here: https://www.asp.net/webmatrix/tutorials/asp-net-web-pages-api-reference . You’ll find other kinds of very interesting helpers to display a Twitter feed, to let your users say they like this page in FaceBook, etc. You can even re-use this new productivity approach and this new Razor syntax into bigger projects based on ASP.NET MVC 3.

That concludes this OData article. See you in the next article where we’ll see how to expose the same service via a SOAP endpoint to enable a full access to WPF and/or Windows Phone 7 applications.

David

Comments

  • Anonymous
    January 09, 2011
    Hi David, Nice Series, Currently I am struggling with a project where I want to reuse the Ria Services of a silverlight app in a WP7 app. Since Consuming an OData service is the only built in support for WP7 at this moment, I exposed my Ria Service as an OData endpoint. All works fine except. When consuming a plain old WCF Data Service the entity references are created into the model I generated with the DataSvcUtil. But this is not the case when I consume a WCF Ria Service that was exposed as a OData Endpoint. Any Ideas? Or suggestions on Reusing existing WCF Ria Services on a WP7 app without losing entity references. Thx in advance.

  • Anonymous
    January 09, 2011
    Hi Stijn, No, you should rather use the SOAP endpoint of your RIA Services layer for your WP7 client. I've shown you a sample here : blogs.msdn.com/.../how-to-open-a-wcf-ria-services-application-to-other-type-of-clients-the-soap-endpoint-3-5.aspx on how to expose the RIA Services layer via SOAP for a WP7 client. You will then have a full CRUD support + entities model created on the client thanks to this SOAP endpoint. Regards, David Rousset

  • Anonymous
    March 20, 2011
    Hi David, very useful info you have put together here. Do you have anything to add on validation? particularly sharing rules between entity framework/ria services and mixed clientss e.g. Silverlight and ASP.Net. How would you do this? Thanks Again