PageRenderTime 1143ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/AMO2TabularV2/AMO2Tabular.CalculatedColumnFunctions.cs

http://tabularamo2012.codeplex.com
C# | 237 lines | 101 code | 31 blank | 105 comment | 24 complexity | 13039bae0c39c1ce37d0c98490782ba4 MD5 | raw file
  1. /*=====================================================================
  2. File: AMO2Tabular.CalculatedColumnFunctions.cs
  3. Summary: This part of the AMO2Tabular class contains all
  4. functions related to manage and manipulate Calculated
  5. Columns in a tabular model
  6. AMO to Tabular (AMO2Tabular) is sample code to show and
  7. explain how to use AMO to handle Tabular model objects.
  8. The sample can be seen as a sample library of functions
  9. with the necessary code to execute each particular
  10. action or operation over a logical tabular object.
  11. Authors: JuanPablo Jofre (jpjofre@microsoft.com)
  12. Date: 04-Apr-2012
  13. Change history:
  14. @TODO:
  15. -----------------------------------------------------------------------
  16. This file is part of the Microsoft SQL Server Code Samples.
  17. Copyright (C) Microsoft Corporation. All rights reserved.
  18. This source code is intended only as a supplement to Microsoft
  19. Development Tools and/or on-line documentation. See these other
  20. materials for detailed information regarding Microsoft code samples.
  21. THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF
  22. ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  23. THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  24. PARTICULAR PURPOSE.
  25. ======================================================================*/
  26. using System;
  27. using System.Linq;
  28. using MicrosoftSql2012Samples.Amo2Tabular.Properties;
  29. using AMO = Microsoft.AnalysisServices;
  30. namespace MicrosoftSql2012Samples.Amo2Tabular
  31. {
  32. // Note:
  33. // CalculatedColumn functionality like Drop and Alter
  34. // is done through regular ColumnDrop and ColumnAlter functions
  35. // Hence: no need to implement that functionality here
  36. public static partial class AMO2Tabular
  37. {
  38. public static void CalculatedColumnAdd(AMO.Database tabularDatabase,
  39. string tableName,
  40. string columnName,
  41. string daxExpression,
  42. bool updateInstance = true,
  43. ColumnInfo? columnProperties = null,
  44. ReportingInfo? reportingProperties = null,
  45. AMO.ProcessType? processType = null)
  46. {
  47. // Major steps in adding a calculated calculatedColumn to a table in the database
  48. //
  49. // - Validate required input arguments
  50. // - Other Initial preparations
  51. // - Adding calculatedColumn as attribute to dimension
  52. // - Adding calculatedColumn as attribute to degenerated dimension in Measure Group
  53. // - Set calculatedColumn properties according to optional parameters
  54. // - Set reporting properties according to optional parameters
  55. // - Process table/database according to optional parameters
  56. //
  57. // Note: There are no validations for duplicated names, invalid names or
  58. // similar scenarios. It is expected the server will take care of them and
  59. // throw exceptions on any invalid situation.
  60. //
  61. // Note: In AMO, strings as indexers refer to the ID of the object, not the Name of the object
  62. //
  63. // Note: Only one DataSourceView is used in Tabular Models
  64. // ==> tabularDatabase.DataSourceViews[0] represents the DSV of the model
  65. //
  66. // Note: Only one Cube is used in Tabular Models
  67. // ==> tabularDatabase.Cubes[0] represents the cube in the model
  68. //
  69. // Note: Microsoft design tools use the following pattern to keep track of the
  70. // datasource matching elements:
  71. // DataSourceView->TableName <---> Dimension.ID, MeasureGroup.ID
  72. // DataSourceView->ColumnName <---> Dimension->ColumnID, MeasureGroup.DegeneratedDimension->CoumnID
  73. // So far, this sample follows the same pattern.
  74. //
  75. // WARNING: Breaking the above pattern when creating your
  76. // own AMO to Tabular functions might lead to
  77. // unpredictable behavior when using Microsoft
  78. // Design tools in your models.
  79. //
  80. // Note: There are no validations on the ProcessType requested and whatever value is passed it's used
  81. //
  82. // Note: For Calculated Columns, in tabular models, the following ProcessType values are 'valid' or have sense:
  83. // - ProcessDefault ==> (issued at table level) verifies if a data (at partition level) or recalc is
  84. // required and issues coresponding internal process tasks
  85. // - ProcessFull ==> (issued at table level) forces data upload (on all partitions) and recalc,
  86. // regardless of table status
  87. // - ProcessRecalc ==> (issued at Database level) forces a recalc of internal structures (measures,
  88. // calculated columns, hierarchies, etc.) at database level; doesn't load
  89. // new data.
  90. //
  91. // Note: Issuing a process request (setting parameter processType != null) forces a database update
  92. #region Validate input arguments and other initial preparations
  93. // Validate required input arguments
  94. if (tabularDatabase == null) throw new ArgumentNullException("tabularDatabase");
  95. if (tableName.IsNullOrEmptyOrWhitespace()) throw new ArgumentNullException("tableName");
  96. if (columnName.IsNullOrEmptyOrWhitespace()) throw new ArgumentNullException("columnName");
  97. if (daxExpression.IsNullOrEmptyOrWhitespace()) throw new ArgumentNullException("daxExpression");
  98. // Other initial preparations
  99. // - Cleaning and preparing name variables
  100. tableName = tableName.Trim();
  101. columnName = columnName.Trim();
  102. daxExpression = daxExpression.Trim();
  103. // - Obtain table name in DSV
  104. string datasourceTableName = tabularDatabase.Dimensions.GetByName(tableName).ID;
  105. // - Obtain "RowNumber" column id
  106. string rowNumberColumnId = string.Empty;
  107. foreach (AMO.DimensionAttribute da in tabularDatabase.Dimensions[datasourceTableName].Attributes)
  108. {
  109. if (da.Type == AMO.AttributeType.RowNumber)
  110. {
  111. rowNumberColumnId = da.ID;
  112. break;
  113. }
  114. }
  115. #endregion
  116. // Add calculated calculatedColumn as attribute to the Dimension
  117. // Note: datasourceTableName == tableId; because parity with DS object names needs to be kept
  118. using (AMO.Dimension tableDimension = tabularDatabase.Dimensions[datasourceTableName])
  119. using (AMO.DimensionAttribute calculatedColumnDimensionAttribute = tableDimension.Attributes.Add(columnName, columnName))
  120. using (AMO.DataItem dataItemEmptyType = new AMO.DataItem(datasourceTableName, columnName, System.Data.OleDb.OleDbType.Empty))
  121. using (AMO.ExpressionBinding expressionBinding = new AMO.ExpressionBinding(daxExpression))
  122. using (AMO.DataItem dataItemWCharType = new AMO.DataItem(datasourceTableName, columnName, System.Data.OleDb.OleDbType.WChar))
  123. {
  124. calculatedColumnDimensionAttribute.Usage = AMO.AttributeUsage.Regular;
  125. calculatedColumnDimensionAttribute.KeyUniquenessGuarantee = false;
  126. calculatedColumnDimensionAttribute.KeyColumns.Add(dataItemEmptyType);
  127. calculatedColumnDimensionAttribute.KeyColumns[0].Source = expressionBinding.Clone();
  128. calculatedColumnDimensionAttribute.KeyColumns[0].NullProcessing = AMO.NullProcessing.Preserve;
  129. calculatedColumnDimensionAttribute.NameColumn = dataItemWCharType;
  130. calculatedColumnDimensionAttribute.NameColumn.Source = expressionBinding;
  131. calculatedColumnDimensionAttribute.NameColumn.NullProcessing = AMO.NullProcessing.ZeroOrBlank;
  132. calculatedColumnDimensionAttribute.OrderBy = AMO.OrderBy.Key;
  133. using (AMO.AttributeRelationship calculatedColumnDimensionAttributeRelationship = tableDimension.Attributes[rowNumberColumnId].AttributeRelationships.Add(calculatedColumnDimensionAttribute.ID))
  134. {
  135. calculatedColumnDimensionAttributeRelationship.Cardinality = AMO.Cardinality.Many;
  136. calculatedColumnDimensionAttributeRelationship.OverrideBehavior = AMO.OverrideBehavior.None;
  137. }
  138. }
  139. // Add calculatedColumn as attribute to the MG, in the DegeneratedMeasureGroupDimension
  140. using (AMO.MeasureGroup tableMeasureGroup = tabularDatabase.Cubes[0].MeasureGroups[datasourceTableName])
  141. using (AMO.DegenerateMeasureGroupDimension tableMGDimension = (AMO.DegenerateMeasureGroupDimension)tableMeasureGroup.Dimensions[datasourceTableName])
  142. using (AMO.MeasureGroupAttribute calculatedColumnMGAttribute = new AMO.MeasureGroupAttribute(columnName))
  143. using (AMO.DataItem dataItemEmptyType = new AMO.DataItem(datasourceTableName, columnName, System.Data.OleDb.OleDbType.Empty))
  144. using (AMO.ExpressionBinding expressionBinding = new AMO.ExpressionBinding(daxExpression))
  145. {
  146. calculatedColumnMGAttribute.KeyColumns.Add(dataItemEmptyType);
  147. calculatedColumnMGAttribute.KeyColumns[0].Source = expressionBinding;
  148. tableMGDimension.Attributes.Add(calculatedColumnMGAttribute);
  149. }
  150. // Set/update optional calculatedColumn properties
  151. if (columnProperties != null)
  152. {
  153. if (!columnProperties.Value.DataFormat.IsNullOrEmptyOrWhitespace())
  154. ColumnAlterFormat(tabularDatabase, tableName, columnName, columnProperties.Value.DataFormat);
  155. if (columnProperties.Value.DataType != DataType.Default && columnProperties.Value.DataType != DataType.Unsupported)
  156. ColumnAlterDataType(tabularDatabase, tableName, columnName, columnProperties.Value.DataType);
  157. if (columnProperties.Value.Visible != null)
  158. ColumnAlterVisibility(tabularDatabase, tableName, columnName, columnProperties.Value.Visible.Value);
  159. if (!columnProperties.Value.SortByColumn.IsNullOrEmptyOrWhitespace())
  160. ColumnAlterSortByColumnName(tabularDatabase, tableName, columnName, columnProperties.Value.SortByColumn);
  161. }
  162. // ToDo: Set/update optional reporting properties
  163. //if (reportingProperties != null)
  164. //{
  165. //}
  166. // Update server instance
  167. if(updateInstance)
  168. tabularDatabase.Update(AMO.UpdateOptions.ExpandFull, AMO.UpdateMode.UpdateOrCreate);
  169. if (processType != null)
  170. {
  171. // Throw exception if server instance is outdated and user requests process
  172. if (!updateInstance)
  173. throw new InvalidOperationException(Resources.ProcessRequestedForOutdatedModelInvalidOperationException);
  174. // Now the table, that contains the Calculated Column, can be processed according to the user request
  175. TableProcess(tabularDatabase, tableName, (AMO.ProcessType)processType);
  176. // Calculated columns require a database level process recalc
  177. tabularDatabase.Process(AMO.ProcessType.ProcessRecalc);
  178. }
  179. }
  180. public static string[] CalculatedColumnsEnumerate(AMO.Database tabularDatabase, string tableName)
  181. {
  182. #region Validate input arguments and other initial preparations
  183. // Validate required input arguments
  184. if (tabularDatabase == null) throw new ArgumentNullException("tabularDatabase");
  185. if (tableName.IsNullOrEmptyOrWhitespace()) throw new ArgumentNullException("tableName");
  186. // Other initial preparations
  187. // - Cleaning and preparing name variables
  188. tableName = tableName.Trim();
  189. #endregion
  190. return (from calculatedColumn in tabularDatabase.Dimensions.GetByName(tableName).Attributes.Cast<AMO.DimensionAttribute>()
  191. where (calculatedColumn.NameColumn.Source is AMO.ExpressionBinding)
  192. select calculatedColumn.Name).ToArray();
  193. }
  194. }
  195. }