此文章由机器翻译。

OData

可视化流数据与 OData 最简单的方法

Louis Ross

下载代码示例

开放数据协议 (OData) 是 rest 风格的协议,旨在使功能强大的查询以及对后备存储,通常是 SQL 数据库中的数据修改操作。在 HTTP 响应中返回的结果与通过 HTTP 请求的 URL 形成资源表达式和查询。复杂的查询,它可以塑造、 顺序、 筛选器和页面的数据请求的支持是建立在通过一种查询语言。OData 是 OASIS 标准,因为它得到广泛实施,并可以跨所有流行的客户端平台,如 Web 浏览器,以及电话和基于 Android 和 Windows 的 iOS 设备消耗。OData 经常被看作是好的方式,提供可以容易地消耗跨多个平台的基于标准的服务。一些首次置业的链接,请参阅"其他参考"。

在本文中,我将演示为什么语义的 OData 使它完美车辆因揭露近实时时间序列流数据,以及数据从 SQL 备份存储区。(我使用时间数列来区分从静态的后备存储区中。这避免了使用实时,在不同上下文中具有不同的含义)。

这篇文章将演示示例执行使用 OData 服务工业自动化时间序列数据流能力。图 1 演示示例测试客户端,写在 C# 中使用 Windows 演示文稿基础 (WPF),连接到服务并使用简单的 LINQ 查询来处理时间序列数据的流。我不久就会讨论这在更多的细节。如前所述,许多可能的客户端存在,包括基于浏览器和设备应用程序。

样品 OData 测试客户端写在 C# 中使用窗口演示文稿基础Foundation
图 1 样品 OData 测试客户端写在 C# 中使用窗口演示文稿基础

客户端连接的 OData 服务来管理多个项目和订阅。每个订阅中的项目的成员,该订阅更新的时间间隔和动态监测中的订阅所有通过可以管理测试客户端。我将涵盖项目和订阅的更多细节稍后的概念。图 2 正在流从一个订阅的时间序列数据显示的测试客户端,另一个窗口。

时间序列数据流从 OData Service 中的订阅
图 2 时间序列数据流从 OData Service 中的订阅

这种技术可以扩展到任何时间序列数据从设备上物联网 (物联网)。随着物联网的不断打造出了,从雨传感器到销售点终端设备越来越可用直接在互联网上。进一步这个过程去,越需要简单、 一致的方式来访问和管理的所有数据。这项技术我展示在这里非常适合于在 internet 上,允许数据从物联网设备能够被计算机和其他设备,使信息可访问的、 有意义。

我举的例子是用于演示目的,和范围能力为明确起见:它公开的项目都是相同的整数类型 ; 提供数据的"设备"是一种模拟 ; 并没有试图来验证用户的身份或提供用户身份和每个用户会话。所有这些事情将是必要的一家生产服务,但出于演示的目的已被淘汰。

设计实体模型

定义 OData 服务的第一步设计的实体模型。OData 服务暴露在特定的基 URL 例如 http://localhost:10001/TimeSeriesData.svc,和下面这基 URL 的每个 URL 分段表示一种不同的实体模型内的资源。基 URL 可能会更改,具体取决于如何部署了 OData 服务,但实体模型将决定一组固定的资源 URL 分段在它之下。

由数据库支持的典型 OData 服务,可以使用自动化的工具,例如 ADO.NET 实体框架建模资源。我的时间序列 OData 服务,另一方面,公开资源作为内存中的结构,所以我用手来生成实体模型。一套合理的流媒体服务的时间序列数据的资源可能是项目、 订阅和样品:

获取所有项目:http://localhost:10001/TimeSeriesData.svc/Items 应返回可用设备,该服务提供的数据中的所有项。每个项目应该有属性 (如 id、 名称、 类型、 状态、 读写能力,当前值等。

获取所有的订阅:http://localhost:10001/TimeSeriesData.svc/Subscriptions 应返回已创建并坚持以以前调用该服务的所有订阅。订阅是一种机制来对项目进行分组到集合。订阅附加功能是分组到每个订阅的项目在一起是以特定速率抽取的样本。每个订阅应具有属性,如 id、 订阅间隔、 其收藏的物品,其收藏的那些项目的样品。

获取所有样本:http://localhost:10001/TimeSeriesData.svc/Samples 应返回由服务全球公开的所有样品。对于本文来说,此查询永远不会返回任何东西,因为在现实生活中的场景,这是毫无意义的查询。样品都与一个订阅,所以应该没有质疑的根本原因相关联。然而,要暴露样本作为实体的 WCF 数据服务,根查询必须有效。

