如何:为 PerformancePoint Services 表格数据源创建提供程序

上次修改时间: 2011年8月30日

数据源提供程序连接到数据源,访问其数据,然后返回查询结果。Microsoft SharePoint Server 2010 中的 PerformancePoint Services 使用表格式数据源提供程序访问 Microsoft Excel 和 Excel Services 工作表、SharePoint 列表以及 Microsoft SQL Server 表中的数据。

适用范围: SharePoint Server 2010

您可以创建自定义数据源提供程序来使用 PerformancePoint Services 不支持的表格式数据源中的数据。表格式数据源提供程序的主要功能是创建模拟运算表并使用数据源中的数据填充它。它还创建列映射以定义每列包含的数据类型(事实、维度或时间维度)。这对表格式数据应用基本的多维结构。

本主题中的过程和代码示例基于自定义对象示例中的 WSTabularDataSourceProvider 类。提供程序从外部 Web 服务检索指定股票符号的股市报价。它将历史股市报价数据存储在缓存文件中,这样可以按时间切分数据。本主题的"示例"一节中提供了该类的完整代码。

备注

我们建议您将示例数据源提供程序用作模板。该示例演示如何在 PerformancePoint Services API 中调用对象并演示 PerformancePoint Services 开发的最佳实践。

通过执行两个基本过程来创建数据源提供程序,如下所示:

  • 创建和配置提供程序类

  • 定义提供程序功能

若要创建自定义提供程序,请首先创建提供程序类。

创建和配置提供程序类

  1. 安装 PerformancePoint Services,或者将您的扩展使用的 DLL(步骤 3 中列出)复制到您的计算机上。有关说明,请参阅 开发方案中使用的 PerformancePoint Services DLL

  2. 在 Visual Studio 中,创建一个 C# 类库。如果您已为扩展创建了一个类库,请添加新 C# 类。

  3. 将以下 PerformancePoint Services DLL 作为程序集引用添加到项目中:

    • Microsoft.PerformancePoint.Scorecards.Client.dll

    • Microsoft.PerformancePoint.Scorecards.DataSourceProviders.Standard.dll

    示例数据源提供程序还包含对 System.Core.dll、System.ServiceModel.dll、System.Web.dll、System.Web.Services.dll 和 System.Xml.Linq.dll 的程序集引用。根据您的扩展的功能,可能需要其他项目引用。

  4. 添加一个名为 StockQuotes 的、引用位于地址 http://www.webservicex.net/stockquote.asmx 的 Web 服务的服务引用。此 Web 服务为示例数据源提供股市报价。

  5. 将示例中的 BasicTabularDataSourceProvider 和 SampleDSCacheHandler 类添加到您的项目中。BasicTabularDataSourceProvider 继承自 TabularDataSourceProvider 类,它是表格式数据源提供程序的基类。

    示例数据源还将该类用作 TabularDataSourceProvider 没有实现的替代的抽象方法(GetDatabaseNames()GetCubeNames()GetCubeNameInfos()GetCubeMetaDataValidate())的容器。

  6. 在提供程序类中,为以下 PerformancePoint Services 命名空间添加 using 指令:

    根据您的扩展的功能,可能需要其他 using 指令。

  7. 继承自 BasicTabularDataSourceProvider 类。

创建并配置提供程序类后,必须定义提供程序的功能。

