使用 ADO MD 和 .NET

OpenSchema 可以帮助解决 COM 互操作性问题

Russ Whitney

既然 Visual Studio .NET 和 Microsoft .NET 语言能够买到,许多开发人员就打算超越试验阶段并使用 Visual Studio .NET 来开发生产应用程序。如果您像我一样,使用 SQL Server 2000 Analysis Services 来开发应用程序,则可能会在尝试将它与 Visual Studio .NET 结合时遇到障碍。.NET 框架本身不支持对多维(即 OLAP)数据源的数据访问。因此,OLAP 开发人员必须使用现有的基于 COM 的 ADO MD 来开发应用程序。乍一看,此问题似乎并不严重,因为 .NET 语言可以与 COM 互操作。但是,您是否尝试过使 .NET 和 ADO MD 一起工作?我已经尝试过,而且我发现它并不简单。下面列出了我在尝试将 .NET 与现有的 COM 应用程序集成时遇到的一些问题,以及可用来避免类似问题的方法。

用 ADO MD 编程

用 ADO MD 编程与用 ADO 编程非常相似。与 ADO 一样,ADO MD 通过两种主要方法来检索信息:您可以从架构行集检索元数据,也可以执行查询。但是,与 ADO 应用程序的不同之处在于,ADO MD 应用程序通常比 ADO 应用程序更依赖架构行集。ADO MD 中的架构行集包含维度、级别和成员,它们不仅描述数据的结构,而且还描述大部分数据。通过使用 ADO MD 的 OpenSchema 方法来检索架构行集(而不是使用 MDX 查询),可以获得某些用户查询的答案。例如,在 FoodMart 2000 Sales 多维数据集中,可以使用 OpenSchema 调用来回答“饮料产品系列中都有哪些产品部门?”这类的问题。OpenSchema 是用来检索架构行集的方法调用。一般来说,使用 ADO(一般和关系数据库一起使用)时,表的结构通常不随表的内容一起更改。使用 ADO 时,类似的一些问题总是需要一个 SQL 查询。

要在 Visual Studio .NET 中开始使用 ADO MD 进行编程,需要将 ADO MD 类型库导入到 .NET 项目中。这可通过在 Visual Basic (VB) 6.0 中添加引用的方法来完成。在 Visual Studio .NET 中,打开“项目”菜单,然后选择“添加引用”。在生成的对话框中,单击 COM 选项卡并向下滚动以选择 Microsoft ActiveX Data Objects (Multi-dimensional) 2.x Library。还需要选择一个名为 Microsoft ActiveX Data Objects 2.x Library 的非多维版本的 ADO。在选择了这两个类型库之后,单击“选择”,然后单击“确定”。您已经将这些库中的类型定义添加到了您的项目中。最后,如果您使用的是 C#,则可以包括 ADO MD 类型库,以便您不必在源代码中的所有 ADO MD 类型引用前面都加上 ADO MD 类型库的名称。通过向源代码中添加相应的 USING 子句(例如,USING ADOMD),可在每个 C# 源文件中包括类型库。

C# 的问题

