Validate M dependencies with data sources, PBIT hardening validation

This commit is contained in:
Christian Wade 2019-12-31 15:54:00 -08:00
parent ba37cf38ec
commit 03a330aee8
7 changed files with 116 additions and 51 deletions

View File

@ -66,7 +66,7 @@ namespace BismNormalizer.TabularCompare
Telemetry.TrackEvent("CreateComparisonInitialized", new Dictionary<string, string> { { "App", comparisonInfo.AppName.Replace(" ", "") } }); Telemetry.TrackEvent("CreateComparisonInitialized", new Dictionary<string, string> { { "App", comparisonInfo.AppName.Replace(" ", "") } });
//If composite models not allowed on AS, check DQ/Import at model level matches: //If composite models not allowed on AS, check DQ/Import at model level matches:
if (comparisonInfo.ConnectionInfoSource.ServerName != null && !comparisonInfo.ConnectionInfoSource.ServerName.StartsWith("powerbi://") && !Settings.Default.OptionCompositeModelsOverride && comparisonInfo.SourceDirectQuery != comparisonInfo.TargetDirectQuery) if (comparisonInfo.AppName == "BISM Normalizer" && comparisonInfo.ConnectionInfoTarget.ServerName != null && !comparisonInfo.ConnectionInfoTarget.ServerName.StartsWith("powerbi://") && !Settings.Default.OptionCompositeModelsOverride && comparisonInfo.SourceDirectQuery != comparisonInfo.TargetDirectQuery)
{ {
throw new ConnectionException($"Mixed DirectQuery settings are not supported for AS skus.\nSource is {(comparisonInfo.SourceDirectQuery ? "On" : "Off")} and target is {(comparisonInfo.TargetDirectQuery ? "On" : "Off")}."); throw new ConnectionException($"Mixed DirectQuery settings are not supported for AS skus.\nSource is {(comparisonInfo.SourceDirectQuery ? "On" : "Off")} and target is {(comparisonInfo.TargetDirectQuery ? "On" : "Off")}.");
} }

View File

@ -234,7 +234,7 @@ namespace BismNormalizer.TabularCompare.MultidimensionalMetadata
while (whatsLeftOfLine.Contains('[') && whatsLeftOfLine.Contains(']')) while (whatsLeftOfLine.Contains('[') && whatsLeftOfLine.Contains(']'))
{ {
int openSquareBracketPosition = whatsLeftOfLine.IndexOf('[', 0); int openSquareBracketPosition = whatsLeftOfLine.IndexOf('[', 0);
//brilliant person at microsoft has ]] instead of ] //someone has ]] instead of ]
int closeSquareBracketPosition = whatsLeftOfLine.Replace("]]", " ").IndexOf(']', openSquareBracketPosition + 1); int closeSquareBracketPosition = whatsLeftOfLine.Replace("]]", " ").IndexOf(']', openSquareBracketPosition + 1);
if (openSquareBracketPosition < closeSquareBracketPosition - 1) if (openSquareBracketPosition < closeSquareBracketPosition - 1)
@ -242,7 +242,7 @@ namespace BismNormalizer.TabularCompare.MultidimensionalMetadata
string potentialDependency = whatsLeftOfLine.Substring(openSquareBracketPosition + 1, closeSquareBracketPosition - openSquareBracketPosition - 1); string potentialDependency = whatsLeftOfLine.Substring(openSquareBracketPosition + 1, closeSquareBracketPosition - openSquareBracketPosition - 1);
if (!potentialDependency.Contains('"') && !dependencies.Contains(potentialDependency)) if (!potentialDependency.Contains('"') && !dependencies.Contains(potentialDependency))
{ {
//unbelievable: some genius at m$ did a replace on ] with ]] //someone did a replace on ] with ]]
dependencies.Add(potentialDependency); dependencies.Add(potentialDependency);
} }
} }

View File

