演练:使用类型提供程序访问 OData 服务 (F#)

OData 代表 Open Data Protocol,它是用于通过 Internet 传输数据的协议。 许多数据提供商通过发布 OData Web 服务来公开对其数据的访问。 您可以通过 F# 3.0 使用由 ODataService 类型提供程序自动生成的数据类型, 有关 OData 的更多信息,请参见 Introducing OData

本演练显示如何使用 F# ODataService 类型提供程序生成 OData 服务和查询服务提供的数据输入的客户端类型。

本演练演示以下任务,您应该按该演练的顺序执行才能成功:

  • Configuring a client project for an OData service

  • Accessing OData types

  • Querying an OData service

  • Verifying the OData requests

配置 OData 服务的客户端项目

在此步骤中,设置项目可以使用 OData 类型提供程序。

配置 OData 服务的客户端项目

  • 打开 F# 控制台应用程序项目,然后添加对 System.Data.Services.Client 框架程序集的引用。

  • 在**“扩展名”**下,请添加对 FSharp.Data.TypeProviders 程序集的引用。

访问 OData 类型

在此步骤中,创建提供访问类型和 OData 服务的数据的类型提供程序。

为了访问 OData 类型

  • 在“代码编辑器”中,打开 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()
    

    在此示例中,已调用 F# 类型提供程序并指示其根据指定的 OData URI 创建一组类型。 包含有关数据的信息的两个对象可用;一个是一种简化的数据上下文,另一个则是示例中的 db。 此对象仅包含与数据库关联的数据类型(包括表或源的类型)。 其他对象,fullContext 在此示例中,是 DataContext 并包含许多其他属性、方法和事件的实例。

查询 OData 服务

在本步骤中,您使用 F# 查询表达式可以查询 OData 服务。

查询 OData 服务

  1. 现在,既然已设置类型提供程序,就可以查询 OData 服务。

    OData 仅支持可用查询操作的子集。 支持以下操作及其相应的关键字:

    • 投射 (select)

    • 筛选(where,使用字符串和日期操作)

    • 分页 (skip, take)

    • 顺序 (orderBy, thenBy)

    • 特定于 OData 操作的 AddQueryOption 和 Expand

    有关更多信息,请参见LINQ Considerations

    如果只想要源或表中的所有项,请使用查询表达式的最简单的窗体,如以下代码所示:

    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. 通过使用 select 关键字之后的元祖指定所需字段或列。

    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. 使用 where 子句来指定条件。

    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. 使用 Contains 方法来指定要查询的子字符串条件。 下面的查询返回带有“厨师”名称的所有产品。 此外,请注意 GetValueOrDefault 的使用。 UnitPrice 是可以为 null 的值,因此,您必须使用 Value 属性或您必须调用 GetValueOrDefault 来获取值。

    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. 使用方法 EndsWith 来指定以包含子字符串结尾的字符串。

    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. 在 WHERE 子句中,使用 && 运算符组合条件。

    // 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()))
    

    运算符 ?> 和 ?< 是可以为 null 的运算符。 一组完整的可以为 null 的等式和比较运算符可用。 有关更多信息,请参见Linq.NullableOperators 模块 (F#)

  7. 使用 sortBy 查询运算符来指定排序,以及 thenBy 指定排序的另一个级别。 还请注意,在查询的选择部分使用元组。 因此,查询具有一个作为元素类型的元组。

    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. 通过使用 Skip 运算符来忽略指定的记录数并使用 Take 运算符指定要返回的记录数。 通过这种方式,可以对数据源实现分页。

    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)) 
    

验证 OData 请求

每个 OData 查询都转换为特定的 OData 请求 URI。 可能出于调试的目的,可通过将事件处理程序添加到完整数据上下文对象上的 SendingRequest 来验证 URI,如以下代码所示。

验证 OData 请求

  • 若要验证 OData 请求 URI 类型,使用以下代码:

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

    先前的代码的输出为:

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

请参见

任务

查询表达式 (F#)

参考

ODataService 类型提供程序 (F#)

其他资源

LINQ Considerations