定义提供程序功能

  1. 声明变量并定义用于分析、存储和检索股票符号的属性、缓存文件位置和代理服务器的 URI。

  2. 替代 IsConnectionStringSecure 属性。PerformancePoint Services 不使用此属性,但它旨在供自定义应用程序选择用于识别连接字符串是否公开可能带来安全风险的信息。

    如果您的扩展在数据源的连接字符串中存储敏感信息(例如用户名或密码),则返回 true。如果没有存储敏感信息或数据源没有使用连接字符串,则返回 false。

  3. 替代 GetId() 方法以返回提供程序的唯一标识符。GetId() 必须返回与自定义数据源提供程序的 PerformancePoint Services web.config 文件中注册的 key 属性相同的字符串。

  4. 替代 SetDataSource 方法以定义列映射。SetDataSource 调用 CreateDataColumnMappings 方法将数据源列定义为 FactDimensionTimeDimension 类型。

    SetDataSource 还从自定义数据源对象的 CustomData 属性中检索股票符号、缓存文件位置和代理服务器地址。这些值由仪表板作者在示例数据源编辑器中定义。

  5. 替代 GetDataSet() 方法以创建 DataSet 对象来存储数据源中的数据。示例数据源提供程序使用 FillResultsTable 和 GetLiveQuote 方法来使用 Web 服务中的数据填充模拟运算表。

    **下一步:**在创建数据源提供程序和数据源编辑器(如果需要,包括其用户界面)后,部署扩展,如如何:手动注册 PerformancePoint Services 扩展中所述。有关如何安装示例数据源扩展的说明,请参阅代码示例:自定义报表、筛选器和表格数据源对象中的"安装示例报表、筛选器和数据源对象"一节。

示例

下面的代码示例中的类创建的表格式数据源提供程序从外部 Web 服务检索股市报价,然后将数据转换为表格格式。

备注

在编译此代码示例之前,您必须配置开发环境,如创建和配置提供程序类中所述。

using System;
using System.Data;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.PerformancePoint.Scorecards;
using Microsoft.PerformancePoint.Scorecards.ServerCommon;
using Microsoft.PerformancePoint.SDK.Samples.StockQuotes;
using System.ServiceModel;

namespace Microsoft.PerformancePoint.SDK.Samples.SampleDataSource
{

    // Represents the class that defines the sample data source provider.
    // It inherits from the BasicTabularDataSourceProvider class, which
    // contains overridden abstract methods that are not implemented.
    public class WSTabularDataSourceProvider : BasicTabularDataSourceProvider
    {
        #region Constants
        private const int StockSymbolsIndex = 0;
        private const int CacheFileLocationIndex = 1;
        private const int ProxyAddressIndex = 2;
        #endregion

        #region Properties

        // This property stores the stock symbols that are used
        // to query the Web service.
        // Its value is obtained by parsing the CustomData property
        // of the data source object. 
        private string[] StockSymbols
        {
            get;
            set;
        }

        // The address of the proxy server.
        private Uri ProxyAddress
        {
            get;
            set;
        }

        // This property is not used by PerformancePoint Services.
        // Its intended use is for custom applications to indicate
        // whether a provider stores sensitive information in the
        // connection string, such as user name and password.
        // This sample does not, so it returns false. 
        public override bool IsConnectionStringSecure
        {
            get { return false; }
        }
        #endregion

        #region Overridden methods

        // The source name for your data source. This value must match the key
        // attribute that is registered in the web.config file.
        public override string GetId()
        {
            return "WSTabularDataSource";
        }

        // Add column mappings for the sample columns if they do not exist.
        // Column mappings may be missing if the custom data source has never
        // been edited or if the workspace was not refreshed, which saves
        // changes to the server.
        public override void SetDataSource(DataSource dataSource)
        {

            base.SetDataSource(dataSource);

            // Check for symbols stored in the CustomData
            // property of the data source.
            if (null == dataSource ||
                 string.IsNullOrEmpty(dataSource.CustomData))
            {

                // Create a symbol for testing purposes.
                StockSymbols = new[] { "MSFT" };
            }
            else
            {
                string[] splitCustomData = dataSource.CustomData.Split('&');
                if (splitCustomData.Length > 2)
                {
                    StockSymbols = splitCustomData[StockSymbolsIndex].ToUpper().Split(',');
                    for (int iLoop = 0; iLoop < StockSymbols.Length; iLoop++)
                    {
                        StockSymbols[iLoop] = StockSymbols[iLoop].Trim();
                    }

                    SampleDSCacheHandler.CacheFileLocation = splitCustomData[CacheFileLocationIndex];
                    ProxyAddress = new Uri(splitCustomData[ProxyAddressIndex]);
                }
            }

            // Check whether column mappings exist. Do not overwrite them.
            if (dataSource.DataTableMapping.ColumnMappings.Count == 0)
            {
                dataSource.DataTableMapping = CreateDataColumnMappings();
            }
        }

