From 9fab70a9888c4f1e9a4532fc98e7dff74960b48a Mon Sep 17 00:00:00 2001 From: ArturNawrocki <92156014+ArturNawrocki@users.noreply.github.com> Date: Wed, 26 Aug 2026 12:13:05 +0200 Subject: [PATCH 1/2] Fix reversed USERELATIONSHIP argument detection Fixes a false positive in the `INACTIVE_RELATIONSHIPS_THAT_ARE_NEVER_ACTIVATED` rule. The current expression detects `USERELATIONSHIP` only when its arguments follow the TOM `From` to `To` relationship order. DAX permits the two relationship columns to be supplied in either order. ## Changes - Check both valid `USERELATIONSHIP` argument orders. - Apply the check to measures and calculation items. - Preserve the existing rule ID, severity and scope. ## Validation Tested with: - `From` to `To` arguments in a measure - reversed arguments in a measure - `From` to `To` arguments in a calculation item - reversed arguments in a calculation item - an unused inactive relationship The unused relationship remains a violation. All four valid `USERELATIONSHIP` cases are recognised. ## Reference https://learn.microsoft.com/en-us/dax/userelationship-function-dax --- BestPracticeRules/BPARules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BestPracticeRules/BPARules.json b/BestPracticeRules/BPARules.json index 5bf7ab2..e18dd8e 100644 --- a/BestPracticeRules/BPARules.json +++ b/BestPracticeRules/BPARules.json @@ -337,7 +337,7 @@ "Description": "Inactive relationships are activated using the USERELATIONSHIP function. If an inactive relationship is not referenced in any measure via this function, the relationship will not be used. It should be determined whether the relationship is not necessary or to activate the relationship via this method.\r\n\r\nReference: https://docs.microsoft.com/power-bi/guidance/relationships-active-inactive\r\nReference: https://dax.guide/userelationship/", "Severity": 2, "Scope": "Relationship", - "Expression": "IsActive == false\r\nand not\r\n(\r\nModel.AllMeasures.Any(RegEx.IsMatch(Expression,\r\n\"(?i)USERELATIONSHIP\\s*\\(\\s*\\'*\" +\r\ncurrent.FromTable.Name + \"\\'*\\[\" + \r\ncurrent.FromColumn.Name + \"\\]\\s*,\\s*\\'*\" +\r\ncurrent.ToTable.Name + \"\\'*\\[\" +\r\ncurrent.ToColumn.Name + \"\\]\"))\r\nor\r\nModel.AllCalculationItems.Any(RegEx.IsMatch(Expression,\r\n\"(?i)USERELATIONSHIP\\s*\\(\\s*\\'*\" +\r\ncurrent.FromTable.Name + \"\\'*\\[\" + \r\ncurrent.FromColumn.Name + \"\\]\\s*,\\s*\\'*\" +\r\ncurrent.ToTable.Name + \"\\'*\\[\" +\r\ncurrent.ToColumn.Name + \"\\]\"))\r\n)", + "Expression": "IsActive == false\r\nand not\r\n(\r\n Model.AllMeasures.Any(\r\n RegEx.IsMatch(Expression, \"(?i)USERELATIONSHIP\\s*\\(\\s*\\'*\" + current.FromTable.Name + \"\\'*\\s*\\[\" + current.FromColumn.Name + \"\\]\\s*,\\s*\\'*\" + current.ToTable.Name + \"\\'*\\s*\\[\" + current.ToColumn.Name + \"\\]\")\r\n or RegEx.IsMatch(Expression, \"(?i)USERELATIONSHIP\\s*\\(\\s*\\'*\" + current.ToTable.Name + \"\\'*\\s*\\[\" + current.ToColumn.Name + \"\\]\\s*,\\s*\\'*\" + current.FromTable.Name + \"\\'*\\s*\\[\" + current.FromColumn.Name + \"\\]\")\r\n )\r\n or Model.AllCalculationItems.Any(\r\n RegEx.IsMatch(Expression, \"(?i)USERELATIONSHIP\\s*\\(\\s*\\'*\" + current.FromTable.Name + \"\\'*\\s*\\[\" + current.FromColumn.Name + \"\\]\\s*,\\s*\\'*\" + current.ToTable.Name + \"\\'*\\s*\\[\" + current.ToColumn.Name + \"\\]\")\r\n or RegEx.IsMatch(Expression, \"(?i)USERELATIONSHIP\\s*\\(\\s*\\'*\" + current.ToTable.Name + \"\\'*\\s*\\[\" + current.ToColumn.Name + \"\\]\\s*,\\s*\\'*\" + current.FromTable.Name + \"\\'*\\s*\\[\" + current.FromColumn.Name + \"\\]\")\r\n )\r\n)", "CompatibilityLevel": 1200 }, { From 3da7be322391a97f8052b68c5f93fbf8d8f4fdf2 Mon Sep 17 00:00:00 2001 From: ArturNawrocki <92156014+ArturNawrocki@users.noreply.github.com> Date: Wed, 26 Aug 2026 12:32:06 +0200 Subject: [PATCH 2/2] Update BPARules.json Small fix to INACTIVE_RELATIONSHIPS_THAT_ARE_NEVER_ACTIVATED --- BestPracticeRules/BPARules.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BestPracticeRules/BPARules.json b/BestPracticeRules/BPARules.json index e18dd8e..b22bbce 100644 --- a/BestPracticeRules/BPARules.json +++ b/BestPracticeRules/BPARules.json @@ -337,7 +337,7 @@ "Description": "Inactive relationships are activated using the USERELATIONSHIP function. If an inactive relationship is not referenced in any measure via this function, the relationship will not be used. It should be determined whether the relationship is not necessary or to activate the relationship via this method.\r\n\r\nReference: https://docs.microsoft.com/power-bi/guidance/relationships-active-inactive\r\nReference: https://dax.guide/userelationship/", "Severity": 2, "Scope": "Relationship", - "Expression": "IsActive == false\r\nand not\r\n(\r\n Model.AllMeasures.Any(\r\n RegEx.IsMatch(Expression, \"(?i)USERELATIONSHIP\\s*\\(\\s*\\'*\" + current.FromTable.Name + \"\\'*\\s*\\[\" + current.FromColumn.Name + \"\\]\\s*,\\s*\\'*\" + current.ToTable.Name + \"\\'*\\s*\\[\" + current.ToColumn.Name + \"\\]\")\r\n or RegEx.IsMatch(Expression, \"(?i)USERELATIONSHIP\\s*\\(\\s*\\'*\" + current.ToTable.Name + \"\\'*\\s*\\[\" + current.ToColumn.Name + \"\\]\\s*,\\s*\\'*\" + current.FromTable.Name + \"\\'*\\s*\\[\" + current.FromColumn.Name + \"\\]\")\r\n )\r\n or Model.AllCalculationItems.Any(\r\n RegEx.IsMatch(Expression, \"(?i)USERELATIONSHIP\\s*\\(\\s*\\'*\" + current.FromTable.Name + \"\\'*\\s*\\[\" + current.FromColumn.Name + \"\\]\\s*,\\s*\\'*\" + current.ToTable.Name + \"\\'*\\s*\\[\" + current.ToColumn.Name + \"\\]\")\r\n or RegEx.IsMatch(Expression, \"(?i)USERELATIONSHIP\\s*\\(\\s*\\'*\" + current.ToTable.Name + \"\\'*\\s*\\[\" + current.ToColumn.Name + \"\\]\\s*,\\s*\\'*\" + current.FromTable.Name + \"\\'*\\s*\\[\" + current.FromColumn.Name + \"\\]\")\r\n )\r\n)", + "Expression": "IsActive == false\r\nand not\r\n(\r\n Model.AllMeasures.Any(\r\n RegEx.IsMatch(Expression, \"(?i)USERELATIONSHIP\\s*\\(\\s*'*\" + RegEx.Escape(current.FromTable.Name) + \"'*\\s*\\[\" + RegEx.Escape(current.FromColumn.Name) + \"\\]\\s*,\\s*'*\" + RegEx.Escape(current.ToTable.Name) + \"'*\\s*\\[\" + RegEx.Escape(current.ToColumn.Name) + \"\\]\")\r\n or RegEx.IsMatch(Expression, \"(?i)USERELATIONSHIP\\s*\\(\\s*'*\" + RegEx.Escape(current.ToTable.Name) + \"'*\\s*\\[\" + RegEx.Escape(current.ToColumn.Name) + \"\\]\\s*,\\s*'*\" + RegEx.Escape(current.FromTable.Name) + \"'*\\s*\\[\" + RegEx.Escape(current.FromColumn.Name) + \"\\]\")\r\n )\r\n or Model.AllCalculationItems.Any(\r\n RegEx.IsMatch(Expression, \"(?i)USERELATIONSHIP\\s*\\(\\s*'*\" + RegEx.Escape(current.FromTable.Name) + \"'*\\s*\\[\" + RegEx.Escape(current.FromColumn.Name) + \"\\]\\s*,\\s*'*\" + RegEx.Escape(current.ToTable.Name) + \"'*\\s*\\[\" + RegEx.Escape(current.ToColumn.Name) + \"\\]\")\r\n or RegEx.IsMatch(Expression, \"(?i)USERELATIONSHIP\\s*\\(\\s*'*\" + RegEx.Escape(current.ToTable.Name) + \"'*\\s*\\[\" + RegEx.Escape(current.ToColumn.Name) + \"\\]\\s*,\\s*'*\" + RegEx.Escape(current.FromTable.Name) + \"'*\\s*\\[\" + RegEx.Escape(current.FromColumn.Name) + \"\\]\")\r\n )\r\n)", "CompatibilityLevel": 1200 }, {