diff --git a/BismNormalizer/BismNormalizer.CommandLine/Properties/AssemblyInfo.cs b/BismNormalizer/BismNormalizer.CommandLine/Properties/AssemblyInfo.cs index 8efd75d..f2d083b 100644 --- a/BismNormalizer/BismNormalizer.CommandLine/Properties/AssemblyInfo.cs +++ b/BismNormalizer/BismNormalizer.CommandLine/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("4.0.0.16")] -[assembly: AssemblyFileVersion("4.0.0.16")] +[assembly: AssemblyVersion("4.0.0.18")] +[assembly: AssemblyFileVersion("4.0.0.18")] diff --git a/BismNormalizer/BismNormalizer.IconSetup/Properties/AssemblyInfo.cs b/BismNormalizer/BismNormalizer.IconSetup/Properties/AssemblyInfo.cs index fc46226..86f1e06 100644 --- a/BismNormalizer/BismNormalizer.IconSetup/Properties/AssemblyInfo.cs +++ b/BismNormalizer/BismNormalizer.IconSetup/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("4.0.0.16")] -[assembly: AssemblyFileVersion("4.0.0.16")] +[assembly: AssemblyVersion("4.0.0.18")] +[assembly: AssemblyFileVersion("4.0.0.18")] diff --git a/BismNormalizer/BismNormalizer/BismNormalizer.csproj b/BismNormalizer/BismNormalizer/BismNormalizer.csproj index 8c0a239..9291238 100644 --- a/BismNormalizer/BismNormalizer/BismNormalizer.csproj +++ b/BismNormalizer/BismNormalizer/BismNormalizer.csproj @@ -287,7 +287,7 @@ - + diff --git a/BismNormalizer/BismNormalizer/Properties/AssemblyInfo.cs b/BismNormalizer/BismNormalizer/Properties/AssemblyInfo.cs index 5933a0b..2681011 100644 --- a/BismNormalizer/BismNormalizer/Properties/AssemblyInfo.cs +++ b/BismNormalizer/BismNormalizer/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("4.0.0.16")] -[assembly: AssemblyFileVersion("4.0.0.16")] +[assembly: AssemblyVersion("4.0.0.18")] +[assembly: AssemblyFileVersion("4.0.0.18")] diff --git a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RelationshipChain.cs b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RelationshipChainsFromRoot.cs similarity index 74% rename from BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RelationshipChain.cs rename to BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RelationshipChainsFromRoot.cs index 90a23be..439364c 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RelationshipChain.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RelationshipChainsFromRoot.cs @@ -7,7 +7,7 @@ namespace BismNormalizer.TabularCompare.TabularMetadata /// /// Represents a chain of RelationshipLink objects, used to detect ambiguity in relationship paths. /// - public class RelationshipChain : List + public class RelationshipChainsFromRoot : List { /// /// Find end table by name. @@ -49,9 +49,26 @@ namespace BismNormalizer.TabularCompare.TabularMetadata /// Boolean indicating if the end table was found. public bool ContainsEndTableName(string endTableName) { - foreach (RelationshipLink ReferencedTable in this) + foreach (RelationshipLink link in this) { - if (ReferencedTable.EndTable.Name == endTableName) + if (link.EndTable.Name == endTableName) + { + return true; + } + } + return false; + } + + /// + /// Check if chain of RelationshipLink objects contains an end table with specified name that is BiDi invoked. + /// + /// Name of the end table. + /// Boolean indicating if the BiDi invoked end table was found. + public bool ContainsBidiToEndTable(string endTableName) + { + foreach (RelationshipLink link in this) + { + if (link.EndTable.Name == endTableName && link.PrecedingPathBiDiInvoked) { return true; } diff --git a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RelationshipLink.cs b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RelationshipLink.cs index 8f8c21e..43db588 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RelationshipLink.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/RelationshipLink.cs @@ -14,7 +14,9 @@ namespace BismNormalizer.TabularCompare.TabularMetadata private Table _beginTable; private Table _endTable; private bool _root; + private bool _biDiInvoked; private string _tablePath; + private bool _precedingPathBiDiInvoked; private Relationship _filteringRelationship; /// @@ -25,19 +27,23 @@ namespace BismNormalizer.TabularCompare.TabularMetadata /// Boolean indicating if the root link in a the relationship chain. /// Recursive path to the preceding table in the relationship chain. /// Relationship object for the relationship link. - public RelationshipLink(Table beginTable, Table endTable, bool root, string precedingTablePath, Relationship filteringRelationship) + public RelationshipLink(Table beginTable, Table endTable, bool root, string precedingTablePath, bool precedingPathBiDiInvoked, Relationship filteringRelationship, bool biDiInvoked) { _beginTable = beginTable; _endTable = endTable; _root = root; + _biDiInvoked = biDiInvoked; if (root) { + //_tablePath = $"'{beginTable.Name}'->{(biDi ? "(BiDi)" : "")}'{endTable.Name}'"; _tablePath = $"'{beginTable.Name}'->'{endTable.Name}'"; } else { + //_tablePath = $"{precedingTablePath}->{(biDi ? "(BiDi)" : "")}'{endTable.Name}'"; _tablePath = $"{precedingTablePath}->'{endTable.Name}'"; } + _precedingPathBiDiInvoked = (precedingPathBiDiInvoked || biDiInvoked); _filteringRelationship = filteringRelationship; } @@ -56,11 +62,21 @@ namespace BismNormalizer.TabularCompare.TabularMetadata /// public bool Root => _root; + /// + /// Boolean indicating if the relationship is BiDi and traversing from the many to the one side. + /// + public bool BiDiInvoked => _biDiInvoked; + /// /// Recursive path to the preceding table in the relationship chain. /// public string TablePath => _tablePath; + /// + /// Boolean indicating if the relationship is BiDi and traversing from the many to the one side. + /// + public bool PrecedingPathBiDiInvoked => _precedingPathBiDiInvoked; + /// /// Relationship object for the relationship link. /// diff --git a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TabularModel.cs b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TabularModel.cs index 762208e..be6cf41 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TabularModel.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/TabularMetadata/TabularModel.cs @@ -586,13 +586,12 @@ namespace BismNormalizer.TabularCompare.TabularMetadata { //beginTable might be the From or the To table in TOM Relationship object; it depends on CrossFilterDirection. - RelationshipChain referencedTableCollection = new RelationshipChain(); + RelationshipChainsFromRoot referencedTableCollection = new RelationshipChainsFromRoot(); foreach (Relationship filteringRelationship in beginTable.FindFilteringRelationships()) { // EndTable can be either the From or the To table of a Relationship object depending on CrossFilteringBehavior - string endTableName = filteringRelationship.TomRelationship.FromTable.Name == beginTable.Name ? filteringRelationship.TomRelationship.ToTable.Name : filteringRelationship.TomRelationship.FromTable.Name; - - RelationshipLink rootLink = new RelationshipLink(beginTable, _tables.FindByName(endTableName), true, "", filteringRelationship); + string endTableName = GetEndTableName(beginTable, filteringRelationship, out bool biDi); + RelationshipLink rootLink = new RelationshipLink(beginTable, _tables.FindByName(endTableName), true, "", false, filteringRelationship, biDi); ValidateLink(rootLink, referencedTableCollection); } } @@ -613,15 +612,19 @@ namespace BismNormalizer.TabularCompare.TabularMetadata } } - private void ValidateLink(RelationshipLink link, RelationshipChain chain) + private void ValidateLink(RelationshipLink link, RelationshipChainsFromRoot chainsFromRoot) { - if (chain.ContainsEndTableName(link.EndTable.Name)) + if ( + chainsFromRoot.ContainsEndTableName(link.EndTable.Name) + //&& !(link.PrecedingPathBiDiInvoked && !chainsFromRoot.ContainsBidiToEndTable(link.EndTable.Name)) + //Fix 12/1/2017: we allow 1 ambiguous relationship path as long as only one of the paths is bidi invoked (2 bidis to the same table counts as ambiguous) + ) { // If we are here, we have identified 2 active paths to get to the same table. // So, the one that was already there in the target should win. - RelationshipLink otherLink = chain.FindByEndTableName(link.EndTable.Name); - string rootTableName = chain.FindRoot().BeginTable.Name; + RelationshipLink otherLink = chainsFromRoot.FindByEndTableName(link.EndTable.Name); + string rootTableName = chainsFromRoot.FindRoot().BeginTable.Name; if (link.FilteringRelationship.CopiedFromSource) { @@ -643,31 +646,47 @@ namespace BismNormalizer.TabularCompare.TabularMetadata ValidationMessageStatus.Warning)); //remove OTHER ONE from collection as no longer in filtering chain - chain.RemoveByEndTableName(otherLink.EndTable.Name); + chainsFromRoot.RemoveByEndTableName(otherLink.EndTable.Name); } } if (link.FilteringRelationship.TomRelationship.IsActive) //If not, we must have just set it to false above { //Add the link to the chain and re-iterate ... - chain.Add(link); + chainsFromRoot.Add(link); Table beginTable = link.EndTable; //EndTable is now the begin table as iterating next level ... foreach (Relationship filteringRelationship in beginTable.FindFilteringRelationships()) { // EndTable can be either the From or the To table of a Relationship object depending on direction of CrossFilteringBehavior - string endTableName = filteringRelationship.TomRelationship.FromTable.Name == beginTable.Name ? filteringRelationship.TomRelationship.ToTable.Name : filteringRelationship.TomRelationship.FromTable.Name; + string endTableName = GetEndTableName(beginTable, filteringRelationship, out bool biDi); //Need to check if endTableName has already been covered by TablePath to avoid CrossFilteringBehavior leading both ways and never ending loop if (!link.TablePath.Contains("'" + endTableName + "'")) { - RelationshipLink newLink = new RelationshipLink(beginTable, _tables.FindByName(endTableName), false, link.TablePath, filteringRelationship); - ValidateLink(newLink, chain); + RelationshipLink newLink = new RelationshipLink(beginTable, _tables.FindByName(endTableName), false, link.TablePath, link.PrecedingPathBiDiInvoked, filteringRelationship, biDi); + ValidateLink(newLink, chainsFromRoot); } } } } + private string GetEndTableName(Table beginTable, Relationship filteringRelationship, out bool biDi) + { + string endTableName; + biDi = false; + if (filteringRelationship.TomRelationship.FromTable.Name == beginTable.Name) + { + endTableName = filteringRelationship.TomRelationship.ToTable.Name; + } + else + { + endTableName = filteringRelationship.TomRelationship.FromTable.Name; + biDi = true; + } + return endTableName; + } + #endregion #region Variation Cleanup @@ -1251,20 +1270,6 @@ namespace BismNormalizer.TabularCompare.TabularMetadata break; } - //todo delete - ////Not sure why switch statement not stopping on "case ObjectType.Perspective:" above. Fudge below. Todo2: test with later build. - //if (namedObjectSource.ObjectType.ToString() == "Perspective") - //{ - // foreach (Tom.Perspective tomPerspectiveTarget in tomCultureTarget.Model.Perspectives) - // { - // if (namedObjectSource.Name == tomPerspectiveTarget.Name) - // { - // namedObjectTarget = tomPerspectiveTarget; - // break; - // } - // } - //} - //If namedObjectTarget is null, the model object doesn't exist in target, so can ignore if (namedObjectTarget != null) { @@ -1275,6 +1280,11 @@ namespace BismNormalizer.TabularCompare.TabularMetadata if (translation.Object is NamedMetadataObject && ((NamedMetadataObject)translation.Object).Name == namedObjectSource.Name && translation.Object.ObjectType == namedObjectSource.ObjectType && + ( + //check columns are both in same table (could have columns with same name in different tables) + !(translation.Object.Parent.ObjectType == ObjectType.Table && namedObjectSource.Parent.ObjectType == ObjectType.Table) || + (((NamedMetadataObject)translation.Parent).Name == ((NamedMetadataObject)namedObjectSource.Parent).Name) + ) && translation.Property == translationSource.Property ) { @@ -1285,8 +1295,18 @@ namespace BismNormalizer.TabularCompare.TabularMetadata if (translationTarget != null) { //Translation already exists in cultureTarget for this object, so just ensure values match - //Also decouple from object in model and reset coupling - translationTarget.Object = namedObjectTarget; + //Also decouple from object in model and reset coupling if removed + if (translationTarget.Object.IsRemoved) + { + ObjectTranslation translationTargetReplacement = new ObjectTranslation(); + translationTargetReplacement.Object = namedObjectTarget; + translationTargetReplacement.Property = translationSource.Property; + translationTargetReplacement.Value = translationSource.Value; + tomCultureTarget.ObjectTranslations.Remove(translationTarget); + tomCultureTarget.ObjectTranslations.Add(translationTargetReplacement); + translationTarget = translationTargetReplacement; + } + //translationTarget.Object = namedObjectTarget; translationTarget.Value = translationSource.Value; } else diff --git a/BismNormalizer/BismNormalizer/TabularCompare/UI/TreeGridView.cs b/BismNormalizer/BismNormalizer/TabularCompare/UI/TreeGridView.cs index dfe4e91..8a2f1ea 100644 --- a/BismNormalizer/BismNormalizer/TabularCompare/UI/TreeGridView.cs +++ b/BismNormalizer/BismNormalizer/TabularCompare/UI/TreeGridView.cs @@ -64,7 +64,7 @@ namespace BismNormalizer.TabularCompare.UI // Cause edit mode to begin since edit mode is disabled to support // expanding/collapsing base.OnKeyDown(e); - if (!e.Handled) + if (!e.Handled && this.Rows.Count > 0) { if (e.KeyCode == Keys.F2 && this.CurrentCellAddress.X > -1 && this.CurrentCellAddress.Y >-1) { diff --git a/BismNormalizer/BismNormalizer/source.extension.vsixmanifest b/BismNormalizer/BismNormalizer/source.extension.vsixmanifest index 6c19d2c..5732a62 100644 --- a/BismNormalizer/BismNormalizer/source.extension.vsixmanifest +++ b/BismNormalizer/BismNormalizer/source.extension.vsixmanifest @@ -1,7 +1,7 @@  - + BISM Normalizer BISM Normalizer manages Analysis Services tabular models http://bism-normalizer.com/