Walkthrough: Accessing an OData Service by Using Type Providers (F#)

Switch View :
ScriptFree
Visual Studio 11 Beta - Visual F#
Walkthrough: Accessing an OData Service by Using Type Providers (F#)

[This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]

OData, meaning Open Data Protocol, is a protocol for transferring data over the Internet. Many data providers expose access to their data by publishing an OData web service. You can access data from any OData source in F# 3.0 using data types that are automatically generated by the ODataService type provider. For more information about OData, see Introducing OData.

This walkthrough shows you how to use the F# ODataService type provider to generate client types for an OData service and query data feeds that the service provides.

This walkthrough illustrates the following tasks, which you should perform in this order for the walkthrough to succeed:

To configure a client project for an OData service

  • Open an F# Console Application project. Add a reference to the Framework assembly System.Data.Services.Client and, under Extensions, the assembly FSharp.Data.TypeProviders.

To access OData types

  • In the Code Editor, open an F# source file and enter the following code.

    F#
    open Microsoft.FSharp.Data.TypeProviders
    
    
    type Northwind = ODataService<"http://services.odata.org/Northwind/Northwind.svc/">
    
    let db = Northwind.GetDataContext()
    let fullContext = Northwind.ServiceTypes.NorthwindEntities()
    
    

    In this example, you have invoked the F# type provider and instructed it to create a set of types based on the specified OData URI. Two objects are available that contain information about the data; one is a simplified data context, db in the example. This contains only the data types associated with the database including types for tables or feeds. The other, fullContext in this example, is an instance of DataContext and contains many additional properties, methods, and events.

To query an OData service

  1. Now that you have set up the type provider, you can query an OData service.

    OData only supports a subset of the query operations that are available. Supported operations and their corresponding keywords are as follows:.

    • Projection (select)

    • filtering (where, using string and date operations)

    • paging (skip, take)

    • ordering (orderBy, thenBy)

    • OData-specific operations AddQueryOption and Expand

    For a discussion of supported operations, see LINQ Considerations.

    If you just want all of the entries in a feed or table, use the simplest form of the query expression, as in the following code:

    F#
    
    query { for customer in db.Customers do
            select customer }
    |> Seq.iter (fun customer ->
        printfn "ID: %s\nCompany: %s" customer.CustomerID customer.CompanyName
        printfn "Contact: %s\nAddress: %s" customer.ContactName customer.Address
        printfn "         %s, %s %s" customer.City customer.Region customer.PostalCode
        printfn "%s\n" customer.Phone)
    
    
  2. Specify desired fields or columns by using a tuple after the select keyword.

    F#
    query { for cat in db.Categories do
            select (cat.CategoryID, cat.CategoryName, cat.Description) }
    |> Seq.iter (fun (id, name, description) ->
        printfn "ID: %d\nCategory: %s\nDescription: %s\n" id name description)
    
    
  3. Use a where clause to specify conditions.

    F#
    query { for employee in db.Employees do
            where (employee.EmployeeID = 9)
            select employee }
    |> Seq.iter (fun employee ->
        printfn "Name: %s ID: %d" (employee.FirstName + " " + employee.LastName) (employee.EmployeeID))                         
    
    
    
  4. Use the System.String.Contains method to specify a substring condition to the query. The following query returns all products with "Chef" in the name. Also notice the use of GetValueOrDefault. The UnitPrice is a nullable value, so you need to either get the value by using the Value property, or call GetValueOrDefault.

    F#
    query { for product in db.Products do
            where (product.ProductName.Contains("Chef"))
            select product }
    |> Seq.iter (fun product ->
        printfn "ID: %d Product: %s" product.ProductID product.ProductName
        printfn "Price: %M\n" (product.UnitPrice.GetValueOrDefault()))
    
    
  5. Use the System.String.EndsWith method to specify that a string ends with a certain substring.

    F#
    query { for product in db.Products do
            where (product.ProductName.EndsWith("u"))
            select product }
    |> Seq.iter (fun product ->
        printfn "ID: %d Product: %s" product.ProductID product.ProductName
        printfn "Price: %M\n" (product.UnitPrice.GetValueOrDefault()))
    
    
  6. Use the && operator to combine conditions in a where clause.

    F#
    // Open this module to use the nullable operators ?> and ?<.
    open Microsoft.FSharp.Linq.NullableOperators
    
    let salesIn1997 = query { for sales in db.Category_Sales_for_1997 do
                              where (sales.CategorySales ?> 50000.00M && sales.CategorySales ?< 60000.0M)
                              select sales }
    salesIn1997
    |> Seq.iter (fun sales ->
        printfn "Category: %s Sales: %M" sales.CategoryName (sales.CategorySales.GetValueOrDefault()))
    
    

    The operators ?> and ?< are nullable operators. A full set of nullable equality and comparison operators is available. For more information, see Linq.NullableOperators Module (F#).

  7. Use the sortBy query operator to specify ordering, and thenBy to specify another level of ordering. Notice also the use of a tuple in the select part of the query. This means that the query has a tuple as an element type.

    F#
    printfn "Freight for some orders: "
    query { for order in db.Orders do
            sortBy (order.OrderDate.Value)
            thenBy (order.OrderID)
            select (order.OrderDate, order.OrderID, order.Customer.CompanyName)
             }
    |> Seq.iter (fun (orderDate, orderID, company) ->
        printfn "OrderDate: %s" (orderDate.GetValueOrDefault().ToString())
        printfn "OrderID: %d Company: %s\n" orderID company)
    
    
  8. Use the skip operator to ignore a specified number of records. Use the take operator to specify a number of records to return. In this way, you can implement paging on data feeds.

    F#
    printfn "Get the first page of 2 employees."
    query { for employee in db.Employees do
            take 2
            select employee }
    |> Seq.iter (fun employee ->
        printfn "Name: %s ID: %d" (employee.FirstName + " " + employee.LastName) (employee.EmployeeID)) 
    
    printfn "Get the next 2 employees."
    query { for employee in db.Employees do
            skip 2
            take 2
            select employee }
    |> Seq.iter (fun employee ->
        printfn "Name: %s ID: %d" (employee.FirstName + " " + employee.LastName) (employee.EmployeeID)) 
    
    

To verify the OData request

  • Every OData query is translated into a specific OData request URI. You can verify the OData request URI, perhaps for debugging purposes, by adding an event handler to the SendingRequest event on the full data context object, as shown in the following code:

    F#
        // The DataContext property returns the full data context.
        db.DataContext.SendingRequest.Add (fun eventArgs -> printfn "Requesting %A" eventArgs.Request.RequestUri)
    
    

    The output of the previous code is:

    requesting http://services.odata.org/Northwind/Northwind.svc/Orders()?$orderby=ShippedDate&$select=OrderID,ShippedDate
See Also

Tasks

Reference

Other Resources