由于 ADO MD 对架构行集的依赖性很强,您需要能够完全利用 OpenSchema 方法。与 MDX 不同的是,OpenSchema 是 ADO MD 所提供的用来检索架构行集表内容的唯一机制。但是,由于 OpenSchema 参数非常复杂,因此很难从 .NET 语言(如 C#)调用 OpenSchema 方法。产生这种复杂性的因素有二:这些参数是可选的,而条件参数是一个数组。当您尝试将 .NET 和 COM 集成在一起时,这两个因素都会产生问题。

OpenSchema 的设计使您能够用一个、两个或三个参数来调用它,如下面的示例调用所示:

OpenSchema QueryType
OpenSchema QueryType, Criteria
OpenSchema QueryType, Criteria, SchemaID

但是,在 C# 中,不能发出上述调用中的前两个调用,因为您不能忽略参数 ?? 即使它们在 COM 类型库中被声明为可选时也是如此。此限制是 C# 语法的限制。在其他采用可选参数的函数中,这种限制不会产生问题,因为您可以发出带有所有参数的调用,并传递任何参数的默认值,而如果该语言支持可选参数(像 VB 那样)的话,这些参数则已经被忽略了。但是,在使用 OpenSchema 时,传递默认参数非常困难且具有一定的风险,因为 ADO MD 类型库中没有 SchemaID 参数的值。您将必须确定 SchemaID 的默认值并在 C# 中声明等效值。由于未来版本的 ADO MD 可能会更改这些常数的值,而且您的代码会因其声明中包含旧值而不再能够运行,因此使用 SchemaID 的默认值有一定的风险。我在解决这些问题时所使用的方法是,在 C# 中不重新声明任何在将来可能更改的 ADO MD SchemaID 常数。让我们看一下您所面临的两个 OpenSchema 问题。

解决集成问题

首先,让我们看一下如何处理 OpenSchema 的可选参数问题。正如我刚解释的那样,C# 不支持对参数进行忽略,而且 ADO MD 类型库中不包括第三个参数 SchemaID 的值。因此,您必须确定一种调用 OpenSchema 的新方法。要忽略参数,您必须利用晚期绑定。通常,绑定发生在程序编译时,但是晚期绑定意味着方法调用及其参数只有在运行时之后才根据其定义进行验证。清单 1 举例说明了如何使用只有一个参数的晚期绑定来针对 Connection 对象调用 Open 方法。此示例之所以奏效,是因为 .NET 运行时库中的 InvokeMember 方法可以确定在某些参数被忽略的情况下如何发出方法调用,而 C# 编译器却不能这样做。只用一个连接字符串的情况下调用 Open 方法会非常方便,因为您很少将其他参数(例如,UserID、Password、Options)用于分析服务。UserID 和 Password 参数通常是不必要的,因为分析服务使用 Windows 身份验证来确定客户端应用程序的标识。

值得解释一下清单 1 中 InvokeMember() 的用途。InvokeMember() 方法非常灵活且功能强大,使用它,可以执行晚期绑定以访问对象属性(例如,GET 或 SET),或者调用某种方法(其中,该调用的结构是在运行时确定的)。因为 ADO 和 ADO MD 支持在运行时检索有关可用方法、参数和属性的类型信息,所以 InvokeMember() 可以确定如何对您提供的参数进行打包,以便它针对 Connection 对象创建对 Open 方法的正确调用。

对于 .NET 中的每个 Type 对象都可使用 InvokeMember 方法。不要将 Type 对象与特定类型的对象相混淆。.NET 中的 Type 对象包含有关该类型的信息,但是它不是该类型的实例。(在清单 1 中,InvokeMember 的第四个参数 Conn 是可针对其发出方法调用的类型实例。)

在清单 1 中,InvokeMember 的第一个参数 Open 是要访问的方法或属性的名称。第二个参数 BindingFlags.InvokeMethod 告诉 InvokeMember 要执行哪种操作。在清单 1 显示的示例中,您希望 InvokeMember 调用某种方法。第三个参数是绑定对象。如果您将 NULL 作为第三个参数进行传递,则正如清单1 所示,您将获得默认的绑定对象。绑定对象控制 .NET 运行时库访问该对象(第四个参数 Conn) 的方式。最后,第五个参数(即,new Object[])是传递给该方法的参数列表。我本来可以向该数组中添加多个项目,以便向该方法传递更多的参数,但是,我只传递了一个仅包含该连接字符串的数组。

在向 OpenSchema 发出方法调用时遇到的第二个问题是,要找出在 C# 中声明和构造 Criteria 参数的方法。当我尝试将 COM 应用程序与 .NET 集成在一起时,因为没有任何在 C# 中声明条件数组元素的方法的示例,所以遇到了这种问题。通常,您可以使用 Visual Studio .NET 对象浏览器来查看参数的类型。但是,在这种情况下,类型可以随 OpenSchema 调用的不同而发生变化(取决于要使用的限制),而且参数嵌套在一个数组中 ?? 对象浏览器不显示数组参数中的类型。C# 的数据类型与 COM 中的不完全相同,因此,为了与 COM 方法调用中的参数正确匹配,您需要了解 .NET 中的 COM 互操作性层如何将 .NET 数据类型转换为 COM 数据类型。遗憾的是,如果您获得的数据类型有误,则可能会获得一般的错误信息,因此无法给出有关如何修复此问题的提示。在 COM 中,Criteria 参数是 BSTR 变量的安全数组;如果您在 VB 6.0 中使用 ADO MD,则根本无需了解这些信息。您可以将普通的 VB 数组传递到 OpenSchema,而且它完全能工作。解释 BSTR 变量的安全数组已超出本文的范围,但是我将阐释如何在 .NET 中定义等效的条件数组。

清单 2显示了在 C# 中,如何使用条件数组来调用 OpenSchema。可以声明标准的 C# 对象数组,并用 NULL 或字符串填充它。条件是指架构行集中某些列的值;条件定义您希望从行集返回哪些行。例如,如果您希望返回其中多维数据集名称为 Sales 的维度行集行,则条件要在 Sales 多维数据集名称列上指定一个限制。

每个架构行集都有自身可能的条件,这些条件列在 SQL Server 联机丛书 (BOL) 中的“Schema Rowsets, OLAP”主题下。对于每个行集,BOL 的一部分都会列出该行集内可用作条件的列。列的顺序非常重要。例如,维度架构行集有条件列 CATALOG_NAME、SCHEMA_NAME、CUBE_NAME、DIMENSION_NAME 和 DIMENSION_UNIQUE_NAME。如果您希望使用条件来从任何架构行集检索特定信息,则条件数组必须采用 BOL 指定的顺序。在清单2 的代码中,我按多维数据集名称限制了维度架构行集,因此我必须在数组中包括用于前面数组位置 CATALOG_NAME 和 SCHEMA_NAME 的空间。但是,我不希望将生成的行集限制为这些列,因此,我在前两个数组位置中放了一个 NULL。

让我们看一下另一个示例,该示例显示了如何构造 OpenSchema 的条件数组。一个最复杂的架构行集就是成员行集。要筛选在 OpenSchema 调用中返回的成员,可以使用 11 个限制列和一个树运算符。可以按照清单 2 显示的方法使用前 11 个限制列,但是最后一个成员行集限制有所不同。使用树运算符限制,可以通过将相关成员放在 MEMBER_UNIQUE_NAME 限制中来检索维度成员。例如,如 清单 3所示,如果您希望检索所有作为 CA 成员的直接子级的维度成员,则可以将 CA 的唯一名称 ([Customers].[All Customers].[USA].[CA]) 放在 MEMBER_UNIQUE_NAME 条件中,然后将 MDTREEOP_CHILDREN 常数放在树运算符条件中。条件数组非常灵活;条件数组的每个元素都有一个取决于所访问架构行集的含义。这个特定示例不同于我在以前描述的示例,因为条件数组中的元素是运算符,而不是行集列值。

请注意,在清单 3 中,我为不同的树运算符定义了常数,因为在将 ADO MD 类型库导入到 Visual Studio .NET 中之后,ADO MD 类型库中就没有了这些运算符。我不知道为什么没有出现这些常数,但是重新定义常数会有助于针对成员行集发出 OpenSchema 调用。

因为 OpenSchema 运行速度相当快,所以我建议您对于 OpenSchema 调用使用树运算符,而不要通过运行 MDX 查询来查找维度成员的子级。无论您使用哪种技术,总是要注意,维度中可能包含大量成员,而且如果检索不必要的更多维度成员,则成本会很高。例如,应避免检索某个成员的所有子级,甚至要避免检索某个维度级别的所有成员。如果您要针对大型多维数据集使用应用程序,最终可能要检索一百万或更多的成员。

既然您知道了在 C# 中如何向 ADO MD 发出 OpenSchema 调用,则可以使用本文中的代码示例做为指导,来利用 Visual Studio .NET 开发下一代分析应用程序。请注意,从 .NET 发出 COM 方法调用会导致一些开销,这是因为封送处理(进程地址空间之间的数据和代码转换)是必需的。但是,除非您连续数千次调用 ADO MD,否则此开销不会明显影响应用程序的性能。

错误、评价、建议 | 法律 | 法律 隐私 | 广告

版权所有? 2002 Penton Media, Inc. 保留所有权利。

转到原英文页面