        // Get the data from the data source.
        // GetDataSet contains the core logic for the provider.
        public override DataSet GetDataSet()
        {

            // Create a dataset and a data table to store the data.
            DataSet resultSet = new DataSet();
            DataTable resultTable = resultSet.Tables.Add();

            // Define column names and the type of data that they contain. 
            resultTable.Columns.Add("Symbol", typeof(string));
            resultTable.Columns.Add("Value", typeof(float));
            resultTable.Columns.Add("P-E Ratio", typeof(float));
            resultTable.Columns.Add("Percentage Change", typeof(float));
            resultTable.Columns.Add("Date", typeof(DateTime));

            FillResultTable(ref resultTable);

            return resultSet;
        }
        #endregion

        #region Internal methods

        // Fill the data table with the stock quote values from
        // the Web service and local cache file.
        protected void FillResultTable(ref DataTable resultsTable)
        {

            // Check the sematic validity of symbols (out of scope for this sample).
            if (null != StockSymbols &&
                StockSymbols.Length > 0 &&
                !string.IsNullOrEmpty(SampleDSCacheHandler.CacheFileLocation))
            {
                try
                {
                    if (!File.Exists(SampleDSCacheHandler.CacheFileLocation))
                    {

                        // Create the cache file.
                        XDocument doc = SampleDSCacheHandler.DefaultCacheFileContent;
                        doc.Save(@SampleDSCacheHandler.CacheFileLocation);
                    }

                    // Get real-time quotes and update cache file.
                    string wsResult = GetLiveQuote();

                    SampleDSCacheHandler.UpdateXMLCacheFile(wsResult);

                    // Check if a valid cache file location exists.
                    if (SampleDSCacheHandler.CacheFileContent != null)
                    {
                        var query = from c in SampleDSCacheHandler.CacheFileContent.Elements("StockQuotes").Elements("StockQuote")
                                    where StockSymbols.Contains(c.Attribute("Symbol").Value)
                                    select c;

                        foreach (var stockQuote in query)
                        {
                            DataRow row = resultsTable.NewRow();
                            row["Symbol"] = stockQuote.Attribute("Symbol").Value;
                            row["Value"] = stockQuote.Element("Value").Value;
                            row["Percentage Change"] = stockQuote.Element("PercentageChange").Value;
                            row["Date"] = stockQuote.Element("Date").Value;

                            decimal peRatio;

                            // Handle symbols that return 'N/A' for this field.
                            if (decimal.TryParse(stockQuote.Element("PERatio").Value, out peRatio))
                            {
                                row["P-E Ratio"] = peRatio;
                            }

                            resultsTable.Rows.Add(row);
                        }
                    }
                }
                catch (Exception ex)
                {
                    ServerUtils.HandleException(ex);
                }
            }
        }

        // Get real-time quotes from the Web service.
        protected string GetLiveQuote()
        {
            EndpointAddress endpoint = new EndpointAddress("http://www.webservicex.net/stockquote.asmx");
            BasicHttpBinding binding = new BasicHttpBinding();
            binding.ReceiveTimeout = new TimeSpan(0, 0, 120);
            binding.ProxyAddress = ProxyAddress;
            binding.UseDefaultWebProxy = false;

            StockQuotes.StockQuoteSoapClient wsStockQuoteService = new StockQuoteSoapClient(binding, endpoint);

            // Check the sematic validity of symbols (out of scope for this sample).
            if (null != StockSymbols &&
                StockSymbols.Length > 0)
            {
                try
                {
                    string quoteRequest = StockSymbols[0];
                    for (int iLoop = 1; iLoop < StockSymbols.Length; iLoop++)
                    {
                        quoteRequest = string.Format("{0}, {1}", quoteRequest, StockSymbols[iLoop]);
                    }

                    string wsResult = wsStockQuoteService.GetQuote(quoteRequest);
                    return wsResult;
                }
                catch (Exception ex)
                {
                    ServerUtils.HandleException(ex);
                }
            }
            return string.Empty;
        }

