在 SharePoint 中创建 PerformancePoint Services 的表格数据源提供程序

了解如何在自定义表格式数据源扩展中为 PerformancePoint Services 创建数据源提供程序组件。

什么是 PerformancePoint Services 的自定义数据源提供程序?

数据源提供程序连接到数据源、访问其数据,然后返回查询结果。 PerformancePoint Services 使用表格式数据源提供程序访问来自 Excel 和 Excel Services 工作表、SharePoint 列表和 Microsoft SQL Server 表的数据。 您可以创建自定义数据源提供程序以使用不受 PerformancePoint Services 支持的表格式数据源中的数据。

表格式数据源提供程序的主要功能是创建模拟运算表,并用来自数据源的数据填充该表。 它还创建列映射以定义每个列所包含的数据的类型(事实、维度或时间维度)。 这会将基本的多维结构应用到表格式数据中。

本主题中的过程和代码示例以自定义对象示例中的 WSTabularDataSourceProvider 类为依据。 此提供程序从外部 Web 服务检索指定股票代号的股票报价。 它将历史股票报价数据存储在缓存文件中,这样就能够按时间进行数据切片。 有关 类的完整代码,请参阅代码示例:为 SharePoint 中的自定义PerformancePoint Services表格数据源创建数据源提供程序

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

为自定义 PerformancePoint Services 表格式数据源创建数据源提供程序

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

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

    您必须用强名称对您的 DLL 进行签名。 此外,请确保您的 DLL 所引用的所有程序集都具有强名称。 有关如何使用强名称对程序集进行签名以及如何创建公钥/私钥对的信息,请参阅 如何:创建公钥/私钥对

  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 的程序集引用。 可能需要其他项目引用,具体取决于您的扩展名功能。

  1. 添加将引用位于地址 的 Web 服务的名为 http://www.webservicex.net/stockquote.asmx 的服务引用。 这是为示例数据源提供股票报价的 Web 服务。

  2. 将示例中的 BasicTabularDataSourceProviderSampleDSCacheHandler 类添加到您的项目中。 BasicTabularDataSourceProviderTabularDataSourceProvider 类(其为表格式数据源提供程序的基类)继承而来。

    示例数据源还将 类用作 TabularDataSourceProvider 未实现 ( GetDatabaseNames () 、GetCubeNames # C3) GetCubeNameInfos () GetCubeMetaDataValidate () ) 的重写抽象方法的容器。

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

  1. BasicTabularDataSourceProvider 类继承而来。

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

  3. 重写 IsConnectionStringSecure 属性。 PerformancePoint Services 不使用此属性,但是旨在自定义应用程序以可选的方式使用,以便确定连接字符串是否会公开可能带来安全性风险的信息。

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

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

  5. 重写 SetDataSource 方法以定义列映射。 SetDataSource 调用 CreateDataColumnMappings 方法以将数据源列定义为 FactDimensionTimeDimension 类型。

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

  6. 重写 GetDataSet () 方法以创建用于存储数据源中的数据的 DataSet 对象。 示例数据源提供程序使用 FillResultsTableGetLiveQuote 方法,从而用 Web 服务中的数据填充模拟运算表。

代码示例:在 SharePoint 中创建自定义 PerformancePoint Services 表格数据源提供程序

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

在编译此代码示例之前,必须按照为自定义PerformancePoint Services表格数据源创建数据源提供程序中所述配置开发环境。


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 &amp;&amp;
                StockSymbols.Length > 0 &amp;&amp;
                !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 &amp;&amp;
                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
    }
}

后续步骤

创建数据源提供程序和数据源编辑器 (包括其用户界面后,如果需要) ,请部署扩展,如如何:手动注册PerformancePoint Services扩展中所述。

另请参阅