除了这三个基本的实体,我添加了它们之间的关联。订阅资源是与样品和项目从一到多关联。

获取所有样本内订阅:http://localhost:10001/TimeSeriesData.svc/订阅 25/样品应返回可用于订阅 25 的所有样品。 这假定客户端以前创建了订阅实体与 SubscriptionId = 25。 每个样本应具有属性 (如身份证件 ; 进行采样 ; 项的 id 和值、 时间和它进行采样时在该项目的质量。

获取订阅中的所有项:http://localhost:10001/TimeSeriesData.svc/订阅 25/项目应返回的当前订阅 25 与相关联的项的集合。 这假定客户端以前创建订阅的实体,与 SubscriptionId = 25 和与它相关联的项目。

图 3 显示时间序列 OData 服务的实体模型。项目是在设备中,通常通过一个字符串的名称或数字 id 可寻址的物理实体。 项目通常包含由该设备,如温度或压力,以及元数据 (如时间戳的最新的样品和质量的测量值。作为图 3 所示,他们由其数值 ItemId 公开作为 OData 实体。

由时间序列 OData 服务公开的实体模型
图 3 由时间序列 OData 服务公开的实体模型

作为一个实体,订阅是发明一起设备项目的子集,并在一段固定的时间,从该子集收集样本的小说。因为订阅并不真的存在,在要生成的实体,在这种情况下他们似乎支持的一家商店的服务必须有某种机制。

抽样实体都比订阅更短暂。示例实体被发明来捕获特定时刻的值和元数据的单个项目。当订阅运行时,它通常积累抽样实体需要捕获与该订阅关联项的近期的样本。与样品实体小说是辽阔的实体存在履行 OData 请求。在现实中,抽样实体被删除从存储一样快,它们返回到客户端。因为客户端通常不感兴趣不止一次获得相同的样本,但这不是一个问题。

这些内存中抽象状态删除服务运行时的方式处理关键在于允许 OData 服务规模的解决方案 — — 从客户端的顺序调用不应该依赖于服务记住有关订阅或在以前的调用中的样品处理的详细信息。

这意味着抽象的实体,如订阅和样品必须永远不会记住作为 OData 服务的国家。相反,他们必须考虑支持服务或设备的一部分。在我的示例 OData 服务,我实现了一个模拟层,以消除任何对真正的硬件的依赖性。实体提供了模拟的硬件,并简单地通过 OData 服务公开。尤其是,项目和订阅的实体将保留由模拟层到 XML 文件。

示例实体是一种特殊情况。样品都是虚拟的在意义上没有样品实体存在于模拟的硬件,但样品也是短暂的。典型的时间序列客户 — — 而不是历史的客户 — — 感只有在最近期的样本。一旦读取这些样品时,他们可以丢弃在服务中。在我的示例代码中,我提供一个唯一的 Id,每个示例实体和保持 OData 客户端可能会随时问为真的老了样品的小说。然而,旧样品被遗忘模拟的硬件层,所以实际上这样做的任何客户端将会失望了。

执行使用 WCF 数据服务的示例 OData 服务

既然定义了实体模型下, 一步是实现 OData 服务。ASP.NET Web API 框架是一个强大的现代选择,但在年长的 WCF 数据服务,为其简单的实现的基础上下降回来的了。

TimeSeriesDataService 类公开服务模板类 DataService 中派生:

public class TimeSeriesDataService : DataService<TimeSeriesEntities>
{
  public static void InitializeService(DataServiceConfiguration config)
  {
    // Initialize the service here...
  }
}

DataService 基类的模板参数指定的类的定义这项服务,时序的实体模型­实体。若要允许在前面的设计一节所示的查询,TimeSeriesEntities 类样品、 项目和订阅的实体作为公开可查询的集合,如中所示图 4

图 4 中的代码的实体模型

public partial class TimeSeriesEntities
{
  public IQueryable<Sample> Samples
  {
    get { return (new List<Sample>()).AsQueryable<Sample>(); }
  }
  public IQueryable<Item> Items
  {
    get
    {
      return (from item in ItemRegistry.Items
              select new Item(item.Id,
                              item.Name,
                              item.Value,
                              item.Time,
                              (int)item.Quality))
               .AsQueryable<Item>();
      }
  }
  public IQueryable<Subscription> Subscriptions
  {
    get
    {
      return (from subscription in SubscriptionRegistry.Subscriptions
              select new Subscription() { SubscriptionId =
              subscription.Id })
             .AsQueryable<Subscription>();
    }
  }
}

通过提供公共属性,返回 IQueryable < T > 集合,您启用 WCF 数据服务框架来处理所有强大 OData 查询可以表示为 HTTP Url。可查询的集合,提供您的代码和框架执行实际的筛选、 排序、 模式匹配,等等。

使 OData 服务可写

要允许实体在该服务中被修改,你必须添加一个小的更多代码。通过修改命令后,付诸表决或删除动词使用 HTTP 客户端发送的响应的 WCF 数据服务框架将会调用此代码。一个典型的时间序列数据服务允许客户端修改以各种方式服务的状态:写入值的物品 ; 创建和删除订阅 ; 添加和删除项目的订阅 ; 和更多。

使用 WCF 数据服务时,可实现 IUpdatable 接口提供此修改方面的能力。当客户端发送 HTTP POST 一份详细的实体创建时,数据服务框架处理该请求,将请求传递到 12 的 IUpdatable 接口方法,根据需要。

我第一次的服务修改代码中可创建和删除订阅。你需要来实现其他 IUpdatable 接口方法,以支持其他修改功能,如一个值写入一项。

我订阅的创建和删除通过启用执行的 CreateResource 和 DeleteResource IUpdatable 的方法,如中所示图 5

图 5 创建和删除订阅

public partial class TimeSeriesEntities : IUpdatable
{
  object IUpdatable.CreateResource(string containerName,
    string fullTypeName)
  {
    Type t = Type.GetType(fullTypeName, true);
    return Activator.CreateInstance(t);
  }
  void IUpdatable.DeleteResource(object targetResource)
  {
    MethodInfo deleteMethod =
      targetResource.GetType().GetMethod("Delete");
    if (deleteMethod != null && deleteMethod.IsPublic)
    {
      deleteMethod.Invoke(targetResource, null);
    }
  }
}

WCF 数据服务框架负责管理的职位,付诸表决,并删除客户端调用,大部分,只是需要我的代码来处理任何特定于我的内部实现的类型。您可以选择执行静态工厂方法或使用反射来创建新的实例,如中所示图 5。我选择机制要删除资源对象依赖于实现的细节 — — 我的资源的对象都公开要求删除,可以从 DeleteResource 调用一个方法。您可以选择所需的其他实现。

OData 客户端变得更便捷,使用 LINQ

不管如何时间序列 OData 服务的实现和暴露,其威力在于消费的客户端可以用它做什么。这是与 Microsoft.NET Framework 中,尤其如此,因为 LINQ 具有一组直接向 OData 服务,向尽可能服务传递尽可能多的查询接口的扩展。使用 LINQ 便于客户端代码来管理复杂的查询,包括筛选、 排序和其他相对轻松。

在 Visual Studio 中创建 OData 客户端

与我的样品 OData 服务运行,我开始 Visual Studio 并添加服务引用工具指向的 OData 服务的基 URL。该工具检查 OData 元数据和创建 System.Data.Services.Client.DataServiceContext,从派生的类并称之为 TimeSeriesEntities。此类为 OData 服务,自定义,包括属性的返回类型的 DataServiceQuery < TElement > 为每个 IQueryable < TElement > 自定义的对象 在 WCF 数据服务的代码的集合:

public DataServiceQuery<Sample> Samples { get; }
public DataServiceQuery<Item> Items { get; }
public DataServiceQuery<Subscription> Subscriptions { get; }

每个这些 DataServiceQuery < TElement > 实例实现 IEnumerable,所以每个人都是 LINQ 查询的目标。在可能的情况下,LINQ 查询形成针对 DataServiceQuery < TElement > 对象将转化 OData 查询,允许很多的筛选、 排序和模式匹配发生在服务。

下面的示例中,将使用服务基地网址 http://localhost:10001/TimeSeriesData.svc,包含在变量 dataAddress。为所有查询下面的示例中,名为 dataContext 的 TimeSeriesEntities 对象被假定存在,并已初始化使用服务的基 URL:

TimeSeriesDataFeed.TimeSeriesEntities dataContext =
  new TimeSeriesDataFeed.TimeSeriesEntities(dataAddress);

作为一个初学者的例子,OData URL http://localhost:10001/TimeSeriesData.svc/Items 是通过以下的 LINQ 查询生成的:

var serviceQuery = from item in dataContext.Items
                   select item;

返回的 LINQ 查询可以与执行:

serverItemsList = serviceQuery.ToList();

发送 URL 和接收以及导致枚举所有项目实体对象的可用服务根级别的 OData 响应进行解析。

请注意此查询类似于浏览设备命名空间中的所有项目。在其他协议中,浏览像这是添加的功能,和很多精力来定义和执行功能的能力。随着 OData,它都是免费的 !

一个更微妙的查询可能会以枚举当前与特定的订阅,例如,与 SubscriptionId 订阅关联的所有项 = 25。 LINQ 查询中:

var subscriptionQuery =
  (from subscription in dataContext.Subscriptions.Expand("Items")
  where subscription.SubscriptionId == 25
  select subscription).FirstOrDefault();

生成 OData URL:

http://localhost:10001/TimeSeriesData.svc/Subscriptions(25)/?$expand=Items

对 FirstOrDefault 的调用取消引用返回的 LINQ 查询并执行它,将该 URL 发送及接收的响应进行解析。这一次,结果是具有与此订阅关联的所有项目实体对象的单个订阅实体对象。订阅项目可以与枚举:

serverItemsList = serverSubscription.Items.ToList();

客户端代码拉发表的样品从订阅使用 LINQ 查询像这样:

var subscriptionWithSamplesQuery =
  (from subscription in dataContext.Subscriptions.Expand("Samples")
  where subscription.SubscriptionId == 25
  select subscription).FirstOrDefault();

这会生成 OData URL:

http://localhost:10001/TimeSeriesData.svc/Subscriptions(25)/?$expand=Samples

在这种情况下,样品与订阅实体关联的实体包含许多项目实体,可能与订阅关联的每个项目的一个或多个样品的采样的值。只要我使用 LINQ,我会与当地 LINQ 查询操作以前的 LINQ 查询所返回的 IEnumerable 集合按项目分组样本:

var samplesInSubs = from sample in subscriptionWithSamplesQuery.Samples
                    group sample by sample.ItemIdSampled into sampleSubs
                    select sampleSubs;

这个例子是基本课程使用的时间系列数据采集为例。它演示如何自然生成 OData 交易记录的 LINQ 查询可以转向操纵本地数据的内存中 LINQ 查询。在这一点上,samplesInSubs 是 IEnumerable IEnumerable 的样品,按 ItemId 分组的组的集合。它是自然的客户端要处理此表单中的数据。

OData 查询这些例子甚至不开始触摸 OData 的表现力。更多的筛选、 排序和采样是可能的。链接到更多示例,请参阅"其他参考"。

一个简单的 WPF OData 测试客户端

把一切都放在一起,我写了小的 WPF 应用程序来展示时间序列的概念处理来自 OData 服务的数据。所示的 OData 测试客户端数字 6-10 使用 LINQ 查询我探索了早些时候,再加上几个其他人,要从中读取数据并将数据写入样品 OData 服务。

主屏幕是当第一次启动时 (见图 6),它使多个 OData 查询,允许它填写列表框中的服务,以及名称和 Id 的服务中的所有项目的所有订阅。

OData 测试客户端的主窗口
图 6 OData 测试客户端的主窗口

图 7,你看到选择左侧的列表框中的订阅读取的项目的名称和 Id 到右侧的列表框,允许您读取和设置该订阅的更新间隔,该订阅中的项目,使该订阅,当我走,我将描述几个命令按钮。

订阅处于选中状态
图 7 订阅处于选中状态

于是在 CodePlex,发现优秀的动态数据显示项目用于生成多笔动态显示,没有讨论时间序列数据流是条形图表记录器,不完整的。(请注意 Silverlight 库动态的数据显示是微软研究院的产品,其许可证规定图书馆对于非商业使用只)。单个订阅中的所有项的数据卷按预期从右到左。你得到一个订阅监视器对话框类似于图 8 每次你选择订阅并按显示器。

监测单个订阅
图 8 监测单个订阅

除了监控时间序列数据从多个项目,相同的 OData 查询可用于执行项目的浏览,如中所示图 9。从单个订阅或作为一个整体服务的所有项目都显示在右侧的列表框中,但浏览也意味着选择哪些项要包含在订阅中。要得到一个浏览窗口,请选择左边的订阅并按浏览按钮。更改向订阅由移动项目 (离开订阅) 或向右 (进订阅) 在该服务中进行更新,并保持每次服务调用由,所以没有 Save 按钮需要。

浏览项目列入订阅
图 9 浏览项目列入订阅

最后,可以一次,监视多个预订,如中所示图 10

一次监测四个订阅
图 10 一次监测四个订阅

挑战和限制 OData 时间序列流数据

虽然我公司专业从事高密度、 高吞吐量的时间序列流媒体服务和客户端,没有尝试取得了 (尚) 来量化这种使用 OData 的性能。当我比较我 OData 的方式,打击我的行业中常见的其他方法,我看到潜在的一些缺点:

  • 在当今的网络环境没有安全保障,包括强制用户身份验证和可选的加密,可以安全地使用没有数据采集机制。显而易见的方法来提供这些功能是从 HTTP 转移到 HTTPS。公钥基础结构 (PKI) 管理的额外负担和 CPU 和带宽资源的加载,可能使这种方法不可行的对于某些应用程序。
  • 在这篇文章中详细阐述的做法需要服务坚持某种状态的多个订阅定义等。当结合用户身份验证,这意味着一些国家必须坚持在服务端为每个用户。出于可伸缩性方面的考虑,必须小心以确保任何所需的状态信息将纳入实体模型,而不能实施作为服务本身的一部分。只要仔细保存服务本身无国籍问题,服务应该是线性的多租户云托管与添加的客户和服务器农场应该有可能。
  • 为进行快速的重复的服务请求的客户端缓存的响应可以是一个性能增强。这个问题有两种口味:(x) 订阅 / 样品实体集和其他一切。样品从特定的订阅是可能要到目前为止,最常见的请求,因为这就是如何获得了流数据。这适用于服务的设计,因为订阅样品是由定义总是记忆。如果其他类型的请求发生与足够的频率是一个问题,可能需要考虑附加缓存。
  • OData 钢丝性能可能会限制其有效的物料密度或吞吐量。在我公司,我们还没打的度量来确定这一点,并就只有一种可能性。与基于 SOAP 传输相比,JSON 有效载荷从 OData 可媲美到 SOAP 信封的开销。
  • OData 和其他基于 REST 的服务向客户端提供没有机制报告的异常,或推式通知的重要事件。转让必须由客户端请求的所有数据,因此,只有拉模型都是可能的。这种限制是大多数其他协议用于工业能够穿越 Internet 相媲美。例如,OPC UA 是业界标准的基于 SOAP 协议,,需要被泵使用发布请求循环驱动的客户端的通知。这项技术未来发展的可贵探索将探索那么 SignalR 服务器端推送技术的使用。

从这篇文章中的例子,似乎明确 OData 是一个可行的方法有以下几个好处:

  • 客户端代码更加易于管理和部署。
  • 多个客户端平台 — — 从手机到桌面 — — 可以使用相同的数据。
  • OData 是一个可行的方法,为几十年到几个几百个项目和吞吐量达每秒几个几百个样品的密度,但未经过高性能。

在我的下一篇文章,我会跟进与探索如何从 OData 服务与无功扩展 (Rx) 消费"在飞行中的数据"。

其他参考

OData 网站: odata.org

绿洲 OData 标准: bit.ly/163s1gZ

OData 查询语言: ,请参阅在规范说明 bit.ly/15PVBXv 或微软采取包括限制使用 LINQ 来生成查询,在 bit.ly/1zZYMqq

OData、 CKAN 和微软 Azure: 如何 OData 通常提供对大型数据集的访问 (bit.ly/1LrzmYp)。

LINQ 查询映射到 OData 查询: 有扩展 LINQ WCF 数据服务客户端库之外的其他第三方扩展。在详细的基本的 WCF LINQ 查询,和它们如何映射到 OData 查询 bit.ly/1zf8Gp7

动态数据显示: 我发现这真的优秀组控件用于动态数据显示在 bit.ly/1CI73B2。几乎没有为条形图表记录仪使用划痕表面的它的能力。


Louis Ross 是一个多功能的技术建筑师为杰出的施耐德电气,在那里他一直在开发设备集成技术为 17 年。此前,他作为一名设计师,嵌入的控件,专业从事硬件和固件的设计做自由职业者。

衷心感谢以下技术专家对本文的审阅,并提供有价值的反馈:明芳 (施耐德电气) 和 Jason 年轻 (Microsoft)
明芳 (施耐德电气) 是由施耐德电气的杰出发展经理。
Jason 年轻 (微软) 是高级项目经理,微软 DX 和 MS Dev 秀共同主办 (msdevshow.com)