        // Create the column mappings.
        internal static DataTableMapping CreateDataColumnMappings()
        {
            DataTableMapping dtTableMapping = new DataTableMapping();

            // Define the data in the Symbol column as dimension data.
            dtTableMapping.ColumnMappings.Add(new DataColumnMapping
            {
                SourceColumnName = "Symbol",
                FriendlyColumnName = "Symbol",
                UniqueName = "Symbol",
                ColumnType = MappedColumnTypes.Dimension,
                FactAggregation = FactAggregations.None,
                ColumnDataType = MappedColumnDataTypes.String
            });

            // Define the data in the Value column as fact data.
            dtTableMapping.ColumnMappings.Add(new DataColumnMapping
            {
                SourceColumnName = "Value",
                FriendlyColumnName = "Value",
                UniqueName = "Value",
                ColumnType = MappedColumnTypes.Fact,
                FactAggregation = FactAggregations.Average,
                ColumnDataType = MappedColumnDataTypes.Number
            });

            // Define the data in the P-E Ratio column as fact data.
            dtTableMapping.ColumnMappings.Add(new DataColumnMapping
            {
                SourceColumnName = "P-E Ratio",
                FriendlyColumnName = "P-E Ratio",
                UniqueName = "P-E Ratio",
                ColumnType = MappedColumnTypes.Fact,
                FactAggregation = FactAggregations.Average,
                ColumnDataType = MappedColumnDataTypes.Number
            });

            // Define the data in the Percentage Change column as fact data.
            dtTableMapping.ColumnMappings.Add(new DataColumnMapping
            {
                SourceColumnName = "Percentage Change",
                FriendlyColumnName = "Percentage Change",
                UniqueName = "Percentage Change",
                ColumnType = MappedColumnTypes.Fact,
                FactAggregation = FactAggregations.Average,
                ColumnDataType = MappedColumnDataTypes.Number
            });

            // Define the Date column as a time dimension.
            dtTableMapping.ColumnMappings.Add(new DataColumnMapping
            {
                SourceColumnName = "Date",
                FriendlyColumnName = "Date",
                UniqueName = "Date",
                ColumnType = MappedColumnTypes.TimeDimension,
                FactAggregation = FactAggregations.None,
                ColumnDataType = MappedColumnDataTypes.DateTime
            });

            // Increase the granularity of the time dimension.
            dtTableMapping.DateAggregationType |= DateAggregationTypes.Quarter;
            dtTableMapping.DateAggregationType |= DateAggregationTypes.Month;
            dtTableMapping.DateAggregationType |= DateAggregationTypes.Week;
            dtTableMapping.DateAggregationType |= DateAggregationTypes.Day;

            return dtTableMapping;
        }
        #endregion
    }
}

编译代码

在编译此代码示例之前,您必须配置开发环境,如创建和配置提供程序类中所述。

安全性

必须使用强名称对 DLL 进行签名。另外,确保 DLL 引用的所有程序集都具有强名称。有关如何使用强名称对程序集进行签名以及如何创建公钥/私钥对的信息,请参阅How to: Create a Public/Private Key Pair

请参阅

任务

如何:为 PerformancePoint Services 表格数据源创建编辑器

其他资源

为 PerformancePoint Services 创建自定义对象

SharePoint Server 2010 中的 PerformancePoint Services 的代码示例