@ -39,20 +39,23 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
/// <param name="objectType">Type of the object to look up dependencies.</param> /// <param name="objectType">Type of the object to look up dependencies.</param>
/// <param name="objectName">Name of the object to look up dependencies.</param> /// <param name="objectName">Name of the object to look up dependencies.</param>
/// <returns></returns> /// <returns></returns>
public CalcDependencyCollection DependenciesReferenceTo(CalcDependencyObjectType referencedObjectType, string referencedObjectName) public CalcDependencyCollection DependenciesReferenceTo(CalcDependencyObjectType referencedObjectType, string referencedObjectName, string referencedTableName)
{ {
CalcDependencyCollection returnVal = new CalcDependencyCollection(); CalcDependencyCollection returnVal = new CalcDependencyCollection();
LookUpDependenciesReferenceTo(referencedObjectType, referencedObjectName, returnVal); LookUpDependenciesReferenceTo(referencedObjectType, referencedObjectName, referencedTableName, returnVal);
return returnVal; return returnVal;
} }
private void LookUpDependenciesReferenceTo(CalcDependencyObjectType referencedObjectType, string referencedObjectName, CalcDependencyCollection returnVal) private void LookUpDependenciesReferenceTo(CalcDependencyObjectType referencedObjectType, string referencedObjectName, string referencedTableName, CalcDependencyCollection returnVal)
{ {
foreach (CalcDependency calcDependency in this) foreach (CalcDependency calcDependency in this)
{ {
if (calcDependency.ReferencedObjectType == referencedObjectType && calcDependency.ReferencedObjectName == referencedObjectName) if (
(calcDependency.ReferencedObjectType == referencedObjectType && referencedObjectType != CalcDependencyObjectType.Partition && calcDependency.ReferencedObjectName == referencedObjectName) ||
(calcDependency.ReferencedObjectType == referencedObjectType && referencedObjectType == CalcDependencyObjectType.Partition && calcDependency.ReferencedTableName == referencedTableName) //References to table-partition expressions are by table name, not partition name
)
{ {
LookUpDependenciesReferenceTo(calcDependency.ObjectType, calcDependency.ObjectName, returnVal); LookUpDependenciesReferenceTo(calcDependency.ObjectType, calcDependency.ObjectName, calcDependency.TableName, returnVal);
returnVal.Add(calcDependency); returnVal.Add(calcDependency);
} }
} }

View File

@ -68,7 +68,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
while (whatsRemainingOfLine.Contains('[') && whatsRemainingOfLine.Contains(']')) while (whatsRemainingOfLine.Contains('[') && whatsRemainingOfLine.Contains(']'))
{ {
int openSquareBracketPosition = whatsRemainingOfLine.IndexOf('[', 0); int openSquareBracketPosition = whatsRemainingOfLine.IndexOf('[', 0);
//brilliant person at microsoft has ]] instead of ] //someone has ]] instead of ]
int closeSquareBracketPosition = whatsRemainingOfLine.Replace("]]", " ").IndexOf(']', openSquareBracketPosition + 1); int closeSquareBracketPosition = whatsRemainingOfLine.Replace("]]", " ").IndexOf(']', openSquareBracketPosition + 1);
if (openSquareBracketPosition < closeSquareBracketPosition - 1) if (openSquareBracketPosition < closeSquareBracketPosition - 1)
@ -78,7 +78,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
!_tomCalculationItem.Expression.Contains($"\"{potentialDependency}\"") && //it's possible the calculationItem itself is deriving the column name from an ADDCOLUMNS for example !_tomCalculationItem.Expression.Contains($"\"{potentialDependency}\"") && //it's possible the calculationItem itself is deriving the column name from an ADDCOLUMNS for example
!dependencies.Contains(potentialDependency)) !dependencies.Contains(potentialDependency))
{ {
//unbelievable: some genius at m$ did a replace on ] with ]] //someone did a replace on ] with ]]
dependencies.Add(potentialDependency); dependencies.Add(potentialDependency);
} }
} }

View File

@ -635,8 +635,8 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
bool reconnect = false; bool reconnect = false;
try try
{ {
_sourceTabularModel.TomDatabase.Refresh(); if (!_sourceTabularModel.ConnectionInfo.UseBimFile) _sourceTabularModel.TomDatabase.Refresh();
_targetTabularModel.TomDatabase.Refresh(); if (!_targetTabularModel.ConnectionInfo.UseBimFile) _targetTabularModel.TomDatabase.Refresh();
} }
catch (Exception) catch (Exception)
{ {
@ -646,17 +646,12 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
if (reconnect || _uncommitedChanges) if (reconnect || _uncommitedChanges)
{ {
// Reconnect to re-initialize // Reconnect to re-initialize
if (!_comparisonInfo.ConnectionInfoSource.UseBimFile)
{
_sourceTabularModel = new TabularModel(this, _comparisonInfo.ConnectionInfoSource, _comparisonInfo); _sourceTabularModel = new TabularModel(this, _comparisonInfo.ConnectionInfoSource, _comparisonInfo);
_sourceTabularModel.Connect(); _sourceTabularModel.Connect();
}
if (!_comparisonInfo.ConnectionInfoTarget.UseBimFile)
{
_targetTabularModel = new TabularModel(this, _comparisonInfo.ConnectionInfoTarget, _comparisonInfo); _targetTabularModel = new TabularModel(this, _comparisonInfo.ConnectionInfoTarget, _comparisonInfo);
_targetTabularModel.Connect(); _targetTabularModel.Connect();
} }
}
if (!_sourceTabularModel.ConnectionInfo.UseProject && _sourceTabularModel.TomDatabase.LastSchemaUpdate > _lastSourceSchemaUpdate) if (!_sourceTabularModel.ConnectionInfo.UseProject && _sourceTabularModel.TomDatabase.LastSchemaUpdate > _lastSourceSchemaUpdate)
{ {
@ -989,13 +984,13 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
#region Calc dependencies validation #region Calc dependencies validation
private bool HasBlockingToDependenciesInTarget(string targetObjectName, CalcDependencyObjectType targetObjectType, ref List<string> warningObjectList) private bool HasBlockingToDependenciesInTarget(string targetObjectName, string referencedTableName, CalcDependencyObjectType targetObjectType, ref List<string> warningObjectList)
{ {
//For deletion. //For deletion.
//Check any objects in target that depend on this object are also going to be deleted or updated. //Check any objects in target that depend on this object are also going to be deleted or updated.
bool returnVal = false; bool returnVal = false;
CalcDependencyCollection targetToDepdendencies = _targetTabularModel.MDependencies.DependenciesReferenceTo(targetObjectType, targetObjectName); CalcDependencyCollection targetToDepdendencies = _targetTabularModel.MDependencies.DependenciesReferenceTo(targetObjectType, targetObjectName, referencedTableName);
foreach (CalcDependency targetToDependency in targetToDepdendencies) foreach (CalcDependency targetToDependency in targetToDepdendencies)
{ {
foreach (ComparisonObject comparisonObjectToCheck in _comparisonObjects) foreach (ComparisonObject comparisonObjectToCheck in _comparisonObjects)
@ -1096,6 +1091,25 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
returnVal = true; returnVal = true;
} }
break;
case CalcDependencyObjectType.Partition:
//Does the object about to be created/updated (sourceObjectName) have a source dependency on this table (comparisonObjectToCheck)?
if (!_targetTabularModel.Tables.ContainsName(sourceFromDependency.ReferencedTableName) &&
comparisonObjectToCheck.ComparisonObjectType == ComparisonObjectType.Table &&
comparisonObjectToCheck.SourceObjectName == sourceFromDependency.ReferencedTableName &&
comparisonObjectToCheck.Status == ComparisonObjectStatus.MissingInTarget && //Creates being skipped (dependency will be missing).
comparisonObjectToCheck.MergeAction == MergeAction.Skip)
//Deletes are impossible for this object to depend on, so don't need to detect. Other Skips can assume are fine, so don't need to detect.
{
string warningObject = $"Table {comparisonObjectToCheck.SourceObjectName}";
if (!warningObjectList.Contains(warningObject))
{
warningObjectList.Add(warningObject);
}
returnVal = true;
}
break; break;
case CalcDependencyObjectType.DataSource: case CalcDependencyObjectType.DataSource:
//Does the object about to be created/updated (sourceObjectName) have a source dependency on this data source (comparisonObjectToCheck)? //Does the object about to be created/updated (sourceObjectName) have a source dependency on this data source (comparisonObjectToCheck)?
@ -1257,7 +1271,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
//Check any objects in target that depend on the DataSource are also going to be deleted //Check any objects in target that depend on the DataSource are also going to be deleted
List<string> warningObjectList = new List<string>(); List<string> warningObjectList = new List<string>();
bool toDependencies = HasBlockingToDependenciesInTarget(comparisonObject.TargetObjectName, CalcDependencyObjectType.DataSource, ref warningObjectList); bool toDependencies = HasBlockingToDependenciesInTarget(comparisonObject.TargetObjectName, "", CalcDependencyObjectType.DataSource, ref warningObjectList);
//For old non-M partitions, check if any such tables have reference to this DataSource, and will not be deleted //For old non-M partitions, check if any such tables have reference to this DataSource, and will not be deleted
foreach (Table table in _targetTabularModel.Tables) foreach (Table table in _targetTabularModel.Tables)
@ -1393,7 +1407,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
//Check any objects in target that depend on the expression are also going to be deleted //Check any objects in target that depend on the expression are also going to be deleted
List<string> warningObjectList = new List<string>(); List<string> warningObjectList = new List<string>();
if (!HasBlockingToDependenciesInTarget(comparisonObject.TargetObjectName, CalcDependencyObjectType.Expression, ref warningObjectList)) if (!HasBlockingToDependenciesInTarget(comparisonObject.TargetObjectName, "", CalcDependencyObjectType.Expression, ref warningObjectList))
{ {
_targetTabularModel.DeleteExpression(comparisonObject.TargetObjectName); _targetTabularModel.DeleteExpression(comparisonObject.TargetObjectName);
OnValidationMessage(new ValidationMessageEventArgs($"Delete expression [{comparisonObject.TargetObjectName}].", ValidationMessageType.Expression, ValidationMessageStatus.Informational)); OnValidationMessage(new ValidationMessageEventArgs($"Delete expression [{comparisonObject.TargetObjectName}].", ValidationMessageType.Expression, ValidationMessageStatus.Informational));
@ -1495,9 +1509,24 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
{ {
return; return;
}; };
//Check any objects in target that depend on the table expression are also going to be deleted
List<string> warningObjectList = new List<string>();
if (!HasBlockingToDependenciesInTarget("", comparisonObject.TargetObjectName, CalcDependencyObjectType.Partition, ref warningObjectList))
{
_targetTabularModel.DeleteTable(comparisonObject.TargetObjectName); _targetTabularModel.DeleteTable(comparisonObject.TargetObjectName);
OnValidationMessage(new ValidationMessageEventArgs($"Delete {(isCalculationGroup ? "calculation group" : "table")} '{comparisonObject.TargetObjectName}'.", ValidationMessageType.Table, ValidationMessageStatus.Informational)); OnValidationMessage(new ValidationMessageEventArgs($"Delete {(isCalculationGroup ? "calculation group" : "table")} '{comparisonObject.TargetObjectName}'.", ValidationMessageType.Table, ValidationMessageStatus.Informational));
} }
else
{
string message = $"Unable to delete table {comparisonObject.TargetObjectName} because the following objects depend on it: {String.Join(", ", warningObjectList)}.";
if (_comparisonInfo.OptionsInfo.OptionRetainPartitions && !_comparisonInfo.OptionsInfo.OptionRetainPolicyPartitions)
{
message += " Note: the option to retain partitions is on, which may be affecting this.";
}
OnValidationMessage(new ValidationMessageEventArgs(message, ValidationMessageType.Table, ValidationMessageStatus.Warning));
}
}
} }
private void CreateTable(ComparisonObject comparisonObject) private void CreateTable(ComparisonObject comparisonObject)
@ -1855,10 +1884,13 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
private bool DesktopHardened(ComparisonObject comparisonObject, ValidationMessageType validationMessageType) private bool DesktopHardened(ComparisonObject comparisonObject, ValidationMessageType validationMessageType)
{ {
if (_targetTabularModel.ConnectionInfo.UseDesktop && _targetTabularModel.ConnectionInfo.ServerMode == Microsoft.AnalysisServices.ServerMode.SharePoint) if (
(_targetTabularModel.ConnectionInfo.UseDesktop && _targetTabularModel.ConnectionInfo.ServerMode == Microsoft.AnalysisServices.ServerMode.SharePoint) ||
(_targetTabularModel.ConnectionInfo.UseBimFile && _targetTabularModel.ConnectionInfo.BimFile != null && _targetTabularModel.ConnectionInfo.BimFile.ToUpper().EndsWith(".PBIT"))
)
{ {
//V3 hardening //V3 hardening
OnValidationMessage(new ValidationMessageEventArgs($"Unable to {comparisonObject.MergeAction.ToString().ToLower()} {comparisonObject.ComparisonObjectType.ToString()} {comparisonObject.TargetObjectName} because target is Power BI Desktop, which does not yet support modifications for this object type.", validationMessageType, ValidationMessageStatus.Warning)); OnValidationMessage(new ValidationMessageEventArgs($"Unable to {comparisonObject.MergeAction.ToString().ToLower()} {comparisonObject.ComparisonObjectType.ToString()} {comparisonObject.TargetObjectName} because target is Power BI Desktop or .PBIT, which does not yet support modifications for this object type.", validationMessageType, ValidationMessageStatus.Warning));
return false; return false;
} }
else else

View File

@ -79,7 +79,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
while (whatsRemainingOfLine.Contains('[') && whatsRemainingOfLine.Contains(']')) while (whatsRemainingOfLine.Contains('[') && whatsRemainingOfLine.Contains(']'))
{ {
int openSquareBracketPosition = whatsRemainingOfLine.IndexOf('[', 0); int openSquareBracketPosition = whatsRemainingOfLine.IndexOf('[', 0);
//brilliant person at microsoft has ]] instead of ] //someone has ]] instead of ]
int closeSquareBracketPosition = whatsRemainingOfLine.Replace("]]", " ").IndexOf(']', openSquareBracketPosition + 1); int closeSquareBracketPosition = whatsRemainingOfLine.Replace("]]", " ").IndexOf(']', openSquareBracketPosition + 1);
if (openSquareBracketPosition < closeSquareBracketPosition - 1) if (openSquareBracketPosition < closeSquareBracketPosition - 1)
@ -89,7 +89,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
!_tomMeasure.Expression.Contains($"\"{potentialDependency}\"") && //it's possible the measure itself is deriving the column name from an ADDCOLUMNS for example !_tomMeasure.Expression.Contains($"\"{potentialDependency}\"") && //it's possible the measure itself is deriving the column name from an ADDCOLUMNS for example
!dependencies.Contains(potentialDependency)) !dependencies.Contains(potentialDependency))
{ {
//unbelievable: some genius at m$ did a replace on ] with ]] //someone did a replace on ] with ]]
dependencies.Add(potentialDependency); dependencies.Add(potentialDependency);
} }
} }

View File

@ -239,7 +239,8 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
_calcDependencies.Clear(); _calcDependencies.Clear();
List<MObject> mObjects = new List<MObject>(); List<MObject> mObjects = new List<MObject>();
//Add table partitions to mObjects collection #region Add M-dependent objects to collection
foreach (Table table in _tables) foreach (Table table in _tables)
{ {
foreach (Partition partition in table.TomTable.Partitions) foreach (Partition partition in table.TomTable.Partitions)
@ -258,7 +259,6 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
} }
} }
//Add other M expressions to mObjects collection
foreach (Expression expression in _expressions) foreach (Expression expression in _expressions)
{ {
mObjects.Add( mObjects.Add(
@ -271,7 +271,24 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
); );
} }
char[] delimiterChars = { ' ', ',', ':', '\t', '\n', '[', ']', '(', ')', '{', '}' }; foreach (DataSource dataSource in _dataSources)
{
if (dataSource.TomDataSource.Type == DataSourceType.Structured)
{
mObjects.Add(
new MObject(
objectType: "DATA_SOURCE",
tableName: "",
objectName: dataSource.Name,
expression: ""
)
);
}
}
#endregion
char[] delimiterChars = { ' ', ',', ':', '=', '\t', '\n', '[', ']', '(', ')', '{', '}' };
List<string> keywords = new List<string>() { "and", "as", "each", "else", "error", "false", "if", "in", "is", "let", "meta", "not", "otherwise", "or", "section", "shared", "then", "true", "try", "type", "#binary", "#date", "#datetime", "#datetimezone", "#duration", "#infinity", "#nan", "#sections", "#shared", "#table", "#time" }; List<string> keywords = new List<string>() { "and", "as", "each", "else", "error", "false", "if", "in", "is", "let", "meta", "not", "otherwise", "or", "section", "shared", "then", "true", "try", "type", "#binary", "#date", "#datetime", "#datetimezone", "#duration", "#infinity", "#nan", "#sections", "#shared", "#table", "#time" };
foreach (MObject mObject in mObjects) foreach (MObject mObject in mObjects)
@ -291,16 +308,16 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
mObject.TableName == referencedMObject.TableName mObject.TableName == referencedMObject.TableName
)) ))
{ {
if ( //if M_EXPRESSION name contains spaces or is a keyword, only need to check for occurrence like #"My Query" or #"let" if ( //if M_EXPRESSION or DATA_SOURCE, check for occurrence like #"My Query" or #"let"
(referencedMObject.ObjectType == "M_EXPRESSION" && (referencedMObject.ObjectName.Contains(" ") || keywords.Contains(referencedMObject.ObjectName))) && (referencedMObject.ObjectType == "M_EXPRESSION" || referencedMObject.ObjectType == "DATA_SOURCE") &&
(mObject.Expression.Contains("\"" + referencedMObject.ObjectName + "\"")) (mObject.Expression.Contains("#\"" + referencedMObject.ObjectName + "\""))
) )
{ {
foundDependency = true; foundDependency = true;
} }
else if ( //if table name contains spaces or is a keyword, only need to check for occurrence like #"My Query" or #"let" else if ( //if table name, check for occurrence like #"My Query" or #"let"
(referencedMObject.ObjectType == "PARTITION" && (referencedMObject.TableName.Contains(" ") || keywords.Contains(referencedMObject.TableName))) && referencedMObject.ObjectType == "PARTITION" &&
(mObject.Expression.Contains("\"" + referencedMObject.TableName + "\"")) (mObject.Expression.Contains("#\"" + referencedMObject.TableName + "\""))
) )
{ {
foundDependency = true; foundDependency = true;
@ -310,8 +327,14 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
foreach (string word in words) foreach (string word in words)
{ {
if ( if (
(referencedMObject.ObjectType == "M_EXPRESSION" && word == referencedMObject.ObjectName && !keywords.Contains(referencedMObject.ObjectName)) || (
(referencedMObject.ObjectType == "PARTITION" && word == referencedMObject.TableName && !keywords.Contains(referencedMObject.TableName)) (referencedMObject.ObjectType == "M_EXPRESSION" || referencedMObject.ObjectType == "DATA_SOURCE") &&
word == referencedMObject.ObjectName && !keywords.Contains(referencedMObject.ObjectName)
) ||
(
referencedMObject.ObjectType == "PARTITION" &&
word == referencedMObject.TableName && !keywords.Contains(referencedMObject.TableName)
)
) )
{ {
foundDependency = true; foundDependency = true;
@ -1846,13 +1869,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
/// <returns>Boolean indicating whether update was successful.</returns> /// <returns>Boolean indicating whether update was successful.</returns>
public bool Update() public bool Update()
{ {
//Set model annotation for telemetry tagging later SetBNormAnnotation();
const string AnnotationName = "__BNorm";
Tom.Annotation annotationBNorm = new Tom.Annotation();
annotationBNorm.Name = AnnotationName;
annotationBNorm.Value = "1";
if (!_model.TomModel.Annotations.Contains(AnnotationName))
_model.TomModel.Annotations.Add(annotationBNorm);
if (_connectionInfo.UseBimFile) if (_connectionInfo.UseBimFile)
{ {
@ -1884,6 +1901,17 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
return true; return true;
} }
private void SetBNormAnnotation()
{
//Set model annotation for telemetry tagging later
const string AnnotationName = "__BNorm";
Tom.Annotation annotationBNorm = new Tom.Annotation();
annotationBNorm.Name = AnnotationName;
annotationBNorm.Value = "1";
if (!_model.TomModel.Annotations.Contains(AnnotationName))
_model.TomModel.Annotations.Add(annotationBNorm);
}
private void UpdateProject() private void UpdateProject()
{ {
UpdateWithScript(); UpdateWithScript();
@ -2315,6 +2343,8 @@ namespace BismNormalizer.TabularCompare.TabularMetadata
/// <returns>JSON script of tabular model defintion.</returns> /// <returns>JSON script of tabular model defintion.</returns>
public string ScriptDatabase() public string ScriptDatabase()
{ {
SetBNormAnnotation();
//script db to json //script db to json
string json = JsonScripter.ScriptCreateOrReplace(_database); string json = JsonScripter.ScriptCreateOrReplace(_database);