diff --git a/configuration/filterConfig.yml b/configuration/filterConfig.yml
index 1ff23c96..a7f328cb 100644
--- a/configuration/filterConfig.yml
+++ b/configuration/filterConfig.yml
@@ -1,4 +1,5 @@
apiRules:
+# TabularEditor.Shared exclusions
- exclude:
uidRegex: ^TabularEditor\.Shared\.Services
type: Namespace
@@ -11,6 +12,40 @@ apiRules:
- exclude:
uidRegex: ^TabularEditor\.TOMWrapper\.Utils\.DaxDependencyHelper\.GetCachedSemantics
type: Method
+
+# SemanticBridge exclusions - only expose SemanticBridgeService, DatabricksMetricViewService, and their dependencies
+- exclude:
+ uidRegex: ^TabularEditor\.SemanticBridge\.ServiceInitializationResult
+ type: Type
+- exclude:
+ uidRegex: ^TabularEditor\.SemanticBridge\.AbstractModel
+ type: Namespace
+- exclude:
+ uidRegex: ^TabularEditor\.SemanticBridge\.Exceptions
+ type: Namespace
+- exclude:
+ uidRegex: ^TabularEditor\.SemanticBridge\.Util
+ type: Namespace
+- exclude:
+ uidRegex: ^TabularEditor\.SemanticBridge\.Platforms\.Databricks\.Exceptions
+ type: Namespace
+- exclude:
+ uidRegex: ^TabularEditor\.SemanticBridge\.Platforms\.Databricks\.Mapping
+ type: Namespace
+- exclude:
+ uidRegex: ^TabularEditor\.SemanticBridge\.Platforms\.Databricks\.MetricView\.Extensions
+ type: Namespace
+- exclude:
+ uidRegex: ^TabularEditor\.SemanticBridge\.Platforms\.Databricks\.MetricView\.Serialization
+ type: Namespace
+- exclude:
+ uidRegex: ^TabularEditor\.SemanticBridge\.Platforms\.Tabular
+ type: Namespace
+- exclude:
+ uidRegex: ^TabularEditor\.SemanticBridge\.Orchestration\.(?!DiagnosticMessage|DiagnosticSeverity)
+ type: Type
+
+# Global exclusion for EditorBrowsable(Never)
- exclude:
hasAttribute:
uid: System.ComponentModel.EditorBrowsableAttribute
diff --git a/content/_apiSource/SemanticBridge.dll b/content/_apiSource/SemanticBridge.dll
new file mode 100644
index 00000000..bc51edf8
Binary files /dev/null and b/content/_apiSource/SemanticBridge.dll differ
diff --git a/content/_apiSource/SemanticBridge.xml b/content/_apiSource/SemanticBridge.xml
new file mode 100644
index 00000000..b62ada94
--- /dev/null
+++ b/content/_apiSource/SemanticBridge.xml
@@ -0,0 +1,4252 @@
+
+
+
+ SemanticBridge
+
+
+
+
+ Base record for model objects with common properties
+
+
+
+
+ Base record for model objects with common properties
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ These are defined such that the success case is the lowest integral value.
+
+
+
+
+ Key of the field fragment that represents the primary key for this dimension.
+ If not set, no field will be marked as a key.
+
+
+
+
+ Ensure FactKey, DimensionKey, and RelationshipKey are all set, and that
+ the relationships are valid according to cardinality constraints:
+ fact to dim is N:1 and left-to-right. Additionally ensure that the
+ referenced fact knows about this dimension reference.
+
+
+
+
+
+ Used as a placeholder source for dummy fact
+
+
+
+
+ Used as a placeholder for dimension references during construction.
+ Since Fact is a record, this creates a minimal instance rather than inheriting.
+
+
+
+
+ Used as a placeholder for fields during construction of an ITableLike
+
+
+
+
+ Always call after resolving fields
+
+
+
+
+
+ Platform-specific expression for derived fields.
+ When set, the fragment builds to a DerivedField instead of a Field.
+
+
+
+
+ Gets the keys of all fields in this table-like fragment.
+
+
+
+
+ Ensure fragment is a valid part of a model.
+
+ A collection of build errors found during resolution.
+
+ Intended to be used as part of the model build process.
+ If errors are returned, then the model cannot have a fully successful
+ build; partial success is possible
+
+
+
+
+ Resolve left and right fields, check that cardinalities, kind, and
+ direction are set. If direction is not set, check whether sides are
+ fact and dimension: set to dimension filters fact.
+
+
+ Build errors:
+ - if missing or nonexistent field references
+ - if not a fact and dimension
+ - if any property is unset that is required for an Root.Relationship
+
+
+
+
+
+ A field derived from a platform-specific expression.
+ Similar to CalculatedMeasure, this represents a computed value
+ that cannot be directly translated to DAX.
+
+
+
+
+ Mapping of platforms to platform-specific expressions implementing
+ the field calculation.
+
+
+
+
+ Data type of the derived field.
+
+
+
+
+ The table containing this field.
+
+
+
+
+ Constructor for DerivedField.
+
+ Friendly name for display to users
+ Name for internal use by the OLAP engine
+ Prose describing the object
+ The table containing this field
+ Data type of the field
+
+ Mapping of platforms to platform-specific expressions implementing
+ the calculation
+
+
+
+
+ Determines equality by comparing scalar properties and Expressions
+ dictionary contents. Excludes parent Table to avoid circular references.
+
+ The other DerivedField to compare with
+
+ True if both objects have equal scalar properties and equivalent Expressions
+
+
+
+
+ Generates hash code based on scalar properties and Expressions
+ dictionary contents. Excludes parent Table to avoid circular references.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ Accepts a visitor for processing this DerivedField.
+
+
+
+
+ Context for analysis
+
+
+
+
+ Collection of descriptive properties
+
+
+
+
+ Technical name of the primary key field for this dimension.
+ Null if no primary key is explicitly defined.
+
+
+
+
+ Collection of hierarchies
+
+
+
+
+ Reference to source system and queryable or embedded query
+
+
+
+
+ Determines equality by comparing scalar properties and collection
+ contents order-independently.
+
+ The other Dimension to compare with
+
+ True if both objects have equal scalar properties and equivalent collections.
+
+
+
+
+ Generates hash code based on scalar properties and collection contents.
+ Fields property is not included since it's computed from Attributes.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ Descriptive property in a dimension
+
+
+
+
+ Creates a DimensionAttribute from any IField, setting the dimension as the parent table.
+
+
+
+
+ Data type of the attribute
+
+
+
+
+ Field reference in the source; the TechnicalName of the source field.
+
+
+
+
+ Indicates if this attribute is a key
+
+
+
+
+ The underlying field this attribute wraps.
+ Used by visitors to determine if special handling is needed (e.g., DerivedField).
+
+
+
+
+ Determines equality by comparing scalar properties and underlying field type,
+ excluding parent Dimension to avoid circular references.
+
+ The other DimensionAttribute to compare with
+
+ True if both objects have equal scalar properties (excluding parent reference)
+
+
+
+
+ Generates hash code based on scalar properties excluding parent
+ Dimension to avoid circular references.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ Determines equality by comparing scalar properties and child references,
+ excluding parent Fact to avoid circular references.
+
+ The other DimensionReference to compare with
+
+ True if both objects have equal scalar properties and equivalent child references
+
+
+
+
+ Generates hash code based on scalar properties and child references,
+ excluding parent Fact to avoid circular references.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ Data types for attributes and measures
+
+
+
+
+ Permission levels for object rules
+
+
+
+
+ Represents a business event or measurement context
+
+
+
+
+ Collection of quantifiable values
+
+
+
+
+ Collection of dimension references
+
+
+
+
+ Type of fact
+
+
+
+
+ Reference to source system and queryable or embedded query
+
+
+
+
+ Determines equality by comparing scalar properties and collection contents order-independently.
+
+ The other Fact to compare with
+ True if both objects have equal scalar properties and equivalent collections
+
+
+
+ Generates hash code based on scalar properties and collection contents.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ Type of fact table
+
+
+
+
+ Additivity types for measures
+
+
+
+
+ Aggregation types for measures
+
+
+
+
+ Determines equality by comparing scalar properties excluding parent
+ Table to avoid circular references.
+
+ The other Field to compare with
+
+ True if both objects have equal scalar properties (excluding
+ parent reference)
+
+
+
+
+ Generates hash code based on scalar properties excluding parent
+ Table to avoid circular references.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ Logical path for drill-down analysis
+
+
+
+
+ Ordered collection of dimension attributes
+
+
+
+
+ Indicates if hierarchy has inconsistent depth
+
+
+
+
+ Determines equality by comparing scalar properties and Levels
+ collection contents.
+
+ The other Hierarchy to compare with
+ True if both objects have equal scalar properties and equivalent Levels
+
+
+
+ Generates hash code based on scalar properties and Levels
+ collection contents.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ Common properties for all model objects
+
+
+
+
+ Friendly name for display to users
+
+
+
+
+ Name for internal use in formulas by the OLAP engine, potentially the same as Name
+
+
+
+
+ Prose describing the object
+
+
+
+
+ Represents a data source system that can be referenced by model objects
+
+
+
+
+ Type-specific identifier for this source system
+
+
+
+
+ Represents a source reference with query information
+
+
+
+
+ Reference to the source system
+
+
+
+
+ Database or catalog name
+
+
+
+
+ Marker interface for Abstract Model objects that can accept visitors.
+ All Abstract Model domain objects (Root, Fact, Dimension, Measure, etc.)
+ implement this. This interface enables the visitor pattern for
+ traversing the Abstract Model object graph.
+
+
+
+
+ Accepts a visitor that produces a result of type TResult.
+ The concrete type dispatches to the appropriate Visit overload on
+ the visitor based on its runtime type.
+
+ The type of value produced by the visitor
+ The visitor to accept
+ The result produced by the visitor
+
+
+
+ Visitor interface for the Abstract Model object graph.
+ Extends the generic IVisitor pattern with Abstract Model-specific
+ visitation methods. This interface enables type-safe transformation of
+ Abstract Model objects while supporting generic orchestration through
+ the base IVisitor interface.
+
+
+ The type of value produced by visiting Abstract Model objects
+
+
+
+
+ Visits a Root (the top-level Abstract Model container).
+
+
+
+
+ Visits a Fact (business event or measurement context).
+
+
+
+
+ Visits a Dimension (context for analysis).
+
+
+
+
+ Visits a DimensionReference (link between Fact and Dimension).
+
+
+
+
+ Visits a SimpleMeasure (quantifiable value with simple aggregation).
+
+
+
+
+ Visits a CalculatedMeasure (quantifiable value with
+ platform-specific calculation).
+
+
+
+
+ Visits a SimpleRelationship.
+
+
+
+
+ Visits a RolePlayingRelationship.
+
+
+
+
+ Visits a DimensionAttribute (descriptive property in a dimension).
+
+
+
+
+ Visits a Hierarchy (logical drill-down path).
+
+
+
+
+ Visits a Field.
+
+
+
+
+ Visits a DerivedField (field with platform-specific expression).
+
+
+
+
+ Visits a QueryableSource (source referring to a table/view).
+
+
+
+
+ Visits a QuerySource (source using custom query).
+
+
+
+
+ Visits a Perspective (named subset of model objects).
+
+
+
+
+ Visits a SecurityModel (access control specifications).
+
+
+
+
+ Visits a Role (security role definition).
+
+
+
+
+ Base record for measures with common properties
+
+
+
+
+ Display formatting
+
+
+
+
+ Constructor for base Measure record
+
+ Friendly name for display to users
+ Name for internal use in formulas by the OLAP engine
+ Prose describing the object
+ Display formatting
+ Data type of the measure
+
+
+
+ Accepts a visitor for the appropriate measure subtype.
+
+
+
+
+ Quantifiable value in a fact, with a simple aggregation
+
+
+
+
+ Field reference
+
+
+
+
+ Table containing the field
+
+
+
+
+ Additivity type
+
+
+
+
+ Collection of dimensions over which a semi-additive measure is additive
+
+
+
+
+ Aggregation type
+
+
+
+
+ Constructor for SimpleMeasure
+
+ Friendly name for display to users
+ Name for internal use in formulas by the OLAP engine
+ Prose describing the object
+ Display formatting
+ Data type of the measure
+ Field reference
+ Table containing the field
+ Additivity type
+
+ Collection of dimensions over which a semi-additive measure is additive
+
+ Aggregation type
+
+
+
+ Determines equality by comparing scalar properties and
+ AdditiveOverDimensions collection contents.
+
+ The other SimpleMeasure to compare with
+
+ True if both objects have equal scalar properties and equivalent collections
+
+
+
+
+ Generates hash code based on scalar properties and
+ AdditiveOverDimensions collection contents.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ Quantifiable value in a fact, with a platform-specific calculation
+
+ Currently no parsing, validation, or attempted translation for these
+
+
+
+ Mapping of platforms to platform-specific expressions implementing
+ the calculation
+
+
+
+
+ Constructor for CalculatedMeasure
+
+ Friendly name for display to users
+ Name for internal use in formulas by the OLAP engine
+ Prose describing the object
+ Display formatting
+ Data type of the measure
+
+ Mapping of platforms to platform-specific expressions implementing
+ the calculation
+
+
+
+
+ Determines equality by comparing scalar properties and Expressions
+ dictionary contents.
+
+ The other CalculatedMeasure to compare with
+
+ True if both objects have equal scalar properties and equivalent Expressions
+
+
+
+
+ Generates hash code based on scalar properties and Expressions
+ dictionary contents.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ Named subset of model objects for specific usage
+
+
+
+
+ Collection of included model objects
+
+
+
+
+ Documentation of intended users
+
+
+
+
+ Determines equality by comparing scalar properties and ModelObjects
+ collection contents.
+
+ The other Perspective to compare with
+
+ True if both objects have equal scalar properties and equivalent ModelObjects
+
+
+
+
+ Generates hash code based on scalar properties and ModelObjects
+ collection contents.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ Cardinality of a field in a join/relationship
+
+
+
+
+ Abstract representation of a dimensional model
+
+
+
+
+ Collection of facts in the model
+
+
+
+
+ Collection of dimensions in the model
+
+
+
+
+ Collection of perspectives in the model
+
+
+
+
+ Security model
+
+
+
+
+ Determines equality by comparing scalar properties and collection contents order-independently.
+
+ The other Root to compare with
+ True if both objects have equal scalar properties and equivalent collections
+
+
+
+ Generates hash code based on scalar properties and collection contents.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ Creates a dimension reference between a fact and dimension with a
+ simple equi-join relationship.
+
+ Name of the fact to add the dimension reference to
+ Name of the dimension to reference
+ Foreign key field in the fact
+ Primary key field in the dimension
+
+ Filter direction for the relationship (defaults to RightFiltersLeft)
+
+ The created dimension reference
+
+
+
+ Creates a dimension reference between a fact and dimension with a
+ simple equi-join relationship, looking up fields by name.
+
+ Name of the fact to add the dimension reference to
+ Name of the dimension to reference
+ Name of the foreign key field in the fact
+ Name of the primary key field in the dimension
+
+ Filter direction for the relationship (defaults to RightFiltersLeft)
+
+ The created dimension reference
+
+
+
+ Creates a dimension reference between a fact and dimension with the
+ specified relationship.
+
+ Name of the fact to add the dimension reference to
+ Name of the dimension to reference
+ The relationship defining how fact and dimension connect
+ The created dimension reference
+
+
+
+ Row level security rule
+
+
+
+
+ Row level security rule
+
+
+
+
+ Target table or dimension
+
+
+
+
+ Filter expression
+
+
+
+
+ Object level security rule
+
+
+
+
+ Object level security rule
+
+
+
+
+ Target object
+
+
+
+
+ Permission level
+
+
+
+
+ Security role definition
+
+
+
+
+ Expressions restricting data access by dimension values
+
+
+
+
+ Settings controlling object visibility
+
+
+
+
+ Users who belong to a given role
+
+
+
+
+ Determines equality by comparing scalar properties and collection contents.
+
+ The other Role to compare with
+
+ True if both objects have equal scalar properties and equivalent collections
+
+
+
+
+ Generates hash code based on scalar properties and collection contents.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ Access control specifications
+
+
+
+
+ Collection of roles
+
+
+
+
+ Determines equality by comparing scalar properties and Roles
+ collection contents.
+
+ The other SecurityModel to compare with
+ True if both objects have equal scalar properties and equivalent Roles
+
+
+
+ Generates hash code based on scalar properties and Roles
+ collection contents.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ Represents a source that refers to a specific queryable object (table/view)
+
+
+
+
+ Determines equality by comparing scalar properties and Fields
+ dictionary contents.
+
+ The other QueryableSource to compare with
+ True if both objects have equal scalar properties and equivalent Fields
+
+
+
+ Generates hash code based on scalar properties and Fields
+ dictionary contents.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ Represents a source that uses a custom query
+
+
+
+
+ Determines equality by comparing scalar properties and Fields
+ dictionary contents.
+
+ The other QuerySource to compare with
+ True if both objects have equal scalar properties and equivalent Fields
+
+
+
+ Generates hash code based on scalar properties and Fields
+ dictionary contents.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ Databricks source system
+
+
+
+ Friendly name for display to users
+ Name for internal use by the OLAP engine
+ Description of the source system
+ Databricks host address
+ HTTP path for the Databricks workspace
+ Optional database name
+ Optional schema name
+ Optional port number (default is 443)
+
+
+
+ Host name or IP address
+
+
+
+
+ HTTP path for the Databricks workspace
+
+
+
+
+ Port number (default is 443)
+
+
+
+
+
+
+
+ Generates an M (Power Query) expression for the specified source.
+ Dispatches to type-specific generation methods based on ISource implementation.
+
+
+ The source to generate an M expression for (QueryableSource or QuerySource)
+
+ M expression string that can be used in a Tabular Model partition
+
+ Thrown when the source type is not supported
+
+
+
+
+ Generates M expression for a QueryableSource (table/view reference).
+ Creates Power Query M code that references a Databricks catalog object.
+
+
+ The queryable source containing database, schema, and object names
+
+ M expression referencing the Databricks object
+
+
+
+ Generates M expression for a QuerySource (custom SQL query).
+ Wraps a SQL query in Databricks connection context.
+
+ The query source containing the SQL query text
+ M expression that executes the query against Databricks
+
+
+
+ Determines equality by comparing scalar properties and Properties dictionary contents.
+
+ The other GenericSourceSystem to compare with
+ True if both objects have equal scalar properties and equivalent Properties
+
+
+
+ Generates hash code based on scalar properties and Properties dictionary contents.
+
+ A hash code that remains consistent for equivalent objects
+
+
+
+ ODBC source system
+
+
+
+ Friendly name for display to users
+ Name for internal use by the OLAP engine
+ Description of the source system
+ DSN name or connection identifier
+
+
+
+ DSN name or connection identifier
+
+
+
+
+
+
+
+ SQL Server source system
+
+
+
+ Friendly name for display to users
+ Name for internal use by the OLAP engine
+ Description of the source system
+ Server DNS name or IP address
+ Optional port number (default is 1433)
+
+
+
+ Server DNS name or IP address
+
+
+
+
+ Port number for SQL Server connection
+
+
+
+
+
+
+
+ Thrown in a mapping context when there is no way to represent a concept or
+ construct from the `SourceModel` in the `TargetModel`. Currently used both
+ for not-yet-supported and impossible-to-map.
+
+
+
+
+
+
+
+
+ Thrown in a mapping context when there is no way to represent a concept or
+ construct from the `SourceModel` in the `TargetModel`. Currently used both
+ for not-yet-supported and impossible-to-map.
+
+
+
+
+
+
+
+
+ Simple abstraction for checking if SemanticBridge feature is enabled.
+ This avoids circular dependency with TabularEditor3.Shared.
+ Internal to prevent license bypass - users cannot implement their own feature check.
+
+
+
+
+ Returns true if the SemanticBridge feature is enabled for the current license.
+
+
+
+
+ Generic visitor interface for traversing object graphs.
+
+
+
+
+ Databricks MetricView operations and state management service.
+ Manages a single current MetricView instance and provides operations for
+ loading, validation, and conversion to Tabular models.
+
+
+
+
+ Gets the currently loaded MetricView, or null if no view is loaded.
+ Use or to load a view.
+
+
+
+
+ Creates a new instance of the Databricks MetricView service.
+ This constructor is internal and should only be called by .
+
+
+
+
+ Loads a Databricks MetricView from a YAML file and sets it as the current view.
+ Loading a new view replaces any previously loaded view.
+
+ Full path to the YAML file on disk.
+ Thrown when the YAML is invalid.
+ Thrown when the file does not exist.
+
+
+
+ Deserializes a Databricks MetricView from a YAML string and sets it as the current view.
+ Deserializing a new view replaces any previously loaded view.
+
+ The YAML string to deserialize.
+ Thrown when the YAML is invalid.
+
+
+
+ Saves the currently loaded MetricView to a YAML file.
+
+ The path where the YAML file should be saved.
+ Thrown when no MetricView is currently loaded.
+ Thrown when the file cannot be written.
+
+
+
+ Serializes the currently loaded MetricView to a YAML string.
+
+ The YAML representation of the current MetricView.
+ Thrown when no MetricView is currently loaded.
+
+
+
+ Validates the currently loaded MetricView using default validation rules.
+
+ An enumerable of diagnostic messages from validation.
+ Thrown when no MetricView is currently loaded.
+
+
+
+ Validates the currently loaded MetricView using the provided custom validation rules.
+ Default rules are not applied unless explicitly included in the rules parameter.
+
+ The validation rules to apply.
+ An enumerable of diagnostic messages from validation.
+ Thrown when no MetricView is currently loaded.
+
+
+
+ Creates a context-aware validation rule for a specific MetricView object type.
+ The validation function receives both the object and the validation context.
+
+
+ Type of MetricView object the rule applies to (View, Join, Dimension, or Measure)
+
+ Unique name for the validation rule
+ Category for organizational purposes (e.g., "Naming", "Structure")
+
+ Validation function that receives the object and context, and returns diagnostic messages
+
+
+ A validation rule that can be used with
+
+
+
+
+ Creates a validation rule for a specific type of MetricView object with simple predicate validation.
+
+
+ Type of MetricView object the rule applies to (View, Join, Dimension, or Measure)
+
+ Unique name for the validation rule
+ Category for organizational purposes (e.g., "Naming", "Structure")
+ Error message returned when the object is invalid
+ Predicate function that returns true when the object is invalid
+
+ A validation rule that can be used with
+
+
+
+
+ Creates a validation rule for the root View object.
+
+ Name of the rule.
+ Category for the rule.
+ Error message to return if validation fails.
+ Predicate function to determine if the view is invalid.
+ A validation rule for use in validation.
+
+
+
+ Creates a validation rule specifically for Join objects.
+
+ Unique name for the validation rule
+ Category for organizational purposes
+ Error message returned when the join is invalid
+ Predicate function that returns true when the join is invalid
+ A validation rule for Join objects
+
+
+
+ Creates a validation rule specifically for Dimension objects.
+
+ Unique name for the validation rule
+ Category for organizational purposes
+ Error message returned when the dimension is invalid
+ Predicate function that returns true when the dimension is invalid
+ A validation rule for Dimension objects
+
+
+
+ Creates a validation rule for Measure objects.
+
+ Unique name for the validation rule
+ Category for organizational purposes
+ Error message returned when the measure is invalid
+ Predicate function that returns true when the measure is invalid
+ A validation rule for Measure objects
+
+
+
+ Converts the currently loaded Databricks MetricView to a Tabular Model.
+
+ The target TOM model to populate.
+ Databricks host name for connection.
+ Databricks HTTP path for connection.
+ Diagnostic messages from validation and conversion.
+ If true, abort on validation errors before creating TOM objects.
+ True if conversion succeeded, false if critical errors occurred.
+ Thrown when no MetricView is currently loaded.
+
+
+
+ Loads a MetricView from a YAML file and converts it to a Tabular Model in one operation.
+
+ Full path to the YAML file to load.
+ The target TOM model to populate.
+ Databricks host name for connection.
+ Databricks HTTP path for connection.
+ Diagnostic messages from validation and conversion.
+ If true, abort on validation errors before creating TOM objects.
+ True if conversion succeeded, false if critical errors occurred.
+
+
+
+ Throws InvalidOperationException if no MetricView is currently loaded.
+
+ Thrown when no MetricView is currently loaded.
+
+
+
+ Marker interface for all MetricView domain objects.
+ Used for type safety and identification in public API
+
+
+
+
+ Interface for validation rules that can be applied to metric view objects.
+ ValidMetricViewRules encapsulate a specific validation check that produces
+ diagnostic messages when validation fails.
+
+
+
+
+ Gets the unique name of the validation rule.
+ Names should be descriptive and indicate what the rule validates.
+
+
+
+
+ Gets the category of the validation rule for organizational purposes.
+ Categories help group related rules (e.g., "Names", "Structure", "Expressions").
+
+
+
+
+ Gets the type of object this rule is designed to validate.
+ This allows for efficient filtering and application of rules.
+
+
+
+
+ Validates the target object using the provided read-only context.
+ ValidMetricViewRules receive a read-only view to prevent accidental
+ mutation of validation state.
+
+ The object to validate
+
+ Read-only validation context with state information including path tracking
+ and uniqueness collections
+
+
+ Collection of diagnostic messages for any validation failures, or an empty
+ collection if validation passes
+
+
+
+
+ This is a platform-specific interface that enables the visitor pattern
+ for traversing the MetricView object graph.
+
+
+
+
+ Accepts a visitor that produces a result of type TResult.
+ The concrete type dispatches to the appropriate Visit overload on
+ the visitor based on its runtime type (View, Join, Dimension, or
+ Measure).
+
+ The type of value produced by the visitor
+ The visitor to accept
+ The result produced by the visitor
+
+
+
+ Visitor interface for the MetricView object graph.
+ Extends the generic IVisitor pattern with MetricView-specific visitation methods.
+ This interface enables type-safe transformation of MetricView objects while
+ supporting generic orchestration through the base IVisitor interface.
+
+ The type of value produced by visiting MetricView objects
+
+
+
+ Visits a MetricView root object.
+
+
+
+
+ Visits a Join (dimension relationship) in the MetricView.
+
+
+
+
+ Visits a Dimension (attribute) in the MetricView.
+
+
+
+
+ Visits a Measure (metric) in the MetricView.
+
+
+
+
+ Entry point for generic visitor pattern orchestration.
+ Delegates to the visitable object's Accept method for double dispatch,
+ which calls back to the appropriate type-specific Visit method (View, Join, Dimension, or Measure).
+ This method enables generic orchestration code to work with MetricView objects
+ through the IVisitor interface while maintaining complete type safety.
+ The compiler enforces that only IMetricViewVisitable objects can be passed to this method.
+
+ The MetricView object to visit (View, Join, Dimension, or Measure)
+ The builder with fragments added based on the visited object
+
+
+
+ Create relationship and dimension reference fragments from the join.
+ Ensure that the fields referenced are in the fact and dimension.
+ Ensure the dimension exists.
+
+
+
+
+
+
+ Helper for managing field fragments during Metric View to Abstract Model mapping.
+ Handles field lookup, creation, and type updates for fields referenced by measures.
+
+
+ This helper encapsulates the logic for ensuring fields exist in both the fact table
+ and its source, managing data type updates when measures require Decimal types,
+ and searching for existing fields by name or technical name.
+
+
+ Creates a new FieldFragmentHelper for managing field fragments.
+
+ The model builder containing all fragments
+ The fact fragment to manage fields for
+ The source fragment for the fact
+
+
+
+ Helper for managing field fragments during Metric View to Abstract Model mapping.
+ Handles field lookup, creation, and type updates for fields referenced by measures.
+
+
+ This helper encapsulates the logic for ensuring fields exist in both the fact table
+ and its source, managing data type updates when measures require Decimal types,
+ and searching for existing fields by name or technical name.
+
+
+ Creates a new FieldFragmentHelper for managing field fragments.
+
+ The model builder containing all fragments
+ The fact fragment to manage fields for
+ The source fragment for the fact
+
+
+
+ Finds the field fragment key for a given column reference.
+ Searches fact fields first, then dimension fields.
+ Matches by technical name or name.
+
+ The column reference to find
+ The field fragment key if found, otherwise null
+
+
+
+ Ensures a field exists for a measure, creating one if needed.
+ For aggregations that imply numeric values (SUM, AVG, MIN, MAX), updates
+ existing String fields to Decimal. COUNT aggregations don't imply a
+ particular field type, so the field's DataType is preserved.
+
+ Column reference from the measure expression
+ The aggregation type of the measure
+ Field fragment key for the field
+
+ This method handles two scenarios:
+
+ - Field exists (e.g., created by a dimension): Updates DataType to Decimal
+ - Field doesn't exist: Creates new field with Decimal type
+
+
+
+
+
+ Updates an existing field fragment and its corresponding source field
+ to the specified DataType, but only if the current type is String.
+ Non-String types (e.g., Integer keys) are preserved.
+
+ Key of the field fragment to update
+ New data type for the field
+
+
+
+ Creates new field fragments (fact and source) for a measure that
+ references a non-existent field.
+ Both Name and TechnicalName are set to the column name.
+
+ Column reference from the measure expression
+ Key of the created fact field fragment
+
+
+
+ Traverses a MetricView object graph and returns all nodes using the visitor pattern.
+
+
+
+
+ Returns all objects in the MetricView graph.
+
+
+
+
+ Static wrapper/convenience class for use in C# scripts
+
+
+
+
+ Loads a MetricView from a YAML file
+
+ full path to the yaml file on disk
+ an unvalidated MetricView
+
+ thrown when the YAML is invalid, but doesn't check for internal
+ consistency of the model
+
+
+
+
+ Deserialize a MetricView from a YAML string
+
+ the YAML string to deserialize
+ an unvalidated MetricView
+
+ thrown when the YAML is invalid, but doesn't check for internal
+ consistency of the model
+
+
+
+
+ Serialize the MetricView to YAML and save to disk
+
+ the MetricView to serialize
+ the path to save the YAML file
+
+
+
+ Serialize a MetricView to a YAML string
+
+ the MetricView to serialize
+ the serialized YAML string
+
+
+
+ The default validation rules to use when validating MetricViews.
+ Automatically used when calling `Validate(MetricView)` overload
+
+
+
+
+ Validate a MetricView using the provided validator
+
+ the MetricView to validate
+ the validator to use
+ enumerable of diagnostic messages resulting from the validation rules
+
+
+
+ Validate a MetricView using the default set of validation rules
+
+ MetricView to validate
+ enumerable of diagnostic messages resulting from the validation rules
+ The set of rules is available as `MetricViews.DefaultValidationRules`
+
+
+
+ Validate a MetricView using the provided set of rules
+
+ the MetricView to validate
+ the set of validation rules to apply
+ enumerable of diagnostic messages resulting from the validation rules
+ does not use any of the default rules unless you explicitly include these
+
+
+
+ Make a new MetricViewValidationVisitor with the default set of rules
+
+ a reusable MetricViewValidationVisitor to use as a validator
+
+
+
+ Make a new MetricViewValidationVisitor with the provided set of
+ rules, optionally including the default rules
+
+ the set of validation rules to use in the validator
+
+ whether to include the default rules (default: `true`)
+
+ a reusable MetricViewValidationVisitor to use as a validator
+
+
+
+ Creates a validation rule for a specific type of MetricView object.
+
+
+ type of MetricView object the rule applies to. One of `MetricView`,
+ `MetricViewDimension`, `MetricViewJoin`, `MetricViewMeasure`
+
+ name of the rule
+ category for the rule (you can use any text)
+ validation function
+ a validation rule for use in a validator
+
+ the validation function `validate` is a function of two arguments:
+ 1. the metric view object to which the rule is applied
+ 2. the validation context
+ validation context is a `IReadOnlyValidationContext` object which
+ provides:
+ - `PathStack`: identifies the exact object currently being
+ examined, with all of its parents (in reverse order from this
+ object to the root). In general you probably don't need to do
+ much with this.
+ - `JoinNames`: set of strings of all join names already seen
+ during validation
+ - `DimensionNames`: set of strings of all dimension names already
+ seen
+ - `MeasureNames`: set of strings of all measure names already seen
+ - `MakeError(message, property)`: function to create a diagnostic
+ error. **You should use this to create the errors you return**.
+ The first arg is a string describing the error. The second arg
+ is an optional property name (e.g. "Name") to append to the path.
+ **Only use the name of a MetricView object property**, not any
+ arbitrary string.
+ Your function must return an enumerable of DiagnosticMessage objects.
+ If there are no problems, return an empty enumerable (e.g. `[]`).
+ If the object is invalid according to the rule (your conditional
+ checks), then return an enumerable with one or more errors created
+ with the context's `MakeError` method. You can inspect any properties
+ of the object (the first arg to your function) or anything in the
+ context object.
+
+
+
+
+ Create a validation rule for a specific MetricView object type
+
+
+ type of MetricView object the rule applies to. One of `MetricView`,
+ `MetricViewDimension`, `MetricViewJoin`, `MetricViewMeasure`
+
+ name of the rule
+ category for the rule (you can use any text)
+ the message returned in the error if the object is invalid
+ a predicate function to determine if the object is invalid
+ a validation rule for use in a validator
+
+ the function `isInvalid` is a simple predicate function that can
+ only inspect the properties of the current MetricView object. If you
+ need to inspect other context, you should use the other overload of
+ `MakeValidationRule`
+
+
+
+
+ Create a validation rule for the root MetricView object
+
+ name of the rule
+ category for the rule (you can use any text)
+ the message returned in the error if the object is invalid
+ a predicate function to determine if the object is invalid
+ a validation rule for use in a validator
+
+
+
+ Create a validation rule for MetricViewJoin objects
+
+ name of the rule
+ category for the rule (you can use any text)
+
+ the message returned in the error if the MetricViewJoin is invalid
+
+
+ a predicate function to determine if the MetricViewJoin is invalid
+
+ a validation rule for use in a validator
+
+
+
+ Create a validation rule for MetricViewDimension objects
+
+ name of the rule
+ category for the rule (you can use any text)
+
+ the message returned in the error if the MetricViewDimension is invalid
+
+
+ a predicate function to determine if the MetricViewDimension is invalid
+
+ a validation rule for use in a validator
+
+
+
+ Create a validation rule for MetricViewMeasure objects
+
+ name of the rule
+ category for the rule (you can use any text)
+
+ the message returned in the error if the MetricViewMeasure is invalid
+
+
+ a predicate function to determine if the MetricViewMeasure is invalid
+
+ a validation rule for use in a validator
+
+
+
+ Converts a Databricks MetricView to a Tabular Model.
+
+
+ The target TOM model to populate with tables, columns, measures,
+ and relationships
+
+ The source MetricView to convert
+ Databricks host name for connection
+ Databricks HTTP path for connection
+
+ A list of diagnostic messages indicating details of partial or full
+ failues. May be populated even when the method returns true and
+ does create a TOM model.
+
+
+ if true, abort on validation errors before creating any TOM objects
+
+
+
+
+
+
+ Represents a dimension definition in a Databricks Metric View
+
+
+ A dimension in a Metric View is a single field. There is no namespacing
+ or delineation of separate named collections of fields.
+
+
+
+
+ The YAML name for the dimension field
+
+
+
+
+ The YAML string representing the expression for this dimension,
+ either a field reference or scalar SQL expression.
+
+
+
+
+ Lightweight value representation of a single column reference within a
+ Metric View expression. The parser populates only
+ when a single (non‑backticked) dot qualification is present (or when a
+ backticked token acts as table side). For undotted identifiers (or
+ identifiers whose dot is inside backticks) is the
+ empty string. Formatting policy is intentionally minimal: only quote
+ when required.
+
+
+ Optional table/alias portion (empty string if not present or not applicable).
+
+
+ The logical column identifier (never null; empty only for a
+ default-initialized value).
+
+
+
+
+ Lightweight value representation of a single column reference within a
+ Metric View expression. The parser populates only
+ when a single (non‑backticked) dot qualification is present (or when a
+ backticked token acts as table side). For undotted identifiers (or
+ identifiers whose dot is inside backticks) is the
+ empty string. Formatting policy is intentionally minimal: only quote
+ when required.
+
+
+ Optional table/alias portion (empty string if not present or not applicable).
+
+
+ The logical column identifier (never null; empty only for a
+ default-initialized value).
+
+
+
+
+ Optional table/alias portion (empty string if not present or not applicable).
+
+
+
+
+ The logical column identifier (never null; empty only for a
+ default-initialized value).
+
+
+
+
+ Render the reference using minimal quoting rules consistent with
+ the parser. Rules per side (table / column):
+ - If empty -> omitted (for table) or empty string for column.
+ - Any token containing a dot is always backticked (avoids
+ ambiguity and preserves original backticked literals like
+ `orders.amount`).
+ - Bare identifier pattern [A-Za-z_][A-Za-z0-9_]* AND not a
+ reserved word -> emitted bare.
+ - Otherwise wrapped in backticks (parser strips existing backticks
+ so internal backticks not expected).
+
+
+
+
+ Lightweight, heuristic parser for extracting column identifiers from
+ Databricks Metric View expressions. Focused only on documented patterns
+ (bare identifiers and backtick-delimited identifiers) and deliberately
+ avoids full SQL parsing. Silent on malformed input, returning what can
+ be confidently identified.
+
+
+
+
+ Databricks reserved keywords that must be backtick-quoted to be
+ part of an identifier.
+
+
+
+
+ Attempt to determine if an expression is exactly a single column
+ reference. Returns a structured
+ (table + column). Supports:
+ * Bare identifier (non-reserved)
+ * Backticked identifier (any content except stray backticks)
+ * Single dotted qualification outside backticks (table.column)
+ with rules:
+ - At most one dot outside backticks (multi-dot => throw
+ )
+ - No whitespace adjacent to the dot (spaced forms return false)
+ - BOTH sides must be bare (non-reserved) or individually
+ backticked. A bare reserved keyword is rejected on EITHER side;
+ backticks required to treat it literally.
+ * Entire expression backticked is treated as undotted column even
+ if inner text contains dots
+ All other forms return false (silent failure posture) except
+ multi-dot which throws.
+
+
+
+
+ Convenience wrapper over .
+
+
+
+
+ Extract distinct structured column references (first-seen order,
+ case-insensitive distinctness via linear scan). Dotted
+ (table.column) pairs are produced only when syntactically valid with
+ a single dot outside backticks and no whitespace adjacency.
+ Multi-dot contiguous sequences (a.b.c or variants mixing
+ backticked segments) throw .
+ Spaced-dot patterns yield separate undotted identifiers. Backticked
+ whole tokens with internal dots may still act as table side when
+ followed immediately by ".identifier" (e.g.
+ `orders.amount`.field -> table:"orders.amount", column:"field"). If
+ a backticked left side is followed by a bare reserved right side
+ (e.g. `orders`.CASE) both sides are skipped (zero references) to
+ avoid surfacing an unusable partial reference. Bare left reserved
+ (CASE.amount) is ignored and the right side is surfaced undotted.
+ Bare reserved right (orders.CASE) causes only the left side to be
+ surfaced undotted. Backticked right sides always pass regardless of
+ reserved status. Function heads (identifier immediately followed
+ by '(') are excluded.
+
+
+
+
+ Extract distinct structured column references with their source positions
+ (first-seen order, case-insensitive distinctness via linear scan).
+ Returns position information for each reference, enabling delimiter-aware parsing.
+
+
+ Position spans include the entire reference from start to end, including:
+ - Backticks (if present)
+ - Table qualification and dot (if present)
+ - Column name
+ Follows the same parsing rules as .
+
+
+
+
+ Parse one side of a potential dotted identifier (used only by
+ single-column detection). Bare reserved words are rejected on
+ BOTH sides; backticks required to treat them literally.
+
+
+
+
+ Remove all block (`/* ... */`) and line (`-- ...\n`) comments from
+ expr. Handles nested comments and normalizes line endings to `\n`.
+
+ Databricks SQL expression
+
+ A string with all comments removed, with '\n' line endings, and all
+ leading and trailing whitespace removed (this leading and trailing
+ space is trimmed after comment removal). Leading whitespace on an
+ individual line is not removed.
+
+
+
+
+ Represents the position and length of a column reference in source text.
+ Used for delimiter-aware parsing and error reporting.
+
+
+
+
+ Zero-based index where the column reference starts in the source string.
+
+
+
+
+ Length of the column reference in characters, including any backticks or qualifiers.
+
+
+
+
+ Zero-based index of the character after the last character of the reference.
+
+
+
+
+ Checks if a given position falls within this span.
+
+ Zero-based character position to check
+ True if position is within [Start, End), false otherwise
+
+
+
+ Creates a new span with the given start position and length.
+
+ Zero-based start position
+ Length in characters
+
+ Thrown when start or length is negative
+
+
+
+
+ A column reference with associated source position information.
+ Used when you need both the parsed reference and its location in source text.
+
+
+
+
+ The parsed column reference (table name and column name).
+
+
+
+
+ The position and length of this reference in the source text.
+
+
+
+
+ The table name (may be empty for unqualified references).
+
+
+
+
+ The column name.
+
+
+
+
+ Whether this is a qualified reference (table.column).
+
+
+
+
+ Zero-based start position in source text.
+
+
+
+
+ Length in characters.
+
+
+
+
+ Zero-based end position (exclusive).
+
+
+
+
+ Creates a new reference with position information.
+
+ The parsed column reference
+ The position span in source text
+
+
+
+ Creates a new reference with position information.
+
+ The parsed column reference
+ Zero-based start position
+ Length in characters
+
+
+
+ Checks if a given position falls within this reference's span.
+
+ Zero-based character position
+ True if position is within this span, false otherwise
+
+
+
+ Converts to SQL syntax (ignores position information).
+
+ SQL representation of the column reference
+
+
+
+ Represents the semantic analysis of a Databricks Metric View dimension.
+ Contains information about whether the dimension is a simple field reference
+ or a derived expression, along with field references and target table determination.
+
+
+
+ This is a discriminated union represented through inheritance.
+ Use pattern matching to determine whether the dimension is simple or derived,
+ then access the appropriate properties.
+
+
+ A simple dimension is one that directly references a single column from
+ the source or a joined table. A derived dimension contains a complex
+ expression that must be represented as a calculated column in the target.
+
+
+
+
+
+ The name of the dimension as it appears in the Metric View.
+
+
+
+
+ The complete expression string from the dimension.
+
+
+
+
+ Analysis result for a simple dimension that directly references a column.
+
+
+
+
+ The single field reference that this dimension maps to.
+
+
+
+
+ Analysis result for a derived dimension with a complex expression
+ that must be represented as a calculated column.
+
+
+
+
+ All column references found in the dimension expression.
+
+
+
+
+ The target table for the derived field.
+ This is determined by the source of the referenced fields:
+ - If all fields are from a single dimension, the derived field goes on that dimension
+ - If fields are from multiple tables or the source, the derived field goes on the fact
+
+
+
+
+ Represents the target table for a derived field.
+
+
+
+
+ The derived field should be placed on the fact table.
+
+
+
+
+ The derived field should be placed on a specific dimension table.
+
+
+
+
+ The name of the dimension (join alias) where the derived field belongs.
+
+
+
+
+ Extension methods for analyzing and converting Databricks Metric View dimensions.
+
+
+
+
+ Analyzes a dimension expression to determine if it's a simple field reference
+ or a complex derived expression, and identifies the target table.
+
+ The Metric View dimension to analyze
+
+ A DimensionAnalysis indicating whether the dimension is simple or derived,
+ with appropriate field references and target table information.
+
+
+
+
+ Determines the target table for a derived field based on the tables
+ referenced by its constituent fields.
+
+ The column references from the expression
+
+ A DerivedFieldTarget indicating whether the field should go on the fact
+ or a specific dimension table.
+
+
+ Target determination rules:
+
+ - All fields from source/empty table -> Fact
+ - All fields from a single dimension -> that Dimension
+ - Fields from multiple dimensions -> Fact
+ - Mix of source and dimension fields -> Fact
+
+
+
+
+
+ Parses join fields from MetricViewJoin. Handles 'Using' and 'On'.
+ Returns (factFieldKey, dimensionFieldKey) tuple or null if parsing fails.
+
+
+
+
+ Extracts LHS and RHS from an equality join expression
+
+
+
+
+ We do not handle inequality joins or fields with spaces in their names.
+ We expect fully qualified names on both sides.
+ See: https://docs.databricks.com/aws/en/metric-views/yaml-ref#joins
+
+
+
+
+ Creates a SourceFragment for a join's dimension source
+
+
+
+
+ Pattern:
+ ^\\s*(?<table1>\\w+)\\.(?<field1>\\w+)\\s*=\\s*(?<table2>\\w+)\\.(?<field2>\\w+)\\s*$
+ Explanation:
+
+ â—‹ Match if at the beginning of the string.
+ â—‹ Match a whitespace character atomically any number of times.
+ â—‹ "table1" capture group.
+ â—‹ Match a word character atomically at least once.
+ â—‹ Match '.'.
+ â—‹ "field1" capture group.
+ â—‹ Match a word character atomically at least once.
+ â—‹ Match a whitespace character atomically any number of times.
+ â—‹ Match '='.
+ â—‹ Match a whitespace character atomically any number of times.
+ â—‹ "table2" capture group.
+ â—‹ Match a word character atomically at least once.
+ â—‹ Match '.'.
+ â—‹ "field2" capture group.
+ â—‹ Match a word character atomically at least once.
+ â—‹ Match a whitespace character greedily any number of times.
+ â—‹ Match if at the end of the string or if before an ending newline.
+
+
+
+
+
+ Represents the semantic analysis of a Databricks Metric View measure.
+ Contains information about the measure's type, aggregation function,
+ and field references extracted from the expression.
+
+
+
+ This is a discriminated union represented through inheritance.
+ Use pattern matching or type checks to determine whether the measure
+ is simple or calculated, then access the appropriate properties.
+
+
+ A simple measure is one that can be translated to a basic aggregation in the
+ target platform. It must:
+
+
+ - Use a supported aggregation function (SUM, COUNT, MIN, MAX, AVG, COUNT DISTINCT)
+ - Reference exactly one field
+ - Have no complex expressions (arithmetic, nested functions, etc.)
+
+
+ Calculated measures contain more complex expressions that cannot be automatically
+ translated and must be handled platform-specifically.
+
+
+
+
+
+ The name of the measure as it appears in the Metric View.
+
+
+
+
+ The complete expression string from the measure.
+
+
+
+
+ Analysis result for a simple measure that can be directly translated
+ to a platform-specific aggregation.
+
+
+
+
+ The aggregation type (SUM, COUNT, MIN, MAX, AVG, DISTINCT COUNT).
+
+
+
+
+ The single field reference that this measure aggregates.
+
+
+
+
+ Analysis result for a calculated measure with a complex expression
+ that cannot be automatically translated.
+
+
+
+
+ All column references found in the measure expression.
+ May contain zero or more references depending on the expression.
+
+
+
+
+ Analyzes a Databricks Metric View measure to determine its semantic properties.
+
+ The measure to analyze
+
+ A - either
+ or . Use pattern matching or type checks
+ to determine which and access the appropriate properties.
+
+
+
+ A simple measure is one that can be translated to a basic aggregation in the
+ target platform. It must:
+
+
+ - Use a supported aggregation function (SUM, COUNT, MIN, MAX, AVG, COUNT DISTINCT)
+ - Reference exactly one field
+ - Have no complex expressions (arithmetic, nested functions, etc.)
+
+
+ Calculated measures contain more complex expressions that cannot be automatically
+ translated and must be handled platform-specifically.
+
+
+
+
+
+ Parses an aggregation function name from a measure expression and
+ maps it to AggregationType.
+ Supports: SUM, COUNT, MIN, MAX, AVG/AVERAGE, COUNT(DISTINCT ...)
+
+
+
+
+ Determines if a measure expression is a simple aggregation over a
+ single field. A simple aggregation has the pattern:
+ AGG_FUNCTION(field_reference) or COUNT(DISTINCT field_reference)
+
+
+
+ Uses a two-step validation approach:
+ 1. Extract all column references from the expression using robust ColumnReferenceParser
+ 2. Validate that expression structure is exactly FUNCTION(column) with no extra content
+
+
+ This approach handles all edge cases while preserving original quoting/formatting:
+
+
+ - Backticked identifiers with any content (including parentheses)
+ - Reserved word validation
+ - Qualified references (table.column)
+ - Superfluous quoting (validates structure, not exact string match)
+ - DISTINCT COUNT patterns
+
+
+
+
+
+ Pattern:
+ ^(SUM|COUNT|MIN|MAX|AVG|AVERAGE)\\s*\\(
+ Options:
+ RegexOptions.IgnoreCase
+ Explanation:
+
+ â—‹ Match if at the beginning of the string.
+ â—‹ 1st capture group.
+ â—‹ Match with 4 alternative expressions.
+ â—‹ Match a sequence of expressions.
+ â—‹ Match a character in the set [Ss].
+ â—‹ Match a character in the set [Uu].
+ â—‹ Match a character in the set [Mm].
+ â—‹ Match a sequence of expressions.
+ â—‹ Match a character in the set [Cc].
+ â—‹ Match a character in the set [Oo].
+ â—‹ Match a character in the set [Uu].
+ â—‹ Match a character in the set [Nn].
+ â—‹ Match a character in the set [Tt].
+ â—‹ Match a sequence of expressions.
+ â—‹ Match a character in the set [Mm].
+ â—‹ Match with 2 alternative expressions.
+ â—‹ Match a sequence of expressions.
+ â—‹ Match a character in the set [Ii\u0130].
+ â—‹ Match a character in the set [Nn].
+ â—‹ Match a sequence of expressions.
+ â—‹ Match a character in the set [Aa].
+ â—‹ Match a character in the set [Xx].
+ â—‹ Match a sequence of expressions.
+ â—‹ Match a character in the set [Aa].
+ â—‹ Match a character in the set [Vv].
+ â—‹ Match with 2 alternative expressions.
+ â—‹ Match a character in the set [Gg].
+ â—‹ Match a sequence of expressions.
+ â—‹ Match a character in the set [Ee].
+ â—‹ Match a character in the set [Rr].
+ â—‹ Match a character in the set [Aa].
+ â—‹ Match a character in the set [Gg].
+ â—‹ Match a character in the set [Ee].
+ â—‹ Match a whitespace character atomically any number of times.
+ â—‹ Match '('.
+
+
+
+
+
+ Pattern:
+ ^COUNT\\s*\\(\\s*DISTINCT\\s+
+ Options:
+ RegexOptions.IgnoreCase
+ Explanation:
+
+ â—‹ Match if at the beginning of the string.
+ â—‹ Match a character in the set [Cc].
+ â—‹ Match a character in the set [Oo].
+ â—‹ Match a character in the set [Uu].
+ â—‹ Match a character in the set [Nn].
+ â—‹ Match a character in the set [Tt].
+ â—‹ Match a whitespace character atomically any number of times.
+ â—‹ Match '('.
+ â—‹ Match a whitespace character atomically any number of times.
+ â—‹ Match a character in the set [Dd].
+ â—‹ Match a character in the set [Ii\u0130].
+ â—‹ Match a character in the set [Ss].
+ â—‹ Match a character in the set [Tt].
+ â—‹ Match a character in the set [Ii\u0130].
+ â—‹ Match a character in the set [Nn].
+ â—‹ Match a character in the set [Cc].
+ â—‹ Match a character in the set [Tt].
+ â—‹ Match a whitespace character atomically at least once.
+
+
+
+
+
+ Identifies the aggregation pattern and returns where content starts.
+
+ The expression to analyze
+ The position where content starts after the aggregation pattern, or null if no pattern found
+
+
+
+ Validates that the closing paren is at the end of the expression.
+ Only whitespace is allowed after the closing paren.
+
+ The expression to validate
+ The index of the closing parenthesis
+ True if closing paren is properly positioned, false otherwise
+
+
+
+ Extracts and trims content between two positions.
+
+ The expression containing the content
+ Start position (inclusive)
+ End position (exclusive)
+ The extracted and trimmed content, or null if invalid range or empty
+
+
+
+ Finds the matching closing paren for an opening paren at given index.
+ Handles nested parentheses correctly and skips parentheses inside
+ excluded spans (e.g., backticked identifiers).
+
+ The string to search within
+ The index of the opening parenthesis
+
+ Spans to exclude from paren matching (e.g., backticked identifiers).
+ Any parentheses within these spans are ignored.
+
+ The index of the matching closing parenthesis, or -1 if not found
+
+
+
+ Checks if a position falls within any excluded span.
+
+ The position to check
+ The list of excluded spans
+ True if position is within any span, false otherwise
+
+
+
+ Extracts the content inside aggregation function parentheses.
+ For DISTINCT COUNT, treats DISTINCT as part of the aggregation spec.
+ AGG_FUNCTION(content) -> returns "content"
+ COUNT(DISTINCT content) -> returns "content" (DISTINCT is part of
+ aggregation).
+ Returns null if pattern doesn't match or has invalid structure.
+
+
+ Uses position-aware column reference parsing to correctly handle
+ parentheses inside backticked identifiers (e.g., `Field) Name`).
+
+ The aggregation expression to parse
+ The content between parentheses, or null if extraction fails
+
+
+
+ Validates that expression has the structure of a simple aggregation.
+ Extracts content between parens and validates it's a single column reference.
+ Since ExtractAggregationContent now treats DISTINCT as part of the aggregation,
+ no special handling is needed for DISTINCT COUNT.
+
+
+
+
+ Converts a Databricks Metric View Measure to a MeasureFragment,
+ handling field lookup/creation and determining measure type.
+
+ The Metric View measure to convert
+ The Metric View containing the measure
+ The fact fragment to associate the measure with
+ The model builder for fragment management
+ A configured MeasureFragment ready to be added to the model
+
+
+ This method bridges the gap between measure analysis and fragment creation:
+
+
+ - Analyzes the measure using
+ - For simple measures: ensures field exists and creates aggregation fragment
+ - For calculated measures: creates expression-based fragment
+
+
+ Simple measures get a FieldKey and AggregationType.
+ Calculated measures get an Expression and no AggregationType.
+
+
+
+
+
+ Determines the appropriate DataType for a simple measure based on its aggregation type.
+ Count and DistinctCount produce integer results; other aggregations produce Decimal.
+
+
+
+
+ Extension methods for traversing a MetricView object graph by canonical
+ path string.
+
+
+
+
+ Parses a 3-part SQL table name into its constituent parts.
+
+ The source string to parse
+ Database, schema, and table names if parsing succeeds; null otherwise
+
+
+
+ Creates a SourceFragment with queryable properties if the source is
+ a 3-part table name, otherwise creates one with a query property.
+
+ The source string to create a fragment for
+ The fragment key
+ The fragment name
+ The source system key reference
+ A configured SourceFragment
+
+
+
+ Removes backticks from identifier if present.
+
+ The identifier to clean
+ The cleaned identifier without surrounding backticks
+
+
+
+ Pattern:
+ ^(?<database>[a-zA-Z_][a-zA-Z0-9_]*|`[^`]+`)\\.(?<schema>[a-zA-Z_][a-zA-Z0-9_]*|`[^`]+`)\\.(?<table>[a-zA-Z_][a-zA-Z0-9_]*|`[^`]+`)$
+ Options:
+ RegexOptions.Compiled
+ Explanation:
+
+ â—‹ Match if at the beginning of the string.
+ â—‹ "database" capture group.
+ â—‹ Match with 2 alternative expressions.
+ â—‹ Match a sequence of expressions.
+ â—‹ Match a character in the set [A-Z_a-z].
+ â—‹ Match a character in the set [0-9A-Z_a-z] atomically any number of times.
+ â—‹ Match a sequence of expressions.
+ â—‹ Match '`'.
+ â—‹ Match a character other than '`' atomically at least once.
+ â—‹ Match '`'.
+ â—‹ Match '.'.
+ â—‹ "schema" capture group.
+ â—‹ Match with 2 alternative expressions.
+ â—‹ Match a sequence of expressions.
+ â—‹ Match a character in the set [A-Z_a-z].
+ â—‹ Match a character in the set [0-9A-Z_a-z] atomically any number of times.
+ â—‹ Match a sequence of expressions.
+ â—‹ Match '`'.
+ â—‹ Match a character other than '`' atomically at least once.
+ â—‹ Match '`'.
+ â—‹ Match '.'.
+ â—‹ "table" capture group.
+ â—‹ Match with 2 alternative expressions.
+ â—‹ Match a sequence of expressions.
+ â—‹ Match a character in the set [A-Z_a-z].
+ â—‹ Match a character in the set [0-9A-Z_a-z] atomically any number of times.
+ â—‹ Match a sequence of expressions.
+ â—‹ Match '`'.
+ â—‹ Match a character other than '`' atomically at least once.
+ â—‹ Match '`'.
+ â—‹ Match if at the end of the string or if before an ending newline.
+
+
+
+
+
+ Represents a join definition in a Databricks Metric View
+
+
+
+
+ Name of the joined table
+
+
+
+
+ Source table or query for the join
+
+
+
+
+ Optional SQL boolean expression for the join condition
+
+
+
+
+ Optional list of column names to use for join
+
+
+
+
+ Child joins that are part of this join definition
+
+
+
+
+ Represents a measure definition in a Databricks Metric View
+
+
+
+
+ Aggregate SQL expression that defines the measure
+
+
+
+
+ Internal abstract base class for all MetricView domain objects.
+ Implements both the public marker interface and the internal visitable interface.
+
+
+
+
+ Accepts a visitor that produces a result of type TResult.
+ Must be implemented by derived domain objects.
+
+
+
+
+ Validator for MetricView objects after YAML deserialization. Collects
+ all validation errors. Intentionally a very limited set of rules. We
+ don't own the metric view spec, so this should be somewhat permissive
+ and only return an error for very obvious issues that should be
+ impossible.
+
+
+
+
+ Handles deserialization of Databricks Metric Views from YAML
+
+
+
+
+ Deserializes a YAML string into a MetricView object
+
+ The YAML string to deserialize
+ A MetricView object
+ If YAML is invalid or cannot be deserialized
+
+ If semantic validation fails, contains one or more
+ for each validation error.
+
+
+
+
+ Handles serialization of Databricks Metric Views to YAML
+
+
+
+
+ Serializes a MetricView object to a YAML string
+
+ The MetricView object to serialize
+ A YAML string representation
+
+
+
+ Represents a Databricks Metric View definition
+
+
+
+
+ Version of the metric view specification
+
+
+
+
+ The source data for the metric view, playing the role of a single fact
+
+
+
+
+ Optional SQL boolean expression that applies to all queries; a WHERE
+
+
+
+
+ Optional join definitions for the metric view
+
+
+ A join is also the place where dimension sources are defined; this
+ information exists nowhere else.
+
+
+
+
+ Array of dimension definitions
+
+
+
+
+ Array of measure definitions
+
+
+
+
+ Provides the default set of validation rules that match the current
+ implementation of MetricViewValidationVisitor.
+
+
+
+
+ Returns validation rules for the specified type.
+
+ The type of object to get rules for.
+ ValidMetricViewRules that apply to the specified type.
+
+
+
+ Returns validation rules in the specified category.
+
+ The category of rules to get.
+ ValidMetricViewRules that belong to the specified category.
+
+
+
+ Returns validation rules with the specified name.
+
+ The name of the rule to find.
+ The rule with the specified name, or null if not found.
+
+
+
+ Visitor that validates a MetricView object graph using configurable rules.
+
+
+
+
+ Visitor that validates a MetricView object graph using configurable rules.
+
+
+
+
+ The validation rules which will be applied during traversal.
+
+
+
+
+ Creates a new validation visitor with only the specified rules.
+
+ The validation rules to apply
+
+
+
+ Creates a new validation visitor with the default rules.
+
+
+
+
+ Creates a new validation visitor with the default rules and optional extra rules.
+
+ Extra rules to be used in traversal of the metric view
+
+
+
+
+ Validates a MetricView object by applying all applicable rules and
+ traversing its structure.
+
+
+
+
+ Validates a MetricViewJoin object by applying all applicable rules and
+ traversing any child joins.
+
+
+
+
+ Validates a MetricViewDimension object by applying all applicable rules.
+
+
+
+
+ Validates a MetricViewMeasure object by applying all applicable rules.
+
+
+
+
+ Applies all applicable validation rules to the target object.
+ A rule receives a read-only view of the validation context to prevent
+ accidental mutation.
+
+ The type of object being validated
+ type-appropriate rules to be applied to `target`
+ The object to validate
+
+
+
+ Reset all state in the validation context to ensure idempotent operation.
+
+
+
+
+ Read-only view of validation context state for consumption by validation rules
+
+
+
+
+ Read-only view of the stack tracking the current path in the object graph.
+
+
+
+
+ Read-only view of join names seen during traversal for uniqueness validation
+
+
+
+
+ Read-only view of dimension names seen during traversal for
+ uniqueness validation
+
+
+
+
+ Read-only view of measure names seen during traversal for
+ uniqueness validation
+
+
+
+
+ Creates a diagnostic error message with the current path context
+
+ The error message
+ The property that has the error
+ A DiagnosticMessage with Error severity
+
+
+
+ Mutable validation context for internal validation orchestration.
+ Extends the read-only interface with mutation capabilities.
+ This interface is internal to restrict mutation operations to the
+ validation framework.
+
+
+
+
+ Mutable stack tracking the current path in the object graph
+
+
+
+
+ Mutable set of join names seen during traversal for uniqueness validation
+
+
+
+
+ Mutable set of dimension names seen during traversal for
+ uniqueness validation
+
+
+
+
+ Mutable set of measure names seen during traversal for uniqueness validation
+
+
+
+
+ Resets the context state for a new validation run, ensuring idempotent operation
+
+
+
+
+ Context object that maintains state during validation traversal of a
+ MetricView object graph.
+ External consumers can instantiate this but can only access mutation
+ capabilities within the same assembly.
+
+
+
+
+ Creates a diagnostic error message with the current path context
+
+ The error message
+ The property that has the error
+ A DiagnosticMessage with Error severity
+
+
+
+ Resets the context state for a new validation run, ensuring idempotency
+ internal only. Public consumers only see the readonly interface.
+
+
+
+
+ Combines the path stack elements into a dot-notation path string
+
+ Optional property to append to the path
+ The combined path as a string
+
+
+
+ Generic implementation of a validation rule for a specific type of
+ MetricView object.
+
+ The type of object this rule validates
+
+
+
+ Generic implementation of a validation rule for a specific type of
+ MetricView object.
+
+ The type of object this rule validates
+
+
+
+ Gets the type of object this rule is designed to validate.
+
+
+
+
+ Validates the target object if it matches the expected type.
+
+ The object to validate
+ The read-only validation context
+
+ Collection of diagnostic messages for validation failures, or an
+ empty collection if validation passes
+
+
+
+
+ Maps Abstract Model DataType to Tabular Object Model DataType.
+
+
+
+
+ Extension methods for mapping SimpleMeasure to Tabular Model DAX
+
+
+
+
+ Generates a DAX expression for a SimpleMeasure.
+ Returns a diagnostic message on failure.
+
+ The SimpleMeasure to generate DAX for
+ The TOM model context
+
+ map of Abstract model names to names of created tables in TOM model
+
+ The generated DAX expression, or null on failure
+ A diagnostic message if validation fails, otherwise null
+
+
+
+ Maps an AggregationType to the corresponding DAX function name.
+ Returns null if the aggregation type is not supported.
+
+
+
+
+ Extension methods for mapping SimpleRelationship to Tabular Model relationships.
+
+
+
+
+ Creates a TOM relationship from a SimpleRelationship.
+ Returns a diagnostic message on failure.
+
+
+
+
+ Determines the direction of a relationship based on cardinality.
+ Returns the many-side field, one-side field, and their respective tables.
+
+
+
+
+ Maps Abstract Model FilterDirection to TOM CrossFilteringBehavior.
+
+
+
+
+ Visitor that maps from an Abstract Model to a Tabular (TOM) Model.
+ Traverses the Abstract Model object graph and mutates the provided TOM Model.
+
+
+ Creates a new visitor that will populate the specified Tabular model.
+
+ The TOMWrapper Model to populate with Abstract Model data
+
+
+
+ Visitor that maps from an Abstract Model to a Tabular (TOM) Model.
+ Traverses the Abstract Model object graph and mutates the provided TOM Model.
+
+
+ Creates a new visitor that will populate the specified Tabular model.
+
+ The TOMWrapper Model to populate with Abstract Model data
+
+
+
+ Entry point for generic visitor pattern orchestration.
+ Delegates to the visitable object's Accept method for double dispatch.
+
+
+
+
+ Visits the Root of the Abstract Model.
+ Traverses Facts, Dimensions, Perspectives, and SecurityModel.
+
+
+
+
+ Visits a Fact.
+ Creates a TOM Table and sets up context for child visitors.
+ Traverses Source, Fields, Measures, and DimensionReferences.
+
+
+
+
+ Visits a Dimension.
+ Creates a TOM Table and sets up context for child visitors.
+ Traverses Source , Attributes, and Hierarchies.
+
+
+
+
+ Visits a DimensionReference.
+ Sets context for relationship visitor and delegates relationship creation.
+
+
+
+
+ Visits a SimpleMeasure.
+ Generates a DAX expression based on aggregation type and creates a TOM measure.
+
+
+
+
+ Visits a CalculatedMeasure.
+
+
+
+
+ Visits a SimpleRelationship.
+ Creates a TOM relationship between the current table and the dimension table.
+
+
+
+
+ Visits a RolePlayingRelationship.
+ Currently not implemented - role-playing dimensions require special handling.
+
+
+
+
+ Visits a DimensionAttribute.
+ Creates a data column using the attribute's name and referencing
+ the source field, or delegates to DerivedField visitor if underlying field is derived.
+
+
+
+
+ Visits a Hierarchy.
+ Traverses Levels (which are DimensionAttributes).
+
+
+
+
+ Visits a Field.
+ Fields on sources are metadata and don't create columns.
+ Fields on facts create data columns.
+
+
+
+
+ Visits a DerivedField.
+ Creates a TOM CalculatedColumn with the original expression in a DAX comment.
+
+
+
+
+ Visits a QueryableSource.
+ Creates a TOM M partition with generated expression from the source system.
+ Must be called within a table context (set by Visit(Fact) or Visit(Dimension)).
+
+
+
+
+ Visits a QuerySource.
+ Creates a TOM M partition with generated expression from the source system.
+ Must be called within a table context (set by Visit(Fact) or Visit(Dimension)).
+
+
+
+
+ Visits a Perspective.
+ Note: ModelObjects in perspectives are not visitable, so we don't traverse them.
+
+
+
+
+ Visits a SecurityModel.
+ Traverses Roles.
+
+
+
+
+ Visits a Role.
+
+
+
+
+ Main entry point for cross-platform semantic model operations.
+
+
+
+ Access Pattern: This service is accessed via ScriptHost.SemanticBridge in C# scripts.
+ It is initialized once during application startup in the main routine
+ and remains constant for the application lifetime.
+
+
+ Lifecycle: Unlike ScriptHost.Model and ScriptHost.Selected, which are set per
+ script execution, SemanticBridge is an application-level singleton service. This allows service state
+ (such as the currently loaded MetricView) to persist across multiple script executions.
+
+
+ Feature Flag: Requires the SemanticBridge feature flag to be enabled for the current license tier.
+ Construction will fail with if the feature is not enabled.
+
+
+
+ Load and validate a MetricView in a C# script:
+
+ SemanticBridge.MetricView.Load(@"C:\path\to\metricview.yaml");
+ var diagnostics = SemanticBridge.MetricView.Validate();
+ foreach (var diag in diagnostics)
+ {
+ Output($"{diag.Severity}: {diag.Message}");
+ }
+
+ Import a MetricView into the current Tabular model:
+
+ SemanticBridge.MetricView.Load(@"C:\path\to\metricview.yaml");
+ var success = SemanticBridge.MetricView.ImportToTabular(
+ Model,
+ "databricks-host.cloud.databricks.com",
+ "/sql/1.0/warehouses/abc123",
+ out var diagnostics);
+ if (!success)
+ {
+ foreach (var error in diagnostics)
+ Error($"Import failed: {error.Message}");
+ }
+
+
+
+
+
+
+
+ Gets the Databricks MetricView operations and state management service.
+
+
+ This service manages a single MetricView instance at a time. Loading a new MetricView
+ replaces the current view. The view state persists across multiple script executions.
+
+
+
+
+ Create a new instance of the SemanticBridgeService.
+ Only intended to be called in the `TryCreate` method with feature
+ flag checks and wrapping in a `ServiceInitializationResult`.
+
+
+
+
+ Attempts to create a SemanticBridgeService instance with structured error handling.
+
+
+
+ This factory method uses the Result pattern to provide explicit handling of different failure modes:
+ - : Service initialized successfully
+ - : Feature not enabled for license
+ - : Unexpected initialization error
+
+
+ The Result includes the service instance when successful, making it self-contained and easy to use.
+
+
+ Feature check implementation to determine if SemanticBridge is enabled
+
+ A containing either the initialized service instance
+ or detailed information about why initialization failed.
+
+
+ Initialize service with explicit error handling:
+
+ var result = SemanticBridgeService.TryCreate(featureCheck);
+
+ switch (result)
+ {
+ case ServiceInitializationResult.Success success:
+ ScriptHost.SemanticBridge = success.Service;
+ break;
+
+ case ServiceInitializationResult.LicenseFailure license:
+ // Expected failure - log if needed
+ telemetry.TrackEvent("SemanticBridge.LicenseNotEnabled");
+ break;
+
+ case ServiceInitializationResult.InitializationFailure failure:
+ // Unexpected failure - log with details
+ telemetry.TrackException(failure.Exception, "SemanticBridge.InitializationFailure");
+ break;
+ }
+
+
+
+
+
+ Represents the result of attempting to initialize the SemanticBridge service.
+ This discriminated union provides structured error handling that distinguishes
+ between license restrictions, unexpected failures, and successful initialization.
+
+
+ The Result pattern encapsulates both the success/failure state and the service instance,
+ allowing callers to handle different failure modes explicitly while maintaining access
+ to the initialized service when successful.
+
+
+
+
+ Helpful information for the user about the success or failure of
+ the service initialization, with instructions or details in the
+ case of failure.
+
+
+
+
+ Service was successfully initialized and is ready for use.
+ The service instance is available via pattern matching (see
+ example)
+
+
+ var initResult = SemanticBridgeService.TryCreate(semanticBridgeFeatureCheck);
+ switch (initResult)
+ {
+ case ServiceInitializationResult.Success success:
+ var service = success.Service;
+ // use `service` for work
+ break;
+ case ServiceInitializationResult.SomeOtherCase otherCase:
+ //handle other case
+ ...
+ }
+
+ The successfully initialized SemanticBridgeService instance
+
+
+
+ Service was successfully initialized and is ready for use.
+ The service instance is available via pattern matching (see
+ example)
+
+
+ var initResult = SemanticBridgeService.TryCreate(semanticBridgeFeatureCheck);
+ switch (initResult)
+ {
+ case ServiceInitializationResult.Success success:
+ var service = success.Service;
+ // use `service` for work
+ break;
+ case ServiceInitializationResult.SomeOtherCase otherCase:
+ //handle other case
+ ...
+ }
+
+ The successfully initialized SemanticBridgeService instance
+
+
+ The successfully initialized SemanticBridgeService instance
+
+
+
+ Service initialization failed due to licensing restrictions.
+ The SemanticBridge feature is not enabled for the current license
+ tier and the user should reach out to support to acquire an
+ appropriate license.
+
+
+
+
+ Service initialization failed due to an unexpected error beyond licensing.
+ This typically indicates a configuration or dependency resolution issue.
+
+ Description of the initialization failure
+ The underlying exception, if available
+
+
+
+ Service initialization failed due to an unexpected error beyond licensing.
+ This typically indicates a configuration or dependency resolution issue.
+
+ Description of the initialization failure
+ The underlying exception, if available
+
+
+ The underlying exception, if available
+
+
+
+ Indicates whether initialization was successful.
+ This convenience property allows simple boolean checks without pattern matching.
+
+
+
+
+ A strongly-typed resource class, for looking up localized strings, etc.
+
+
+
+
+ Returns the cached ResourceManager instance used by this class.
+
+
+
+
+ Overrides the current thread's CurrentUICulture property for all
+ resource lookups using this strongly typed resource class.
+
+
+
+
+ Looks up a localized string similar to Fragment key cannot be null or empty..
+
+
+
+
+ Looks up a localized string similar to Fragment with key '{0}' is not of type {1}..
+
+
+
+
+ Looks up a localized string similar to Fragment with key '{0}' not found.
+
+
+
+
+ Looks up a localized string similar to Model.
+
+
+
+
+ Looks up a localized string similar to Could not parse join fields for join '{0}'..
+
+
+
+
+ Looks up a localized string similar to Join expression must reference 'source' table for fact side. Got: '{0}'.
+
+
+
+
+ Looks up a localized string similar to Multiple join conditions are not yet supported. Join '{0}' has {1} conditions..
+
+
+
+
+ Looks up a localized string similar to We do not currently support snowflake joins. Join: {0}.
+
+
+
+
+ Looks up a localized string similar to Cannot find dimension fragment with key '{0}' for dimension reference.
+
+
+
+
+ Looks up a localized string similar to Cannot find fact fragment with key '{0}' for dimension reference.
+
+
+
+
+ Looks up a localized string similar to Cannot find left field fragment with key '{0}' for dimension reference.
+
+
+
+
+ Looks up a localized string similar to Cannot find relationship fragment for dimension reference.
+
+
+
+
+ Looks up a localized string similar to Cannot find right field fragment with key '{0}' for dimension reference.
+
+
+
+
+ Looks up a localized string similar to Failed to add dimension reference: {0}.
+
+
+
+
+
+ Looks up a localized string similar to CalculatedMeasure '{0}' does not have any expressions defined..
+
+
+
+
+ Looks up a localized string similar to Cannot visit measure '{0}' without a current table context..
+
+
+
+
+ Looks up a localized string similar to DerivedField '{0}' contains a complex expression that could not be translated to DAX. The original expression has been preserved as a comment..
+
+
+
+
+ Looks up a localized string similar to DerivedField '{0}' does not have any expressions defined..
+
+
+
+
+ Looks up a localized string similar to Dimension table '{0}' does not exist in the model..
+
+
+
+
+ Looks up a localized string similar to DimensionAttribute '{0}' references source field '{1}' which does not exist in the source's field collection..
+
+
+
+
+ Looks up a localized string similar to Mapping CalculatedMeasure '{0}' to Tabular Model is not yet implemented..
+
+
+
+
+ Looks up a localized string similar to Measure '{0}' contains a complex expression that could not be translated to DAX. The original expression has been preserved as a comment..
+
+
+
+
+ Looks up a localized string similar to Multiple dots in column reference are unsupported (multi-part qualification)..
+
+
+
+
+ Looks up a localized string similar to Length must be non-negative.
+
+
+
+
+ Looks up a localized string similar to Start position must be non-negative.
+
+
+
+
+ Looks up a localized string similar to No MetricView is currently loaded. Call Load() or Deserialize() first..
+
+
+
+
+ Looks up a localized string similar to DataType '{0}' is not supported for mapping to Tabular Model..
+
+
+
+
+ Looks up a localized string similar to Join '{0}' must use exactly one column in the 'using' clause for the MVP implementation.
+
+
+
+
+ Looks up a localized string similar to Measure '{0}' must use a simple aggregate function (SUM, COUNT, MIN, MAX, AVG) for the MVP implementation.
+
+
+
+
+ Looks up a localized string similar to Join '{0}' must use a simple equality condition with table prefixes (e.g. 'source.column = dimension.column').
+
+
+
+
+ Looks up a localized string similar to Dimension '{0}' expr cannot be empty.
+
+
+
+
+ Looks up a localized string similar to Dimension name cannot be empty.
+
+
+
+
+ Looks up a localized string similar to Duplicate dimension name: '{0}'.
+
+
+
+
+ Looks up a localized string similar to Duplicate join name: '{0}'.
+
+
+
+
+ Looks up a localized string similar to Duplicate measure name: '{0}'.
+
+
+
+
+ Looks up a localized string similar to Expressions.
+
+
+
+
+ Looks up a localized string similar to Join '{0}' cannot specify both 'on' and 'using' clauses.
+
+
+
+
+ Looks up a localized string similar to Join '{0}' must have at least one column specified in the 'using' clause.
+
+
+
+
+ Looks up a localized string similar to Join '{0}' must specify either 'on' or 'using' clause.
+
+
+
+
+ Looks up a localized string similar to Join '{0}' source cannot be empty.
+
+
+
+
+ Looks up a localized string similar to Join name cannot be empty.
+
+
+
+
+ Looks up a localized string similar to Measure '{0}' expr cannot be empty.
+
+
+
+
+ Looks up a localized string similar to Measure name cannot be empty.
+
+
+
+
+ Looks up a localized string similar to Names.
+
+
+
+
+ Looks up a localized string similar to Structure.
+
+
+
+
+ Looks up a localized string similar to dimension expr cannot be empty.
+
+
+
+
+ Looks up a localized string similar to dimension name cannot be empty.
+
+
+
+
+ Looks up a localized string similar to join name cannot be empty.
+
+
+
+
+ Looks up a localized string similar to join source cannot be empty.
+
+
+
+
+ Looks up a localized string similar to measure expr cannot be empty.
+
+
+
+
+ Looks up a localized string similar to measure name cannot be empty.
+
+
+
+
+ Looks up a localized string similar to Structure.
+
+
+
+
+ Looks up a localized string similar to view source cannot be empty.
+
+
+
+
+ Looks up a localized string similar to A DimensionAttribute with the same name already exists for dimension {0}. Field: {1}..
+
+
+
+
+ Looks up a localized string similar to Cannot access dimension before it is set..
+
+
+
+
+ Looks up a localized string similar to Dimension Attribute key cannot be null or empty..
+
+
+
+
+ Looks up a localized string similar to Primary key field '{0}' not found in dimension fields.
+
+
+
+
+ Looks up a localized string similar to Relationship key cannot be null or empty..
+
+
+
+
+ Looks up a localized string similar to We do not yet support multiple relationships for a single dimension reference. DimRef: '{0}'; existing relationship: '{1}'.
+
+
+
+
+ Looks up a localized string similar to Dimension '{0}' not found in built objects. Dimensions must be built before dimension references..
+
+
+
+
+ Looks up a localized string similar to Relationship '{0}' could not be built or is not a relationship type.
+
+
+
+
+ Looks up a localized string similar to No relationship key has been set yet..
+
+
+
+
+ Looks up a localized string similar to Ambiguity resolving dimension reference. Dimension key already assigned as: '{0}', but relationship '{1}' refers to '{2}'.
+
+
+
+
+ Looks up a localized string similar to Ambiguity resolving fact reference. Fact key already assigned as: '{0}', but relationship '{1}' refers to '{2}'.
+
+
+
+
+ Looks up a localized string similar to Dimension reference '{0}' not found..
+
+
+
+
+ Looks up a localized string similar to Fact reference '{0}' not found..
+
+
+
+
+ Looks up a localized string similar to Failed to find dimension for relationship, '{0}'..
+
+
+
+
+ Looks up a localized string similar to Failed to find fact for relationship '{0}'.
+
+
+
+
+ Looks up a localized string similar to Failed to find LHS fact for relationship, '{0}'..
+
+
+
+
+ Looks up a localized string similar to Failed to find RHS dimension for relationship, '{0}'..
+
+
+
+
+ Looks up a localized string similar to Left field reference '{0}' not found in relationship '{1}'..
+
+
+
+
+ Looks up a localized string similar to Missing fact, dimension, and relationship details..
+
+
+
+
+ Looks up a localized string similar to Relationship '{0}' does not match the cardinality of
+ dimension '{1}' and fact '{2}'.
+ Expected: 1 to many or many to 1, but got: {3} to {4}..
+
+
+
+
+ Looks up a localized string similar to Relationship reference '{0}' not found..
+
+
+
+
+ Looks up a localized string similar to Right field reference '{0}' not found in relationship '{1}'..
+
+
+
+
+ Looks up a localized string similar to Warning, facts should be LHS of relationships. '{0}'.
+
+
+
+
+ Looks up a localized string similar to Cannot add the same field twice: {0}.
+
+
+
+
+ Looks up a localized string similar to Field name cannot be blank.
+
+
+
+
+ Looks up a localized string similar to Dimension reference key cannot be null or empty..
+
+
+
+
+ Looks up a localized string similar to Fact field key cannot be null or empty..
+
+
+
+
+ Looks up a localized string similar to Measure key cannot be null or empty..
+
+
+
+
+ Looks up a localized string similar to All measures in Fact could not be built..
+
+
+
+
+ Looks up a localized string similar to Could not find sourcekey: '{0}'.
+
+
+
+
+ Looks up a localized string similar to Field '{0}' not a field type.
+
+
+
+
+ Looks up a localized string similar to Measure '{0}' not a measure type.
+
+
+
+
+ Looks up a localized string similar to Source '{0}' not an ISource type.
+
+
+
+
+ Looks up a localized string similar to Table could not be built; all fields resulted in errors only..
+
+
+
+
+ Looks up a localized string similar to FactFragment.ResolveFields: Field '{0}' has a different TableLikeKey '{1}' than expected '{2}'..
+
+
+
+
+ Looks up a localized string similar to FactFragment.ResolveFields: Field '{0}' not found or not a valid field fragment..
+
+
+
+
+ Looks up a localized string similar to FactFragment.ResolveMeasures: Measure '{0}' has a different FactKey '{1}' than expected '{2}'..
+
+
+
+
+ Looks up a localized string similar to FactFragment.ResolveMeasures: Measure '{0}' not found or not a measure fragment..
+
+
+
+
+ Looks up a localized string similar to Cannot access table before it is set..
+
+
+
+
+ Looks up a localized string similar to {0} error: Field not associated with any table..
+
+
+
+
+ Looks up a localized string similar to {0} error: Multiple tables contain field. Using '{1}', also found {2}'.
+
+
+
+
+ Looks up a localized string similar to Field '{0}' references an invalid table-like object '{1}'..
+
+
+
+
+ Looks up a localized string similar to Unsupported Join.On expression. We support 'source.field = dim.field'. Got '{0}'..
+
+
+
+
+ Looks up a localized string similar to Join.On expression cannot be empty.
+
+
+
+
+ Looks up a localized string similar to Metric View join must be 'Using' xor 'On'..
+
+
+
+
+ Looks up a localized string similar to Join kind is invalid.
+
+
+
+
+ Looks up a localized string similar to Using joins are not yet supported..
+
+
+
+
+ Looks up a localized string similar to The SemanticBridge feature is not enabled for the current license tier and the user should reach out to support to acquire an appropriate license..
+
+
+
+
+ Looks up a localized string similar to AggregationType is not set for simple measure.
+
+
+
+
+ Looks up a localized string similar to Could not build measure.
+
+
+
+
+ Looks up a localized string similar to Measure cannot have both Field and Expression; treating as a simple measure..
+
+
+
+
+ Looks up a localized string similar to Measure '{0}' is associated with multiple Facts: {1}. Using the first one: {2}..
+
+
+
+
+ Looks up a localized string similar to Measure '{0}' is not associated with any Fact..
+
+
+
+
+ Looks up a localized string similar to Measure.Resolve: Fact '{0}' does not exist for field '{1}'..
+
+
+
+
+ Looks up a localized string similar to Measure.Resolve: Field '{0}' not found..
+
+
+
+
+ Looks up a localized string similar to Failed to deserialize MetricView from YAML string.
+
+
+
+
+ Looks up a localized string similar to Failed to load MetricView from {0}.
+
+
+
+
+ Looks up a localized string similar to Failed to deserialize YAML to MetricView: {0}.
+
+
+
+
+ Looks up a localized string similar to Failed to deserialize YAML to MetricView: One or more validation errors occurred..
+
+
+
+
+ Looks up a localized string similar to Failed to validate MetricView.
+
+
+
+
+ Looks up a localized string similar to Attempted dictionary key lookup on non-collection type '{0}'..
+
+
+
+
+ Looks up a localized string similar to No item with Name '{0}' found in collection..
+
+
+
+
+ Looks up a localized string similar to Path must not be null or empty..
+
+
+
+
+ Looks up a localized string similar to Property '{0}' not found on type '{1}'..
+
+
+
+
+ Looks up a localized string similar to Property '{0}' on '{1}' is null..
+
+
+
+
+ Looks up a localized string similar to Unknown path segment type: {0}.
+
+
+
+
+ Looks up a localized string similar to Expected closing bracket after quoted key in path: {0}.
+
+
+
+
+ Looks up a localized string similar to Expected single quote after '[' in path: {0}.
+
+
+
+
+ Looks up a localized string similar to Unterminated quoted key in path: {0}.
+
+
+
+
+ Looks up a localized string similar to Node not found in MetricView graph..
+
+
+
+
+ Looks up a localized string similar to Left field '{0}' not found in built objects. Fields must be built before relationships..
+
+
+
+
+ Looks up a localized string similar to LeftFieldKey is not set.
+
+
+
+
+ Looks up a localized string similar to Required properties (cardinalities, join kind, or filter direction) are not set.
+
+
+
+
+ Looks up a localized string similar to Right field '{0}' not found in built objects. Fields must be built before relationships..
+
+
+
+
+ Looks up a localized string similar to RightFieldKey is not set.
+
+
+
+
+ Looks up a localized string similar to {0} '{1}' does not reference a valid field..
+
+
+
+
+ Looks up a localized string similar to {0} '{1}' is missing..
+
+
+
+
+ Looks up a localized string similar to {0} is not set..
+
+
+
+
+ Looks up a localized string similar to FilterDirection is not set and could not be inferred..
+
+
+
+
+ Looks up a localized string similar to Relationship must be between a fact and a dimension..
+
+
+
+
+ Looks up a localized string similar to Fact '{0}' already has a reference to dimension '{1}' with relationship on fields '{2}' and '{3}'.
+
+
+
+
+ Looks up a localized string similar to Dimension '{0}' not found in Root.Dimensions.
+
+
+
+
+ Looks up a localized string similar to Fact '{0}' not found in Root.Facts.
+
+
+
+
+ Looks up a localized string similar to Field '{0}' not found in {1}. Searched TechnicalName and Name..
+
+
+
+
+ Looks up a localized string similar to Relationship left field '{0}' does not belong to fact '{1}' or its source.
+
+
+
+
+ Looks up a localized string similar to Relationship right field '{0}' does not belong to dimension '{1}' or its source.
+
+
+
+
+ Looks up a localized string similar to Failed to initialize SemanticBridge service with an unexpected exception. See more details in the `Exception` property..
+
+
+
+
+ Looks up a localized string similar to Feature check service is not available. Maybe you are running it outside of Tabular Editor 3?.
+
+
+
+
+ Looks up a localized string similar to AggregationType '{0}' is not supported for DAX generation..
+
+
+
+
+ Looks up a localized string similar to SimpleMeasure '{0}' references column '{1}' which does not exist in table '{2}'..
+
+
+
+
+ Looks up a localized string similar to The table '{0}' referenced by measure '{1}' does not exist in the model.
+
+
+
+
+ Looks up a localized string similar to Column '{0}' not found in table '{1}' for relationship '{2}'..
+
+
+
+
+ Looks up a localized string similar to Relationship '{0}' has invalid cardinality configuration. Expected one side with Many and one side with One cardinality..
+
+
+
+
+ Looks up a localized string similar to Source field key cannot be null or empty..
+
+
+
+
+ Looks up a localized string similar to Could not build source system.
+
+
+
+
+ Looks up a localized string similar to Source fragment has no valid object or query defined..
+
+
+
+
+ Looks up a localized string similar to Service was successfully initialized and can be accessed through the `Success` record's `Service` property..
+
+
+
+
+ Looks up a localized string similar to Cannot use validation rule for {0} on node of {1}..
+
+
+
+
+ Extension methods for collection equality comparison that supports order-independent
+ equality checking for collections and dictionaries in AbstractModel records.
+
+
+ These methods implement set-based equality where order does not matter, supporting
+ proper equality semantics for records containing collections. Hash codes are generated
+ consistently to support use as dictionary keys and with MSTest assertions.
+
+
+
+
+ Compares two sequences for equality without regard to order.
+
+ The type of elements in the sequences
+ The first sequence to compare
+ The second sequence to compare
+
+ True if both sequences are null, or both contain the same elements
+ (each element in one sequence has a corresponding equal element in the other).
+ False otherwise.
+
+
+ This method treats sequences as sets - order does not matter, but duplicate
+ elements are significant. Uses the default equality comparer for type T.
+ Performance is O(n) for sequences of the same length.
+
+
+
+
+ Compares two dictionaries for equality, comparing both keys and values.
+
+ The type of dictionary keys
+ The type of dictionary values
+ The first dictionary to compare
+ The second dictionary to compare
+
+ True if both dictionaries are null, or both contain the same key-value pairs.
+ False otherwise.
+
+
+ Dictionaries are considered equal if they have the same set of keys and
+ each key maps to equal values in both dictionaries. Uses default equality
+ comparers for both keys and values.
+
+
+
+
+ Generates a hash code for a sequence that is order-independent.
+
+ The type of elements in the sequence
+ The sequence to generate a hash code for
+
+ A hash code that will be the same for sequences containing the same elements
+ regardless of order. Returns 0 for null collections.
+
+
+ The hash code is computed by combining individual element hash codes using
+ XOR, which is commutative and associative, ensuring order independence.
+ Duplicate elements contribute to the hash code multiple times.
+
+
+
+
+ Generates a hash code for a dictionary based on its key-value pairs.
+
+ The type of dictionary keys
+ The type of dictionary values
+ The dictionary to generate a hash code for
+
+ A hash code based on the dictionary's key-value pairs. Returns 0 for null dictionaries.
+
+
+ Hash codes are computed by combining individual key-value pair hash codes using XOR,
+ ensuring that dictionaries with the same entries produce the same hash code regardless
+ of internal ordering.
+
+
+
+ Custom -derived type for the JoinOnRegex method.
+
+
+ Cached, thread-safe singleton instance.
+
+
+ Initializes the instance.
+
+
+ Provides a factory for creating instances to be used by methods on .
+
+
+ Creates an instance of a used by methods on .
+
+
+ Provides the runner that contains the custom logic implementing the specified regular expression.
+
+
+ Scan the starting from base.runtextstart for the next match.
+ The text being scanned by the regular expression.
+
+
+ Search starting from base.runtextpos for the next location a match could possibly start.
+ The text being scanned by the regular expression.
+ true if a possible match was found; false if no more matches are possible.
+
+
+ Determine whether at base.runtextpos is a match for the regular expression.
+ The text being scanned by the regular expression.
+ true if the regular expression matches at the current position; otherwise, false.
+
+
+ Custom -derived type for the SqlAggRegex method.
+
+
+ Cached, thread-safe singleton instance.
+
+
+ Initializes the instance.
+
+
+ Provides a factory for creating instances to be used by methods on .
+
+
+ Creates an instance of a used by methods on .
+
+
+ Provides the runner that contains the custom logic implementing the specified regular expression.
+
+
+ Scan the starting from base.runtextstart for the next match.
+ The text being scanned by the regular expression.
+
+
+ Search starting from base.runtextpos for the next location a match could possibly start.
+ The text being scanned by the regular expression.
+ true if a possible match was found; false if no more matches are possible.
+
+
+ Determine whether at base.runtextpos is a match for the regular expression.
+ The text being scanned by the regular expression.
+ true if the regular expression matches at the current position; otherwise, false.
+
+
+ Custom -derived type for the DistinctCountRegex method.
+
+
+ Cached, thread-safe singleton instance.
+
+
+ Initializes the instance.
+
+
+ Provides a factory for creating instances to be used by methods on .
+
+
+ Creates an instance of a used by methods on .
+
+
+ Provides the runner that contains the custom logic implementing the specified regular expression.
+
+
+ Scan the starting from base.runtextstart for the next match.
+ The text being scanned by the regular expression.
+
+
+ Search starting from base.runtextpos for the next location a match could possibly start.
+ The text being scanned by the regular expression.
+ true if a possible match was found; false if no more matches are possible.
+
+
+ Determine whether at base.runtextpos is a match for the regular expression.
+ The text being scanned by the regular expression.
+ true if the regular expression matches at the current position; otherwise, false.
+
+
+ Custom -derived type for the SqlObjectPattern method.
+
+
+ Cached, thread-safe singleton instance.
+
+
+ Initializes the instance.
+
+
+ Provides a factory for creating instances to be used by methods on .
+
+
+ Creates an instance of a used by methods on .
+
+
+ Provides the runner that contains the custom logic implementing the specified regular expression.
+
+
+ Scan the starting from base.runtextstart for the next match.
+ The text being scanned by the regular expression.
+
+
+ Search starting from base.runtextpos for the next location a match could possibly start.
+ The text being scanned by the regular expression.
+ true if a possible match was found; false if no more matches are possible.
+
+
+ Determine whether at base.runtextpos is a match for the regular expression.
+ The text being scanned by the regular expression.
+ true if the regular expression matches at the current position; otherwise, false.
+
+
+ Helper methods used by generated -derived implementations.
+
+
+ Default timeout value set in , or if none was set.
+
+
+ Whether is non-infinite.
+
+
+ Determines whether the character is part of the [\w] set.
+
+
+ Supports searching for characters in or not in "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".
+
+
+
diff --git a/content/_apiSource/TOMWrapper.dll b/content/_apiSource/TOMWrapper.dll
index 9cc2ad66..c6a9d32a 100644
Binary files a/content/_apiSource/TOMWrapper.dll and b/content/_apiSource/TOMWrapper.dll differ
diff --git a/content/_apiSource/TabularEditor3.Shared.dll b/content/_apiSource/TabularEditor3.Shared.dll
index ae411f2e..2085564f 100644
Binary files a/content/_apiSource/TabularEditor3.Shared.dll and b/content/_apiSource/TabularEditor3.Shared.dll differ
diff --git a/content/api/index.md b/content/api/index.md
index c9939722..984cc41c 100644
--- a/content/api/index.md
+++ b/content/api/index.md
@@ -2,14 +2,14 @@
uid: api-index
title: Scripting API
author: Daniel Otykier
-updated: 2022-06-16
+updated: 2026-01-27
---
# Tabular Editor API
This is the API documentation for Tabular Editor's C# scripting capabilities.
-Specifically, the objects available for scripting are those found in the **TOMWrapper.dll** and **TabularEditor3.Shared.dll** libraries.
+Specifically, the objects available for scripting are those found in the **TOMWrapper.dll**, **TabularEditor3.Shared.dll**, and **SemanticBridge.dll** libraries.
## Getting started
@@ -28,4 +28,4 @@ var myMeasure = SelectMeasure();
Model.Tables.First().AddMeasure(myMeasure.Name + " copy", myMeasure.Expression);
```
-For more examples, see .
\ No newline at end of file
+For more examples, see .
diff --git a/content/assets/images/features/semantic-bridge/semantic-bridge-file-menu-import.png b/content/assets/images/features/semantic-bridge/semantic-bridge-file-menu-import.png
new file mode 100644
index 00000000..cc58adde
Binary files /dev/null and b/content/assets/images/features/semantic-bridge/semantic-bridge-file-menu-import.png differ
diff --git a/content/assets/images/features/semantic-bridge/semantic-bridge-import-diagnostics.png b/content/assets/images/features/semantic-bridge/semantic-bridge-import-diagnostics.png
new file mode 100644
index 00000000..372558fa
Binary files /dev/null and b/content/assets/images/features/semantic-bridge/semantic-bridge-import-diagnostics.png differ
diff --git a/content/assets/images/features/semantic-bridge/semantic-bridge-import-failed.png b/content/assets/images/features/semantic-bridge/semantic-bridge-import-failed.png
new file mode 100644
index 00000000..b03e05bd
Binary files /dev/null and b/content/assets/images/features/semantic-bridge/semantic-bridge-import-failed.png differ
diff --git a/content/assets/images/features/semantic-bridge/semantic-bridge-import-success-with-issues.png b/content/assets/images/features/semantic-bridge/semantic-bridge-import-success-with-issues.png
new file mode 100644
index 00000000..9b121f21
Binary files /dev/null and b/content/assets/images/features/semantic-bridge/semantic-bridge-import-success-with-issues.png differ
diff --git a/content/assets/images/features/semantic-bridge/semantic-bridge-import-success.png b/content/assets/images/features/semantic-bridge/semantic-bridge-import-success.png
new file mode 100644
index 00000000..fb4da865
Binary files /dev/null and b/content/assets/images/features/semantic-bridge/semantic-bridge-import-success.png differ
diff --git a/content/assets/images/features/semantic-bridge/semantic-bridge-metric-view-details.png b/content/assets/images/features/semantic-bridge/semantic-bridge-metric-view-details.png
new file mode 100644
index 00000000..860e0b75
Binary files /dev/null and b/content/assets/images/features/semantic-bridge/semantic-bridge-metric-view-details.png differ
diff --git a/content/assets/images/features/semantic-bridge/semantic-bridge-metric-view-validation.png b/content/assets/images/features/semantic-bridge/semantic-bridge-metric-view-validation.png
new file mode 100644
index 00000000..4e314581
Binary files /dev/null and b/content/assets/images/features/semantic-bridge/semantic-bridge-metric-view-validation.png differ
diff --git a/content/assets/images/features/semantic-bridge/semantic-bridge-metric-view-validation2.png b/content/assets/images/features/semantic-bridge/semantic-bridge-metric-view-validation2.png
new file mode 100644
index 00000000..5772cdf0
Binary files /dev/null and b/content/assets/images/features/semantic-bridge/semantic-bridge-metric-view-validation2.png differ
diff --git a/content/assets/images/features/semantic-bridge/semantic-bridge-metric-view-validation3.png b/content/assets/images/features/semantic-bridge/semantic-bridge-metric-view-validation3.png
new file mode 100644
index 00000000..64e15f96
Binary files /dev/null and b/content/assets/images/features/semantic-bridge/semantic-bridge-metric-view-validation3.png differ
diff --git a/content/features/index.md b/content/features/index.md
index 19dc5816..671314e3 100644
--- a/content/features/index.md
+++ b/content/features/index.md
@@ -55,4 +55,6 @@ This section contains documentation about Tabular Editor features.
### Command Line and Integration
- @command-line-options - CLI usage and automation
----
+### Semantic Bridge for cross-platform translations
+- @semantic-bridge - Semantic Bridge overview
+- @semantic-bridge-metric-view-validation - Validation rules system for Metric Views
diff --git a/content/features/semantic-bridge-metric-view-object-model.md b/content/features/semantic-bridge-metric-view-object-model.md
new file mode 100644
index 00000000..2a5e77c3
--- /dev/null
+++ b/content/features/semantic-bridge-metric-view-object-model.md
@@ -0,0 +1,297 @@
+---
+uid: semantic-bridge-metric-view-object-model
+title: Semantic Bridge Metric View Object Model
+author: Greg Baldini
+updated: 2025-01-23
+applies_to:
+ products:
+ - product: Tabular Editor 2
+ none: true
+ - product: Tabular Editor 3
+ since: 3.25.0
+ editions:
+ - edition: Desktop
+ none: true
+ - edition: Business
+ none: true
+ - edition: Enterprise
+ full: true
+---
+# Metric View Object Model
+
+
+
+> [!NOTE]
+> The Semantic Bridge as released in 3.25.0 is an MVP feature.
+> It has limitations as documented below, and the API and feature surface area are subject to change.
+> The object model here conspicuously lacks many affordances available in the TOMWrapper which users may be familiar with from C# scripts that manipulate a Tabular model.
+> As noted in the [limitations of the Semantic Bridge](xref:semantic-bridge#mvp-limitations), we currently only support Metric View v0.1 metadata.
+
+The Semantic Bridge includes an object model representing a Databricks Metric View.
+This allows you to work with Metric Views programmatically through C# scripts, similar to how you work with a Tabular model through the TOMWrapper.
+
+Other than the [import GUI](xref:semantic-bridge#interface), all access to and interaction with a Metric View is through C# scripts.
+All content in this document is referring to C# code that you will use in a [C# script](xref:csharp-scripts).
+
+## Loading and accessing the Metric View
+
+You can load a Metric view with [`SemanticBridge.MetricView.Load`](xref:TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_Load_System_String_) or [`SemanticBridge.MetricView.Deserialize`](xref:TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_Deserialize_System_String_).
+This stores the deserialized Metric View as [`SemanticBridge.MetricView.Model`](xref:TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_Model).
+This property returns a [`View`](xref:TabularEditor.SemanticBridge.Platforms.Databricks.MetricView.View) object, which is the root of the Metric View object graph.
+
+```csharp
+// Load a Metric View from disk
+SemanticBridge.MetricView.Load("C:/path/to/metricview.yaml");
+
+// Access the loaded View
+var view = SemanticBridge.MetricView.Model;
+Output($"Metric View version: {view.Version}\r\nSource: {view.Source}");
+```
+
+Similar to a Tabular model and dissimilar to most other objects you may be used to in a C# script, the Metric View is persistent across multiple script executions.
+This means that you can load a Metric View once, and reference it from subsequent script executions without re-loading it every time.
+There is only ever a single Metric View loaded, and it is available in all scripts as `SemanticBridge.MetricView.Model` as mentioned above.
+This behavior is similar to the Tabular model in C# scripts, which is always available simply as `Model`.
+
+## Domain objects
+
+The object model consists of four main types that correspond to the structure of a Metric View YAML file:
+We do not repeat the entire specification here, so we encourage you to reference the [Databricks Metric View documentation](https://learn.microsoft.com/en-us/azure/databricks/metric-views/).
+
+| API Reference | Description |
+|--------------------------------------------------------------------------------------------|------------------------------------------------------------|
+| [`View`](xref:TabularEditor.SemanticBridge.Platforms.Databricks.MetricView.View) | The root object representing the entire Metric View |
+| [`Join`](xref:TabularEditor.SemanticBridge.Platforms.Databricks.MetricView.Join) | A join definition connecting a dimension table to the fact |
+| [`Dimension`](xref:TabularEditor.SemanticBridge.Platforms.Databricks.MetricView.Dimension) | A field definition (column) in the Metric View |
+| [`Measure`](xref:TabularEditor.SemanticBridge.Platforms.Databricks.MetricView.Measure) | An aggregation definition representing business logic |
+
+> [!NOTE]
+> In the object model, we follow C# naming conventions, and so use `PascalCase` for all type and property names in the object model.
+> The Metric View YAML specification follows a naming convention of `snake_case`.
+> In general, we focus on the C# object model that is a component of the Semantic Bridge.
+> Other than changing the case, we do not change any naming convention from the YAML.
+
+### View
+
+The [`View`](xref:TabularEditor.SemanticBridge.Platforms.Databricks.MetricView.View) object is the root of the Metric View and contains:
+
+- `Version`: The Metric View specification version (e.g., "0.1")
+- `Source`: The source data for the fact table (e.g., "catalog.schema.table")
+- `Filter`: Optional SQL boolean expression that applies to all queries
+- `Joins`: Collection of join definitions
+- `Dimensions`: Collection of dimension (field) definitions
+- `Measures`: Collection of measure definitions
+
+```csharp
+var sb = new System.Text.StringBuilder();
+var view = SemanticBridge.MetricView.Model;
+
+sb.AppendLine($"Version: {view.Version}");
+sb.AppendLine($"Source: {view.Source}");
+sb.AppendLine($"Filter: {view.Filter ?? "(none)"}");
+sb.AppendLine($"Joins: {view.Joins?.Count ?? 0}");
+sb.AppendLine($"Dimensions: {view.Dimensions?.Count ?? 0}");
+sb.AppendLine($"Measures: {view.Measures?.Count ?? 0}");
+
+Output(sb.ToString());
+```
+
+#### `View` translation and validation
+
+The `View.Source` property becomes the fact table in the Tabular model, named `'Fact'`.
+If the `Source` is a 3-part table or view reference, it is translated to an M partition that accesses the SQL object by name.
+If the `Source` is not a 3-part table or view reference, it is translated to an M partition with an embedded SQL query, with the entirety of the `Source` string as the SQL query.
+The `Filter` property is ignored for purposes of translation.
+
+For purposes of evaluating validation rules, the `View` is checked first, then each collection is validated in order: `Joins`, then `Dimensions`, then `Measures`.
+Validation of the fact table, `Source` is done in a validation rule for the `View` object.
+
+### Join
+
+A [`Join`](xref:TabularEditor.SemanticBridge.Platforms.Databricks.MetricView.Join) represents a dimension table that is joined to the fact table:
+
+- `Name`: Name of the joined table (used as an alias)
+- `Source`: Source table or query for the join (e.g., "catalog.schema.dimension_table")
+- `On`: Optional SQL boolean expression for the join condition
+- `Using`: Optional list of column names for the join (alternative to `On`)
+- `Joins`: Child joins (for snowflake schemas)
+
+```csharp
+var sb = new System.Text.StringBuilder();
+var view = SemanticBridge.MetricView.Model;
+
+foreach (var join in view.Joins ?? [])
+{
+ sb.AppendLine($"Join: {join.Name}");
+ sb.AppendLine($" Source: {join.Source}");
+ if (!string.IsNullOrEmpty(join.On))
+ sb.AppendLine($" On: {join.On}");
+ if (join.Using != null && join.Using.Count > 0)
+ sb.AppendLine($" Using: {string.Join(", ", join.Using)}");
+}
+
+Output(sb.ToString());
+```
+
+#### `Join` translation and validation
+
+Nested `Join`s are not supported, i.e., only a strict star schema can be translated.
+Only `On` joins with a single-field equijoin are supported for translation.
+`Join`s each become a Tabular table, with an M partition defined according to the same rules as for the `View.Source` property.
+
+`Join`s are validated in the order they appear in the Metric View definition.
+
+### Dimension
+
+A [`Dimension`](xref:TabularEditor.SemanticBridge.Platforms.Databricks.MetricView.Dimension) represents a field (column) in the Metric View:
+
+- `Name`: The display name for the dimension
+- `Expr`: The SQL expression defining the dimension (either a column reference or a SQL expression)
+
+```csharp
+var sb = new System.Text.StringBuilder();
+var view = SemanticBridge.MetricView.Model;
+
+foreach (var dim in view.Dimensions ?? [])
+{
+ sb.AppendLine($"Dimension: {dim.Name}");
+ sb.AppendLine($" Expression: {dim.Expr}");
+}
+
+Output(sb.ToString());
+```
+
+#### `Dimension` translation and validation
+
+Each `Dimension` becomes a column in the Tabular model.
+If the `Expr` is an unqualified field reference, it is added to the fact table.
+If the `Expr` is a qualified reference (e.g., `table.field`), then it is added to the table created for the `Join` with the same name as the table-part of the qualified reference; if the table-part is `source`, it is added to the fact table.
+In both the qualified and unqualified field reference cases, the field is added as a [`TOMWrapper.DataColumn`](xref:TabularEditor.TOMWrapper.DataColumn).
+If the `Expr` is a SQL expression, then it is added as [`TOMWrapper.CalculatedColumn`](xref:TabularEditor.TOMWrapper.CalculatedColumn).
+When the `Expr` is a SQL expression, we attempt to extract all field references; if all field references share the same table-part, then we add it to the table created for that `Join`, otherwise we add it to the fact table.
+We do not translate SQL expressions for `Dimension.Expr` properties; the SQL expression is included as a comment in the DAX expression for the `CalculatedColumn`.
+It is up to the user to translate these expressions.
+We attempt to identify all field references in the SQL expression and add those to the Tabular model as `DataColumn`s if they do not already exist as a Metric View `Dimension`.
+
+Some examples:
+
+| `Expr` | Translated as type | Added to table | Note |
+|-------------------------------------------------------|--------------------|-----------------|------------------------------------------------------------------------------|
+| `field1` | `DataColumn` | `'Fact'` | unqualified field references are equivalent to those qualified with `source` |
+| `source.field2` | `DataColumn` | `'Fact'` | `source` is a reference to the `View.Source` property, aka the fact table |
+| `dimCustomer.key` | `DataColumn` | `'dimCustomer'` | there must be a `Join` whose `Name` property is `dimCustomer` |
+| `CONCAT(dimCustomer.FirstName, dimCustomer.LastName)` | `CalculatedColumn` | `'dimCustomer'` | all table-parts of the qualified name refer to the same name |
+| `CONCAT(dimGeo.Country, dimCustomer.Address)` | `CalculatedColumn` | `'Fact'` | there are multiple distinct table-parts |
+
+`Dimension`s are validated in the order they appear in the Metric View definition.
+
+### Measure
+
+A [`Measure`](xref:TabularEditor.SemanticBridge.Platforms.Databricks.MetricView.Measure) represents a named aggregation with business logic:
+
+- `Name`: The display name for the measure
+- `Expr`: The SQL aggregate expression defining the measure
+
+```csharp
+var sb = new System.Text.StringBuilder();
+var view = SemanticBridge.MetricView.Model;
+
+foreach (var measure in view.Measures ?? [])
+{
+ sb.AppendLine($"Measure: {measure.Name}");
+ sb.AppendLine($" Expression: {measure.Expr}");
+}
+
+Output(sb.ToString());
+```
+
+#### `Measure` translation and validation
+
+All measures are added to the fact table.
+Simple aggregations are translated into DAX expressions.
+A simple aggregation is a single aggregation of a single field (e.g. `SUM(table.field)`).
+Supported aggregations are sum, count, distinct count, max, min, and average.
+Other expressions are passed through as a comment in the DAX expression of the Tabular measure.
+We attempt to identify all field references in the SQL expression and add those to the Tabular model as `DataColumn`s if they do not already exist as a Metric View `Dimension`.
+
+> [!WARNING]
+> SQL and DAX are different languages with different semantics.
+> It is possible that an automatically translated measure does not express the same computation in both Databricks Metric Views and Tabular models.
+> It is up to the user to verify all code works as expected.
+
+`Measure`s are validated in the order they appear in the Metric View definition.
+
+## Using directives
+
+When working with the Metric View object model in C# scripts, you may need to add a using directive to avoid naming conflicts with similarly-named types in the Tabular Object Model.
+We recommend aliasing the namespace:
+
+```csharp
+// Alias to avoid conflicts with TOM types like Measure
+using MetricView = TabularEditor.SemanticBridge.Platforms.Databricks.MetricView;
+
+SemanticBridge.MetricView.Load("C:/path/to/metricview.yaml");
+var view = SemanticBridge.MetricView.Model;
+
+// Now you can reference types explicitly
+foreach (MetricView.Dimension dim in view.Dimensions ?? [])
+{
+ // ...
+}
+```
+
+## Complete example
+
+Here is a complete script that loads a Metric View and outputs a summary of its contents:
+
+```csharp
+using MetricView = TabularEditor.SemanticBridge.Platforms.Databricks.MetricView;
+
+// Load the Metric View
+SemanticBridge.MetricView.Load("C:/path/to/metricview.yaml");
+var sb = new System.Text.StringBuilder();
+var view = SemanticBridge.MetricView.Model;
+
+// sb.AppendLine summary
+sb.AppendLine("=== Metric View Summary ===");
+sb.AppendLine($"Version: {view.Version}");
+sb.AppendLine($"Source: {view.Source}");
+
+if (view.Joins != null && view.Joins.Count > 0)
+{
+ sb.AppendLine($"\nJoins ({view.Joins.Count}):");
+ foreach (var join in view.Joins)
+ {
+ sb.AppendLine($" - {join.Name} -> {join.Source}");
+ }
+}
+
+if (view.Dimensions != null && view.Dimensions.Count > 0)
+{
+ sb.AppendLine($"\nDimensions ({view.Dimensions.Count}):");
+ foreach (var dim in view.Dimensions)
+ {
+ sb.AppendLine($" - {dim.Name}: {dim.Expr}");
+ }
+}
+
+if (view.Measures != null && view.Measures.Count > 0)
+{
+ sb.AppendLine($"\nMeasures ({view.Measures.Count}):");
+ foreach (var measure in view.Measures)
+ {
+ sb.AppendLine($" - {measure.Name}: {measure.Expr}");
+ }
+}
+
+Output(sb.ToString());
+```
+
+## References
+
+- [`MetricView` namespace API documentation](xref:TabularEditor.SemanticBridge.Platforms.Databricks.MetricView)
+- @semantic-bridge-how-tos
+- [Databricks Metric View documentation](https://learn.microsoft.com/en-us/azure/databricks/metric-views/)
+- [Databricks Metric View YAML specification](https://learn.microsoft.com/en-us/azure/databricks/metric-views/data-modeling/syntax)
diff --git a/content/features/semantic-bridge-metric-view-validation.md b/content/features/semantic-bridge-metric-view-validation.md
new file mode 100644
index 00000000..a3897f9c
--- /dev/null
+++ b/content/features/semantic-bridge-metric-view-validation.md
@@ -0,0 +1,231 @@
+---
+uid: semantic-bridge-metric-view-validation
+title: Semantic Bridge Metric View Validation
+author: Greg Baldini
+updated: 2025-01-23
+applies_to:
+ products:
+ - product: Tabular Editor 2
+ none: true
+ - product: Tabular Editor 3
+ since: 3.25.0
+ editions:
+ - edition: Desktop
+ none: true
+ - edition: Business
+ none: true
+ - edition: Enterprise
+ full: true
+---
+# Semantic Bridge Validation
+
+
+
+There is a validation framework built into the Semantic Bridge to allow users to validate and define rules to check a Metric View before importing it to Tabular.
+This validation is shared at every stage of the translation pipeline, from first deserializing the Metric View, through to errors in translation to DAX and Tabular.
+
+> [!NOTE]
+> The Semantic Bridge is currently in its MVP phase, so interfaces may change as the feature matures.
+> For now, the only interface to validation is through C# scripts.
+
+## Validation process
+
+There are several phases of validation
+
+1. upon deserializing some YAML, to check that it represents a valid Metric View
+2. acting on the loaded Metric View
+3. upon translating the Metric View to Tabular
+
+The first and third are automatic and internal to the Semantic Bridge, but the second is where users can provide their own validation rules.
+
+Validation is a process of evaluating each of a set of validation rules against all objects in the Metric View.
+A validation rule is defined to apply to exactly one type of Metric View object, e.g. a `Join` or `Measure`.
+After a validation is complete, all diagnostics from rule violations are returned to the user for further action.
+
+## Anatomy of a validation rule
+
+Validation rules are all instances of [`IMetricViewValidationRule`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.Interfaces.IMetricViewValidationRule.html).
+Rather than dig into that interface, it is easier to understand and work with validation rules with the helper methods:
+
+- [`MakeValidationRuleForDimension`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService.html#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_MakeValidationRuleForDimension_System_String_System_String_System_String_System_Func_TabularEditor_SemanticBridge_Platforms_Databricks_MetricView_Dimension_System_Boolean__)
+- [`MakeValidationRuleForJoin`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService.html#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_MakeValidationRuleForJoin_System_String_System_String_System_String_System_Func_TabularEditor_SemanticBridge_Platforms_Databricks_MetricView_Join_System_Boolean__)
+- [`MakeValidationRuleForMeasure`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService.html#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_MakeValidationRuleForMeasure_System_String_System_String_System_String_System_Func_TabularEditor_SemanticBridge_Platforms_Databricks_MetricView_Measure_System_Boolean__)
+- [`MakeValidationRuleForView`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService.html#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_MakeValidationRuleForView_System_String_System_String_System_String_System_Func_TabularEditor_SemanticBridge_Platforms_Databricks_MetricView_View_System_Boolean__)
+- [`MakeValidationRule`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService.html#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_MakeValidationRule__1_System_String_System_String_System_Func___0_TabularEditor_SemanticBridge_Platforms_Databricks_Validation_IReadOnlyValidationContext_System_Collections_Generic_IEnumerable_TabularEditor_SemanticBridge_Orchestration_DiagnosticMessage___)
+
+The first four are all special purpose to make a rule for the object type in their name.
+They offer a simplified interface where you provide:
+
+- `name`: a short, unique name to identify the rule
+- `category`: useful for grouping similar rules together, but ultimately completely optional
+- `message`: the message that will be shown in the diagnostic message when this rule is violated
+- `isInvalid`: a function that will take the Metric View object as an argument, and will return `true` if that object is invalid
+
+The name and category are intended to make it easier to deal with collections of rules, as you will do in C# scripts that utilize custom rules.
+
+This is easier to understand with an example:
+
+```csharp
+// create a rule to check for underscores in dimension names
+var myRule = SemanticBridge.MetricView.MakeValidationRuleForDimension(
+ "no_underscores",
+ "naming",
+ "Do not include underscores in dimension names. Use user-friendly names with spaces.",
+ (dimension) => dimension.Name.Contains('_')
+ );
+```
+
+This makes a rule that will apply to all [Metric View `Dimension`s](/api/TabularEditor.SemanticBridge.Platforms.Databricks.MetricView.Dimension.html).
+The rule is named (ironically) "no_underscores".
+It has a category of "naming", to indicate that it has to do with how we name things.
+The message you will see when the rule is violated is, "Do not include underscores in dimension names. Use user-friendly names with spaces."
+The last argument defines a function that will be called for each Metric View dimension in the model; its body is a boolean expression that returns `true` for a Metric View dimension with an underscore in its `Name` property.
+
+Here's a full script that defines a Metric View inline, and then deserializes and validates it, showing how this rule is used.
+
+```csharp
+// create a new simple Metric View
+SemanticBridge.MetricView.Deserialize("""
+ version: 0.1
+ source: database.schema.table
+ dimensions:
+ - name: first_field
+ expr: source.first_field
+ - name: another field with no underscores
+ expr: source.another_field_with_no_underscores
+ """);
+
+// create a new validation rule
+var myRule = SemanticBridge.MetricView.MakeValidationRuleForDimension(
+ "no_underscores",
+ "naming",
+ "Do not include underscores in dimension names. Use user-friendly names with spaces.",
+ (dimension) => dimension.Name.Contains('_')
+ );
+
+// run validation with the rule defined above and output the diagnostic messages
+Output(SemanticBridge.MetricView.Validate([myRule]));
+```
+
+You can see that one of the fields defined as a Metric View dimension has an underscore in its name.
+When you run the script, you can see one diagnostic message after validating with the rule we defined.
+You can see the details that are provided in the diagnostic message:
+
+- Code, Context: not used when you use one of these helper methods to make your rule
+- Message: the message you defined in the rule
+- Path: a representation of where you find that object in the Metric View
+- Severity: set to Error by default with these helpers
+
+
+
+If you want more control over the diagnostic message and more flexibility in the function for your validation, you can use `MakeValidationRule` mentioned above to make a contextual validation rule.
+
+```csharp
+// necessary to use the Metric View object model
+// aliasing to avoid conflicts with same-named TOM objects
+using MetricView = TabularEditor.SemanticBridge.Platforms.Databricks.MetricView;
+
+// create a new simple Metric View
+SemanticBridge.MetricView.Deserialize("""
+ version: 0.1
+ source: database.schema.table
+ dimensions:
+ - name: same_field
+ expr: source.same_field
+ - name: same_field
+ expr: source.same_field
+ """);
+
+// create a new validation rule
+var myRule = SemanticBridge.MetricView.MakeValidationRule(
+ "no_duplicate_dimensions",
+ "naming",
+ (dimension, context) =>
+ context.DimensionNames.Contains(dimension.Name)
+ ? [context.MakeError($"{dimension.Name} appears more than once as a dimension")]
+ : []
+ );
+
+// run validation with the rule defined above and output the diagnostic messages
+Output(SemanticBridge.MetricView.Validate([myRule]));
+```
+
+This helper method requires you to pass the object type as a type parameter, and the validation function now is a two-parameter function, defined with the signature `(objectType, context)`.
+The first parameter is the Metric View object that the rule is evaluated for.
+The second parameter is an [`IReadOnlyValidationContext`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.Validation.IReadOnlyValidationContext.html).
+This context object holds collections with the names of already-checked objects; this makes it useful to check for duplicate names.
+The context object also has a helper method to make a new diagnostic message; the benefit here is that your message doesn't have to be a hard-coded string, but can include properties of the object you are checking.
+You can see in this example that we include the duplicated Metric View dimension name in the message.
+
+
+
+## Validation rule best practices
+
+It is a good idea to make many simple rules, rather than fewer, more complex rules.
+The validation process is very light-weight, so there are not performance concerns from a proliferation of rules.
+For example, if you want to make sure that Metric View dimension names are not `camelCased`, not `kebab-cased` and not `snake_cased`, it is better to make three separate rules, rather than trying to check for each of those conditions in a single rule.
+This allows each rule to be simple, and for the messages to be very specific, and therefore more easily actionable.
+
+In general, once you have a rule that catches a specific issue, it is better to leave that alone, rather than editing it.
+If you find that the rule is missing some condition you'd like to catch, just add a new, small, simple rule to catch that new condition.
+
+You can save many different rules in a C# script for re-use with different Metric Views.
+Because [a loaded Metric View is accessible in multiple scripts](xref:semantic-bridge-metric-view-object-model#loading-and-accessing-the-metric-view) you can save C# scripts that only define rules and then call `SemanticBridge.MetricView.Validate`, and re-use those validation scripts easily.
+See the image below, where the script on the left, "load-mv.csx" has already been run, to load a Metric View to Tabular Editor.
+Then, the second script, on the right, "run-rules.csx", is run second to validate.
+This second script could be one that you keep around for all of your Metric Views.
+
+
+
+
+The scripts are copied below for convenience, but are just rearrangements of scripts we saw above.
+
+**"load-mv.csx"**
+
+```csharp
+// create a new simple Metric View
+SemanticBridge.MetricView.Deserialize("""
+ version: 0.1
+ source: database.schema.table
+ dimensions:
+ - name: same_field
+ expr: source.same_field
+ - name: same_field
+ expr: source.same_field
+ """);
+```
+
+**"run-rules.csx"**
+
+```csharp
+// necessary to use the Metric View object model
+// aliasing to avoid conflicts with same-named TOM objects
+using MetricView = TabularEditor.SemanticBridge.Platforms.Databricks.MetricView;
+
+//create a simple validation rule
+var simpleRule = SemanticBridge.MetricView.MakeValidationRuleForDimension(
+ "no_underscores",
+ "naming",
+ "Do not include underscores in dimension names. Use user-friendly names with spaces.",
+ (dimension) => dimension.Name.Contains('_')
+ );
+
+// create a contextual validation rule
+var contextualRule = SemanticBridge.MetricView.MakeValidationRule(
+ "no_duplicate_dimensions",
+ "naming",
+ (dimension, context) =>
+ context.DimensionNames.Contains(dimension.Name)
+ ? [context.MakeError($"{dimension.Name} appears more than once as a dimension")]
+ : []
+ );
+
+// run validation with the rules defined above and output the diagnostic messages
+Output(SemanticBridge.MetricView.Validate([simpleRule, contextualRule]));
+```
+
+## References
+
+- @semantic-bridge-how-tos
diff --git a/content/features/semantic-bridge.md b/content/features/semantic-bridge.md
new file mode 100644
index 00000000..e74bb2af
--- /dev/null
+++ b/content/features/semantic-bridge.md
@@ -0,0 +1,221 @@
+---
+uid: semantic-bridge
+title: Semantic Bridge
+author: Greg Baldini
+updated: 2025-01-23
+applies_to:
+ products:
+ - product: Tabular Editor 2
+ none: true
+ - product: Tabular Editor 3
+ since: 3.25.0
+ editions:
+ - edition: Desktop
+ none: true
+ - edition: Business
+ none: true
+ - edition: Enterprise
+ full: true
+---
+# Semantic Bridge
+
+
+
+> [!NOTE]
+> The Semantic Bridge as released in 3.25.0 is an MVP feature. It has limitations as documented below, and the API and feature surface area are subject to change.
+
+The Semantic Bridge is a semantic model compiler, with the capability to translate the structure and expressions of a semantic model from one platform to another.
+This allows you to reuse business logic on multiple data platforms, supporting end users and meeting them where they consume the data.
+It also allows for migrations from one platform to another.
+
+## Interface
+
+### Import Metric View YAML
+
+The Semantic Bridge is available through **File > Open > Import from Metric View YAML**.
+This will launch a dialogue to guide you through importing a Metric View into the current Tabular model, adding tables, columns, measures, and relationships based on the structure of the Metric View.
+You must have a Tabular model open in Tabular Editor.
+This can be a new, empty model or an existing model you want to enhance with the objects from the Metric View.
+The menu button will not be enabled until you open or create a new Tabular model.
+
+
+
+### Enter Databricks connection details
+
+You need to provide three details in this dialogue:
+
+1. The path to the Metric View YAML file.
+ You can paste the path to the file or use the **Browse** button to find it.
+2. The Databricks hostname.
+ This is to provide the correct argument in the M partition generated for the Databricks source system.
+3. The HTTP path for Databricks.
+ This is to provide the correct argument in the M partition generated for the Databricks source system.
+
+If you are just testing the translation feature, you can provide placeholder values for the last two items, but you will need to fix the M partition definitions before you can refresh data into your Tabular model.
+
+After filling out the details, click **OK**.
+The Semantic Bridge will translate your Metric View to Tabular and create all the TOM objects for you.
+
+
+
+### Result
+
+There are three possible results:
+
+1. Success: everything in the Metric View was translated to Tabular and you have a Tabular model ready to use.
+2. Success, but with some issues: the Semantic Bridge was not able to translate every object in the Metric View; there are diagnostic messages you can view to understand what needs your attention.
+3. Failure: the Semantic Bridge could not translate the Metric View
+
+After either success type, you can use undo/redo functionality like normal in Tabular Editor to undo or instantly replay the import.
+
+**Success**
+
+
+
+**Success with issues**
+
+
+
+If you click on **View Diagnostics**, you can see a list of messages describing the issues in translation.
+These diagnostics are available for review later by outputting them from a C# script:
+
+```csharp
+// Show all diagnostic messages from the last attempted import of a Metric View
+SemanticBridge.MetricView.ImportDiagnostics.Output();
+```
+
+
+
+**Failure**
+
+
+
+Viewing diagnostics for a failure is the same as for success with issues.
+
+## Translation process
+
+Translating a Metric View to a Tabular model happens in several steps:
+
+1. Read the YAML from disk
+2. Deserialize the YAML
+3. Validate that the deserialized YAML represents a valid Metric View
+4. If it is a valid Metric View, store it as a the currently loaded Metric View, similar to how there is a loaded Tabular model that you interact with.
+ If it is not a valid Metric View, the process stops here and messages are available.
+4. Analyze the Metric View and attempt to transform it to an intermediate representation
+5. Attempt to transform the intermediate representation to a Tabular model
+
+The import GUI described above handles all of this for you, but you can also use C# scripts to customize different steps of the process and operate on the Metric View programatically, similarly to how you are used to doing with a Tabular model.
+Specifically, you can
+
+- load a Metric View from disk with [`SemanticBridge.MetricView.Load`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService.html#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_Load_System_String_): loading makes it available in C# scripts as [`SemanticBridge.MetricView.Model`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService.html#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_Model), but does not import the structure into the Tabular model
+- deserialize a Metric view from a string with [`SemanticBridge.MetricView.Deserialize`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService.html#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_Deserialize_System_String_): similar to loading, the model is available as [`SemanticBridge.MetricView.Model`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService.html#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_Model), but is not imported
+- save a Metric View to disk with [`SemanticBridge.MetricView.Save`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService.html#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_Save_System_String_)
+- serialize a Metric View to a string with [`SemanticBridge.MetricView.Serialize`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService.html#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_Serialize).
+- validate a Metric View using a system that is similar to the [Best Practice Analyzer](xref:best-practice-analyzer) with [`SemanticBridge.MetricView.Validate`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService.html#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_Validate)
+ - you can create your own custom validation rules with [`SemanticBridge.MetricView.MakeValidationRule`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService.html#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_MakeValidationRule__1_System_String_System_String_System_Func___0_TabularEditor_SemanticBridge_Platforms_Databricks_Validation_IReadOnlyValidationContext_System_Collections_Generic_IEnumerable_TabularEditor_SemanticBridge_Orchestration_DiagnosticMessage___) and its simpler versions
+- import a Metric View to Tabular with [`SemanticBridge.MetricView.ImportToTabularFromFile`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService.html#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_ImportToTabularFromFile_System_String_TabularEditor_TOMWrapper_Model_System_String_System_String_System_Collections_Generic_List_TabularEditor_SemanticBridge_Orchestration_DiagnosticMessage___System_Boolean_), which does the exact same as the GUI shown above, or [`SemanticBridge.MetricView.ImportToTabular`](/api/TabularEditor.SemanticBridge.Platforms.Databricks.DatabricksMetricViewService.html#TabularEditor_SemanticBridge_Platforms_Databricks_DatabricksMetricViewService_ImportToTabular_TabularEditor_TOMWrapper_Model_System_String_System_String_System_Collections_Generic_List_TabularEditor_SemanticBridge_Orchestration_DiagnosticMessage___System_Boolean_), which is similar, but operates on the currently loaded Metric View, rather than reading one from disk.
+
+
+## MVP Limitations
+
+### Supported platforms
+
+In the MVP release, we support translations from a Databricks Metric View to a Tabular model.
+Specifically we support the following surface area of a Databricks Metric View:
+
+- v0.1 Metric View properties:
+ - supported:
+ - `source`: the source of the fact table
+ - `joins`: collection of tables left-joined to the fact
+ - `dimensions`: flat collection of fields from any table, either the single fact or any of the many joins
+ - `measures`: flat collection of named aggregations representing business logic
+ - unsupported:
+ - `filter`: a SQL filter expression for the Metric View
+
+All v1.1 metadata is not supported in the MVP.
+Any v1.1 metadata is silently ignored upon deserialization of a Metric View, so it will not be visible in a C# script and it will not affect the translation to Tabular in any way.
+
+> [!WARNING]
+> Because the v1.1 metadata is silently ignored, a Metric View that you deserialize and then serialize will be missing this metadata.
+> Be careful not to overwrite a v1.1 source YAML file from a C# script, as that will remove all v1.1 metadata.
+
+### Limitations on translation from SQL
+
+Metric Views provide a structured layer on top of SQL expressions, and so part of translating a Metric View is translating SQL to DAX and M in the Tabular model.
+
+- Metric View `joins` with nested `joins` are not supported.
+ In other words, only strict star schemas are supported for translation; snowflake models are not supported
+- Metric View `joins` with `using` join criteria are not supported; only equijoins on a single key field using the `on` property are supported.
+- Metric View `dimensions` with SQL expressions are not translated to M or DAX; they are presented as Tabular model calculated columns with their SQL expression commented out
+- Metric View `measures` with non-basic aggregations are not translated to DAX; they are presented as a Tabular model measure with their SQL expression commented out
+ - The only aggregations supported are sum, max, min, average, count, and distinct count.
+ - SQL comments in a basic aggregation are not preserved in DAX
+
+> [!WARNING]
+> Note that SQL and DAX are different languages with different semantics.
+> We can make no guarantee that a translated measure will behave identically between the Metric View SQL and the Tabular DAX we generate.
+> Basic aggregates defined on fact table fields should behave the same, whereas aggregates defined on fields in dimension tables are more likely to produce undesired results.
+
+### Connectivity
+
+The MVP does not connect to any platforms besides Tabular, but works entirely with local files.
+You must create your Metric View YAML on your own and then put it where Tabular Editor can see it.
+
+### C# API
+
+The C# interface has been built to optimize for the automatic translation workflow.
+As such, there are limited affordances for interacting with the currently loaded Metric View, and certain types of operations are clunky.
+
+## Appendix on nomenclature
+
+It can be confusing to discuss things when talking about the Semantic Bridge, as there are many words that have both generic and specific meanings, depending what level of abstraction we are talking about and which platform we are discussing.
+For example, the term "semantic model" is both generic, referring to the concept of a collection of data and business logic in some form suitable for supporting business reporting and analytical needs, and also the name Microsoft has adopted for referring to their specific implementation of this generic concept in Power BI and Fabric.
+Thus, a semantic model might generically refer to a Databricks Metric View, an OLAP / Multidimensional Cube, a Power BI semantic model, or a model hosted in another platform's semantic layer.
+Similarly, "dimension" refers to a concept in dimensional modeling, but it is also the name of a specific type of object in a Metric View.
+Because of this, we have adopted the following definitions and standards in our documentation to maintain clarity and sanity.
+
+> [!NOTE]
+> These conventions are only intended for documentation about the Semantic Bridge feature.
+
+### Definitions
+
+- *Semantic model*: when used on its own always refers to the generic concept of a collection of data, metadata, and business logic to support reporting and analytics.
+ If and only if it is immediately preceded by "Fabric" or "Power BI", then it is referring to that artifact type in that platform, specifically a Tabular model that is saved as TMDL or BIM and using M and DAX; we tend to prefer to use the term Tabular model to refer to the Power BI / Fabric semantic model to avoid this confusion where possible, because the Tabular model is shared across Power BI / Fabric as well as Analysis Serviced Tabular.
+- *Platform*: a technology solution that has a semantic layer, on which a generic semantic model is hosted.
+ Databricks Metric Views represent a platform; Fabric / Power BI represent a platform; Analysis Services Tabular is a platform; Analysis Services Multidimensional is a platform which we have no support for in the Semantic Bridge today.
+- *Serialization format*: a way to represent a semantic model on disk in a textual format.
+ TMDL and TMSL (.bim) are two serialization formats for a Power BI semantic model; YAML is the serialization format for a Databricks Metric View.
+- *Object model*: an in-memory representation of a semantic model that we operate on in Tabular Editor via the Semantic Bridge either through GUI actions or C# scripts.
+ The TOM or Tabular Object Model should be familiar to existing users of Tabular Editor.
+ We have also created an object model for Databricks Metric Views, to allow manipulation of these in our tool.
+
+### General dimensional modeling terminology
+
+There are many terms that exist generally in discussion of a dimensional model or semantic model and also in a specific platform's object model and serialization formats.
+For example, the term "measure" refers generically to a quantitative value that is aggregated in a dimensional model to represent a business metric of interest, but it also refers to a specific kind of object in both Databricks Metric Views and Tabular models; in a Metric View, a measure is a named SQL expression that defines an aggregation in the Metric View, and in a Tabular model a measure is a named DAX expression tat defines an aggregation in the Tabular model.
+It is impossible to discuss the work of the Semantic Bridge without talking about multiple meanings of such words at once.
+For example, we talk about translating a Metric View measure to a Tabular measure.
+As such, **we always refer to an object in a specific platform's model by saying the platform and the object, e.g. "Metric View measure" or "Tabular measure"**.
+If the term is ever used without being accompanied by a platform's name, then we are discussing the idea generically.
+
+### Common terms across Metric Views and Tabular models
+
+For those of our users who may be unfamiliar with either Metric Views or Tabular models, we provide an incomplete rosetta stone below.
+We refer to the names of Metric View objects based on their representation in YAML, and Tabular based on the name of the type of object in TMDL/TMSL.
+
+| General term | Name in Tabular | Name in Metric View | Description | Note |
+|----------------------|-----------------|-----------------------|------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| fact | table | source | A table holding foreign keys to dimensions and quantitative values to be aggregated | a Metric View has a single fact which is unnamed and captured as the root-level `source` attribute in YAML. Tabular models do not differentiate between types of tables: whether a table is a fact can only be inferred |
+| dimension | table | join | A table holding descriptive attributes and one primary key to which the fact is related | Tabular models do not differentiate, so the role of "dimension" is inferred only, as with a fact. |
+| partition | parition | source (join only) | An object for data management, holding a subset of data in a table | Tabular model tables can have many partitions and must have at least one. The Metric View fact, as mentioned above is defined purely as a source, but Metric View joins also have a `source` property, which acts roughly like a partition |
+| field | column | dimension | A column in a table | |
+| measure | measure | measure | A quantitative value that is aggregated according to business logic in the model | Measures in a Tabular model are written in DAX, and in a Metric View in SQL |
+| join or relationship | relationship | join.on or join.using | A correspondence between key fields in two tables, a foreign key in one and primary key in the other | Relationships are explicit objects in a Tabular model, and implicitly defined as a property of the `join` object in Metric View YAML |
+
+## Additional Resources
+
+- [Databricks Metric View documentation](https://learn.microsoft.com/en-us/azure/databricks/metric-views/)
+- [Databricks Metric View YAML reference](https://learn.microsoft.com/en-us/azure/databricks/metric-views/data-modeling/syntax)
+- @semantic-bridge-how-tos
diff --git a/content/features/toc.md b/content/features/toc.md
index 964fd566..edd7ea86 100644
--- a/content/features/toc.md
+++ b/content/features/toc.md
@@ -9,29 +9,29 @@
## @diagram-view
# DAX Features
-## @dax-debugger
-## @dax-editor
-## @dax-query
-## @dax-scripts
-## @code-actions
+## @dax-debugger
+## @dax-editor
+## @dax-query
+## @dax-scripts
+## @code-actions
## @dax-package-manager
# Scripting and Automation
-## @csharp-scripts
+## @csharp-scripts
## C# Script Library
### @csharp-script-library
### @script-library-beginner
### @script-library-advanced
-## @script-helper-methods
-## @useful-script-snippets
+## @script-helper-methods
+## @useful-script-snippets
## @macros
# Model Analysis and Quality
-## @best-practice-analyzer
+## @best-practice-analyzer
### @built-in-bpa-rules
-### @using-bpa
-### @using-bpa-sample-rules-expressions
-## @dax-optimizer-integration
+### @using-bpa
+### @using-bpa-sample-rules-expressions
+## @dax-optimizer-integration
# Data Exploration and Connectivity
## @import-tables
@@ -51,13 +51,18 @@
# Model Deployment and Management
## [Model deployment](deployment.md)
-## @workspace-databases
-## @workspace-mode
-## @tmdl
-## @save-to-folder
+## @workspace-databases
+## @workspace-mode
+## @tmdl
+## @save-to-folder
## @save-with-supporting-files
## @advanced-refresh
## @refresh-overrides
# Command Line and Integration
-## @command-line-options
\ No newline at end of file
+## @command-line-options
+
+# Semantic Bridge for cross-platform translations
+## @semantic-bridge
+## @semantic-bridge-metric-view-object-model
+## @semantic-bridge-metric-view-validation
diff --git a/content/how-tos/includes/sample-metricview-deserialize.md b/content/how-tos/includes/sample-metricview-deserialize.md
new file mode 100644
index 00000000..2aeb794a
--- /dev/null
+++ b/content/how-tos/includes/sample-metricview-deserialize.md
@@ -0,0 +1,43 @@
+## Deserialize Metric View for these code samples
+
+This how-to uses a sample e-commerce Metric View representing sales data with three dimension tables (product, customer, date) joined to a fact table (orders).
+Run the snippet below first, if you'd like to follow along with the code in the rest of this how-to
+
+```csharp
+SemanticBridge.MetricView.Deserialize("""
+ version: 0.1
+ source: sales.fact.orders
+ joins:
+ - name: product
+ source: sales.dim.product
+ on: source.product_id = product.product_id
+ - name: customer
+ source: sales.dim.customer
+ on: source.customer_id = customer.customer_id
+ - name: date
+ source: sales.dim.date
+ on: source.order_date = date.date_key
+ dimensions:
+ - name: product_name
+ expr: product.product_name
+ - name: product_category
+ expr: product.category
+ - name: customer_segment
+ expr: customer.segment
+ - name: order_date
+ expr: date.full_date
+ - name: order_year
+ expr: date.year
+ - name: order_month
+ expr: date.month_name
+ measures:
+ - name: total_revenue
+ expr: SUM(revenue)
+ - name: order_count
+ expr: COUNT(order_id)
+ - name: avg_order_value
+ expr: AVG(revenue)
+ - name: unique_customers
+ expr: COUNT(DISTINCT customer_id)
+ """);
+```
diff --git a/content/how-tos/includes/sample-metricview.md b/content/how-tos/includes/sample-metricview.md
new file mode 100644
index 00000000..ae47b012
--- /dev/null
+++ b/content/how-tos/includes/sample-metricview.md
@@ -0,0 +1,38 @@
+This how-to uses a sample e-commerce Metric View representing sales data with three dimension tables (product, customer, date) joined to a fact table (orders).
+
+```yaml
+version: 0.1
+source: sales.fact.orders
+joins:
+ - name: product
+ source: sales.dim.product
+ on: source.product_id = product.product_id
+ - name: customer
+ source: sales.dim.customer
+ on: source.customer_id = customer.customer_id
+ - name: date
+ source: sales.dim.date
+ on: source.order_date = date.date_key
+dimensions:
+ - name: product_name
+ expr: product.product_name
+ - name: product_category
+ expr: product.category
+ - name: customer_segment
+ expr: customer.segment
+ - name: order_date
+ expr: date.full_date
+ - name: order_year
+ expr: date.year
+ - name: order_month
+ expr: date.month_name
+measures:
+ - name: total_revenue
+ expr: SUM(revenue)
+ - name: order_count
+ expr: COUNT(order_id)
+ - name: avg_order_value
+ expr: AVG(revenue)
+ - name: unique_customers
+ expr: COUNT(DISTINCT customer_id)
+```
diff --git a/content/how-tos/includes/sample-metricview.yaml b/content/how-tos/includes/sample-metricview.yaml
new file mode 100644
index 00000000..df457333
--- /dev/null
+++ b/content/how-tos/includes/sample-metricview.yaml
@@ -0,0 +1,34 @@
+version: 0.1
+source: sales.fact.orders
+joins:
+ - name: product
+ source: sales.dim.product
+ on: source.product_id = product.product_id
+ - name: customer
+ source: sales.dim.customer
+ on: source.customer_id = customer.customer_id
+ - name: date
+ source: sales.dim.date
+ on: source.order_date = date.date_key
+dimensions:
+ - name: product_name
+ expr: product.product_name
+ - name: product_category
+ expr: product.category
+ - name: customer_segment
+ expr: customer.segment
+ - name: order_date
+ expr: date.full_date
+ - name: order_year
+ expr: date.year
+ - name: order_month
+ expr: date.month_name
+measures:
+ - name: total_revenue
+ expr: SUM(revenue)
+ - name: order_count
+ expr: COUNT(order_id)
+ - name: avg_order_value
+ expr: AVG(revenue)
+ - name: unique_customers
+ expr: COUNT(DISTINCT customer_id)
diff --git a/content/how-tos/index.md b/content/how-tos/index.md
index d4250781..3d08953d 100644
--- a/content/how-tos/index.md
+++ b/content/how-tos/index.md
@@ -42,6 +42,17 @@ This section contains step-by-step guides for accomplishing specific tasks with
- [Metadata Backup](metadata-backup.md) - Backup and restore model metadata
- [Undo and Redo](undo-redo.md) - Using undo/redo functionality
+### Semantic Bridge
+- @semantic-bridge-load-inspect - Load a Metric View and explore its structure
+- @semantic-bridge-import - Import a Metric View to Tabular and view diagnostics
+- @semantic-bridge-validate-default - Validate with built-in rules
+- @semantic-bridge-validate-simple-rules - Create predicate-based validation rules
+- @semantic-bridge-validate-contextual-rules - Create rules with cross-object checks
+- @semantic-bridge-add-object - Add a new object to a Metric View
+- @semantic-bridge-remove-object - Remove objects from a Metric View
+- @semantic-bridge-rename-objects - Rename objects using copy-modify pattern
+- @semantic-bridge-serialize - Serialize a Metric View back to YAML
+
---
-*Navigate using the table of contents to find guides for specific tasks.*
\ No newline at end of file
+*Navigate using the table of contents to find guides for specific tasks.*
diff --git a/content/how-tos/semantic-bridge-add-object.md b/content/how-tos/semantic-bridge-add-object.md
new file mode 100644
index 00000000..918d69d5
--- /dev/null
+++ b/content/how-tos/semantic-bridge-add-object.md
@@ -0,0 +1,74 @@
+---
+uid: semantic-bridge-add-object
+title: Add an Object to a Metric View
+author: Greg Baldini
+updated: 2025-01-27
+applies_to:
+ products:
+ - product: Tabular Editor 2
+ none: true
+ - product: Tabular Editor 3
+ since: 3.25.0
+ editions:
+ - edition: Desktop
+ none: true
+ - edition: Business
+ none: true
+ - edition: Enterprise
+ full: true
+---
+# Add an object to a Metric View
+
+This how-to demonstrates how to add a new Metric View dimension (field) to a loaded Metric View.
+Similar patterns apply to all Metric View collections.
+
+[!INCLUDE [deserialize](includes/sample-metricview-deserialize.md)]
+
+## Create a new Metric View Dimension object
+
+Use the Metric View `Dimension` constructor to create a new Metric View dimension:
+
+```csharp
+using MetricView = TabularEditor.SemanticBridge.Platforms.Databricks.MetricView;
+
+var newDimension = new MetricView.Dimension
+{
+ Name = "customer_city",
+ Expr = "customer.city"
+};
+```
+
+## Add to the Metric View
+
+The Metric View `Dimensions` property is an `IList`, so you can use `Add()`:
+
+```csharp
+using MetricView = TabularEditor.SemanticBridge.Platforms.Databricks.MetricView;
+
+var sb = new System.Text.StringBuilder();
+sb.AppendLine($"Dimensions before adding: {SemanticBridge.MetricView.Model.Dimensions.Count}");
+
+var newDimension = new MetricView.Dimension
+{
+ Name = "customer_city",
+ Expr = "customer.city"
+};
+
+SemanticBridge.MetricView.Model.Dimensions.Add(newDimension);
+
+sb.AppendLine($"Dimensions after adding: {SemanticBridge.MetricView.Model.Dimensions.Count}");
+Output(sb.ToString());
+```
+
+**Output**
+
+```
+Dimensions before adding: 8
+Dimensions after adding: 9
+```
+
+## See also
+
+- @semantic-bridge-remove-object
+- @semantic-bridge-rename-objects
+- @semantic-bridge-serialize
diff --git a/content/how-tos/semantic-bridge-how-tos.md b/content/how-tos/semantic-bridge-how-tos.md
new file mode 100644
index 00000000..d6e0288e
--- /dev/null
+++ b/content/how-tos/semantic-bridge-how-tos.md
@@ -0,0 +1,43 @@
+---
+uid: semantic-bridge-how-tos
+title: Semantic Bridge How-Tos
+author: Greg Baldini
+updated: 2025-01-27
+applies_to:
+ products:
+ - product: Tabular Editor 2
+ none: true
+ - product: Tabular Editor 3
+ since: 3.25.0
+ editions:
+ - edition: Desktop
+ none: true
+ - edition: Business
+ none: true
+ - edition: Enterprise
+ full: true
+---
+# Semantic Bridge How-Tos
+
+These how-tos are focused on interacting with the [Databricks Metric View object model](xref:semantic-bridge-metric-view-object-model) and supporting import workflows to bring these Metric Views into a Tabular semantic model.
+
+## Getting Started
+
+- @semantic-bridge-load-inspect - Load a Metric View and explore its structure
+- @semantic-bridge-import - Import a Metric View to Tabular and view diagnostics
+
+## Validation
+
+- @semantic-bridge-validate-default - Validate with built-in rules
+- @semantic-bridge-validate-simple-rules - Create predicate-based validation rules for naming conventions
+- @semantic-bridge-validate-contextual-rules - Create rules with cross-object checks like duplicate detection
+
+## Manipulating the Object Model
+
+- @semantic-bridge-add-object - Add a new object to a Metric View
+- @semantic-bridge-remove-object - Remove objects from a Metric View
+- @semantic-bridge-rename-objects - Rename objects using copy-modify pattern
+
+## Serialization
+
+- @semantic-bridge-serialize - Serialize a Metric View back to YAML
diff --git a/content/how-tos/semantic-bridge-import.md b/content/how-tos/semantic-bridge-import.md
new file mode 100644
index 00000000..098f0011
--- /dev/null
+++ b/content/how-tos/semantic-bridge-import.md
@@ -0,0 +1,215 @@
+---
+uid: semantic-bridge-import
+title: Import a Metric View and View Diagnostics
+author: Greg Baldini
+updated: 2025-01-27
+applies_to:
+ products:
+ - product: Tabular Editor 2
+ none: true
+ - product: Tabular Editor 3
+ since: 3.25.0
+ editions:
+ - edition: Desktop
+ none: true
+ - edition: Business
+ none: true
+ - edition: Enterprise
+ full: true
+---
+# Import a Metric View and View Diagnostics
+
+This how-to demonstrates how to import a Metric View into a Tabular model using C# scripts, and how to view diagnostic messages from the import process.
+
+## Prerequisites
+
+You must have a Tabular model open in Tabular Editor before importing. This can be:
+
+- A new, empty model
+- An existing model you want to enhance with objects from the Metric View
+
+## Import methods
+
+There are two import methods:
+
+| Method | Description |
+|---------------------------|------------------------------------------------|
+| `ImportToTabularFromFile` | Loads from a file path and imports in one step |
+| `ImportToTabular` | Imports the currently loaded Metric View |
+
+Both methods require:
+
+- The target Tabular `Model`
+- Databricks hostname (for M partition expressions)
+- Databricks HTTP path (for M partition expressions)
+
+## Import from file
+
+Use `ImportToTabularFromFile` to load and import in one operation:
+
+```csharp
+var success = SemanticBridge.MetricView.ImportToTabularFromFile(
+ "C:/MetricViews/sales-metrics.yaml",
+ Model,
+ "your-workspace.azuredatabricks.net",
+ "/sql/1.0/warehouses/abc123def456",
+ out var diagnostics
+);
+
+var sb = new System.Text.StringBuilder();
+if (success)
+{
+ sb.AppendLine("Import successful!");
+ sb.AppendLine($"Diagnostics: {diagnostics.Count}");
+}
+else
+{
+ sb.AppendLine("Import failed.");
+ sb.AppendLine($"Errors: {diagnostics.Count}");
+}
+
+Output(sb.ToString());
+```
+
+## Import a loaded Metric View
+
+If you've already loaded a Metric View (for inspection or modification), use `ImportToTabular`:
+
+```csharp
+// Load the Metric View first
+SemanticBridge.MetricView.Load("C:/MetricViews/sales-metrics.yaml");
+
+// Optionally inspect or modify it
+var view = SemanticBridge.MetricView.Model;
+
+var sb = new System.Text.StringBuilder();
+sb.AppendLine($"Importing Metric View with {view.Dimensions.Count} dimensions and {view.Measures.Count} measures");
+
+// Import to Tabular
+var success = SemanticBridge.MetricView.ImportToTabular(
+ Model,
+ "your-workspace.azuredatabricks.net",
+ "/sql/1.0/warehouses/abc123def456",
+ out var diagnostics
+);
+
+if (success)
+{
+ sb.AppendLine("Import successful!");
+}
+else
+{
+ sb.AppendLine("Import failed.");
+}
+
+Output(sb.ToString());
+```
+
+## Using placeholder connection values
+
+If you're testing the translation without a real Databricks connection, you can use placeholder values:
+
+```csharp
+var success = SemanticBridge.MetricView.ImportToTabularFromFile(
+ "C:/MetricViews/sales-metrics.yaml",
+ Model,
+ "placeholder-host",
+ "placeholder-path",
+ out var diagnostics
+);
+
+var sb = new System.Text.StringBuilder();
+sb.AppendLine("Import complete (with placeholder connection values)");
+sb.AppendLine("Note: Update the M partition expressions before refreshing data.");
+Output(sb.ToString());
+```
+
+## View diagnostics after import
+
+You can access diagnostics from the last import at any time using `ImportDiagnostics`.
+This example assumes that you have previously run an import, either via GUI or C# script.
+
+```csharp
+var diagnostics = SemanticBridge.MetricView.ImportDiagnostics;
+
+var sb = new System.Text.StringBuilder();
+sb.AppendLine("LAST IMPORT DIAGNOSTICS");
+sb.AppendLine("-----------------------");
+sb.AppendLine("");
+sb.AppendLine($"Total issues: {diagnostics.Count}");
+sb.AppendLine("");
+
+foreach (var diag in diagnostics)
+{
+ sb.AppendLine($"[{diag.Severity}] {diag.Message}");
+}
+
+Output(sb.ToString());
+```
+
+## Output diagnostics directly
+
+For quick inspection, you can output the diagnostics collection directly:
+
+```csharp
+// Output all diagnostics from the last import
+SemanticBridge.MetricView.ImportDiagnostics.Output();
+```
+
+## Complete workflow example
+
+Load, validate, and import with full diagnostic reporting:
+
+```csharp
+var sb = new System.Text.StringBuilder();
+
+// Load the Metric View
+SemanticBridge.MetricView.Load("C:/MetricViews/sales-metrics.yaml");
+var view = SemanticBridge.MetricView.Model;
+
+sb.AppendLine("METRIC VIEW SUMMARY");
+sb.AppendLine("-------------------");
+sb.AppendLine($"Source: {view.Source}");
+sb.AppendLine($"Joins: {view.Joins?.Count ?? 0}");
+sb.AppendLine($"Dimensions: {view.Dimensions.Count}");
+sb.AppendLine($"Measures: {view.Measures.Count}");
+sb.AppendLine("");
+
+// Validate first
+var validationDiags = SemanticBridge.MetricView.Validate().ToList();
+sb.AppendLine("VALIDATION");
+sb.AppendLine("----------");
+sb.AppendLine($"Issues: {validationDiags.Count}");
+sb.AppendLine("");
+
+// Import
+var success = SemanticBridge.MetricView.ImportToTabular(
+ Model,
+ "your-workspace.azuredatabricks.net",
+ "/sql/1.0/warehouses/abc123def456",
+ out var importDiags
+);
+
+sb.AppendLine("IMPORT RESULT");
+sb.AppendLine("-------------");
+sb.AppendLine($"Success: {success}");
+sb.AppendLine($"Diagnostics: {importDiags.Count}");
+sb.AppendLine("");
+
+if (importDiags.Count > 0)
+{
+ sb.AppendLine("Import issues:");
+ foreach (var diag in importDiags)
+ {
+ sb.AppendLine($" [{diag.Severity}] {diag.Message}");
+ }
+}
+
+Output(sb.ToString());
+```
+
+## See also
+
+- [Semantic Bridge Overview](xref:semantic-bridge)
+- [Validate a Metric View](xref:semantic-bridge-validate-default)
+- [Load and Inspect a Metric View](xref:semantic-bridge-load-inspect)
diff --git a/content/how-tos/semantic-bridge-load-inspect.md b/content/how-tos/semantic-bridge-load-inspect.md
new file mode 100644
index 00000000..a29eeb57
--- /dev/null
+++ b/content/how-tos/semantic-bridge-load-inspect.md
@@ -0,0 +1,213 @@
+---
+uid: semantic-bridge-load-inspect
+title: Load and Inspect a Metric View
+author: Greg Baldini
+updated: 2025-01-27
+applies_to:
+ products:
+ - product: Tabular Editor 2
+ none: true
+ - product: Tabular Editor 3
+ since: 3.25.0
+ editions:
+ - edition: Desktop
+ none: true
+ - edition: Business
+ none: true
+ - edition: Enterprise
+ full: true
+---
+# Load and Inspect a Metric View
+
+This how-to demonstrates how to load a Databricks Metric View into Tabular Editor and explore its structure using C# scripts.
+This is the foundational skill for all other Metric View operations.
+
+## Sample Metric View
+
+[!INCLUDE [Sample Metric View](includes/sample-metricview.md)]
+
+## Load a Metric View from a file
+
+Use `SemanticBridge.MetricView.Load` to load a Metric View from a YAML file on disk.
+
+```csharp
+// Load from a file path
+SemanticBridge.MetricView.Load("C:/MetricViews/sales-metrics.yaml");
+
+// Confirm it loaded
+Output($"Loaded Metric View version: {SemanticBridge.MetricView.Model.Version}");
+```
+
+[!INCLUDE [deserialize](includes/sample-metricview-deserialize.md)]
+
+## Access the loaded Metric View
+
+After loading, the Metric View is available in any script as `SemanticBridge.MetricView.Model`.
+This returns a Metric View [`View`](xref:TabularEditor.SemanticBridge.Platforms.Databricks.MetricView.View) object, the root of the [Metric View object graph](xref:semantic-bridge-metric-view-object-model).
+
+```csharp
+var sb = new System.Text.StringBuilder();
+var view = SemanticBridge.MetricView.Model;
+
+sb.AppendLine($"Version: {view.Version}");
+sb.AppendLine($"Source (fact table): {view.Source}");
+Output(sb.ToString());
+```
+
+## Inspect Metric View joins (dimension tables)
+
+The Metric View `Joins` property contains the dimension tables joined to the fact.
+
+```csharp
+var sb = new System.Text.StringBuilder();
+var view = SemanticBridge.MetricView.Model;
+
+sb.AppendLine($"Number of joins: {view.Joins?.Count ?? 0}");
+sb.AppendLine("");
+
+foreach (var join in view.Joins ?? [])
+{
+ sb.AppendLine($"Join: {join.Name}");
+ sb.AppendLine($" Source: {join.Source}");
+ sb.AppendLine($" On: {join.On}");
+ sb.AppendLine("");
+}
+
+Output(sb.ToString());
+```
+
+**Output:**
+
+```
+Number of joins: 3
+
+Join: product
+ Source: sales.dim.product
+ On: product_id = product.product_id
+
+Join: customer
+ Source: sales.dim.customer
+ On: customer_id = customer.customer_id
+
+Join: date
+ Source: sales.dim.date
+ On: order_date = date.date_key
+```
+
+## Inspect Metric View dimensions (fields)
+
+The Metric View `Dimensions` property contains all field definitions.
+
+```csharp
+var sb = new System.Text.StringBuilder();
+var view = SemanticBridge.MetricView.Model;
+
+sb.AppendLine($"Number of dimensions: {view.Dimensions?.Count ?? 0}");
+sb.AppendLine("");
+
+foreach (var dim in view.Dimensions ?? [])
+{
+ sb.AppendLine($"{dim.Name,-20} <- {dim.Expr}");
+}
+
+Output(sb.ToString());
+```
+
+**Output:**
+
+```
+Number of dimensions: 6
+
+product_name <- product.product_name
+product_category <- product.category
+customer_segment <- customer.segment
+order_date <- date.full_date
+order_year <- date.year
+order_month <- date.month_name
+```
+
+## Inspect Metric View measures
+
+The Metric View `Measures` property contains all Metric View measure definitions with their aggregation expressions.
+
+```csharp
+var sb = new System.Text.StringBuilder();
+var view = SemanticBridge.MetricView.Model;
+
+sb.AppendLine($"Number of measures: {view.Measures?.Count ?? 0}");
+sb.AppendLine("");
+
+foreach (var measure in view.Measures ?? [])
+{
+ sb.AppendLine($"{measure.Name,-20} = {measure.Expr}");
+}
+
+Output(sb.ToString());
+```
+
+**Output:**
+
+```
+Number of measures: 4
+
+total_revenue = SUM(revenue)
+order_count = COUNT(order_id)
+avg_order_value = AVG(revenue)
+unique_customers = COUNT(DISTINCT customer_id)
+```
+
+## Generate a complete summary
+
+Here is a complete script that outputs a formatted summary of the entire Metric View.
+
+```csharp
+var sb = new System.Text.StringBuilder();
+var view = SemanticBridge.MetricView.Model;
+
+sb.AppendLine("METRIC VIEW SUMMARY");
+sb.AppendLine("===================");
+sb.AppendLine("");
+sb.AppendLine($"Version: {view.Version}");
+sb.AppendLine($"Fact Source: {view.Source}");
+sb.AppendLine("");
+
+// Joins
+sb.AppendLine($"JOINS ({view.Joins?.Count ?? 0})");
+sb.AppendLine("---------");
+foreach (var join in view.Joins ?? [])
+{
+ sb.AppendLine($" {join.Name,-15} -> {join.Source}");
+}
+sb.AppendLine("");
+
+// Dimensions
+sb.AppendLine($"DIMENSIONS ({view.Dimensions?.Count ?? 0})");
+sb.AppendLine("--------------");
+foreach (var dim in view.Dimensions ?? [])
+{
+ sb.AppendLine($" {dim.Name,-20} <- {dim.Expr}");
+}
+sb.AppendLine("");
+
+// Measures
+sb.AppendLine($"MEASURES ({view.Measures?.Count ?? 0})");
+sb.AppendLine("------------");
+foreach (var measure in view.Measures ?? [])
+{
+ sb.AppendLine($" {measure.Name,-20} = {measure.Expr}");
+}
+
+Output(sb.ToString());
+```
+
+## Next steps
+
+Now that you can load and inspect a Metric View, you can:
+
+- [Validate the Metric View](xref:semantic-bridge-metric-view-validation) to check for issues
+- [Import the Metric View to Tabular](xref:semantic-bridge) to create tables, columns, and measures
+
+## See also
+
+- [Metric View Object Model](xref:semantic-bridge-metric-view-object-model)
+- [Semantic Bridge Overview](xref:semantic-bridge)
diff --git a/content/how-tos/semantic-bridge-remove-object.md b/content/how-tos/semantic-bridge-remove-object.md
new file mode 100644
index 00000000..e61177d5
--- /dev/null
+++ b/content/how-tos/semantic-bridge-remove-object.md
@@ -0,0 +1,156 @@
+---
+uid: semantic-bridge-remove-object
+title: Remove an Object from a Metric View
+author: Greg Baldini
+updated: 2025-01-27
+applies_to:
+ products:
+ - product: Tabular Editor 2
+ none: true
+ - product: Tabular Editor 3
+ since: 3.25.0
+ editions:
+ - edition: Desktop
+ none: true
+ - edition: Business
+ none: true
+ - edition: Enterprise
+ full: true
+---
+# Remove an object from a Metric View
+
+This how-to demonstrates how to remove Metric View dimensions from a loaded Metric View.
+Similar approaches apply to all collections in a Metric View.
+
+[!INCLUDE [deserialize](includes/sample-metricview-deserialize.md)]
+
+> [!NOTE]
+> Each removal script here affects the currently loaded Metric View.
+> If you want to run all of these, make sure to run the `Deserialize` above before each removal.
+
+## Remove by name
+
+Find the Metric View dimension and remove it from the collection:
+
+```csharp
+var view = SemanticBridge.MetricView.Model;
+
+var sb = new System.Text.StringBuilder();
+sb.AppendLine($"Dimensions before: {view.Dimensions.Count}");
+
+var dimToRemove = view.Dimensions.FirstOrDefault(d => d.Name == "order_month");
+if (dimToRemove != null)
+{
+ view.Dimensions.Remove(dimToRemove);
+ sb.AppendLine($"Removed: {dimToRemove.Name}");
+}
+
+sb.AppendLine($"Dimensions after: {view.Dimensions.Count}");
+Output(sb.ToString());
+```
+
+**Output:**
+
+```
+Dimensions before: 6
+Removed: order_month
+Dimensions after: 5
+```
+
+Observe that if you run the script above twice in a row, there is no additional removal; the before and after counts are both 5.
+
+## Remove multiple Metric View dimensions
+
+Use LINQ to filter and rebuild the collection:
+
+```csharp
+using MetricView = TabularEditor.SemanticBridge.Platforms.Databricks.MetricView;
+
+var view = SemanticBridge.MetricView.Model;
+
+var sb = new System.Text.StringBuilder();
+sb.AppendLine($"Dimensions before: {view.Dimensions.Count}");
+
+// Remove all date-related dimensions
+string[] toRemove = ["order_date", "order_year", "order_month"];
+
+var toKeep = view.Dimensions
+ .Where(d => !toRemove.Contains(d.Name))
+ .ToList();
+
+// Clear and repopulate
+view.Dimensions.Clear();
+foreach (var dim in toKeep)
+{
+ view.Dimensions.Add(dim);
+}
+
+sb.AppendLine($"Dimensions after: {view.Dimensions.Count}");
+sb.AppendLine();
+sb.AppendLine("Remaining dimensions:");
+sb.AppendLine("---------------------");
+foreach (var dim in view.Dimensions)
+{
+ sb.AppendLine($" {dim.Name}");
+}
+
+Output(sb.ToString());
+```
+
+**Output:**
+
+```
+Dimensions before: 6
+Dimensions after: 3
+
+Remaining dimensions:
+---------------------
+ product_name
+ product_category
+ customer_segment
+```
+
+## Remove Metric View dimensions from a specific table
+
+Remove all Metric View dimensions that reference the date table.
+
+> [!WARNING]
+> This example is not guaranteed to remove all and exclusively Metric View dimensions which reference a given Metric View Join.
+> Metric View Dimensions may include near-arbitrary SQL expressions, and may also reference previously defined Metric View Dimensions.
+> This example is for illustrative purposes only.
+
+```csharp
+var view = SemanticBridge.MetricView.Model;
+
+var sb = new System.Text.StringBuilder();
+sb.AppendLine($"Dimensions before: {view.Dimensions.Count}");
+
+var toRemove = view.Dimensions
+ .Where(d => d.Expr.StartsWith("date."))
+ .ToList();
+
+foreach (var dim in toRemove)
+{
+ view.Dimensions.Remove(dim);
+ sb.AppendLine($"Removed: {dim.Name} ({dim.Expr})");
+}
+
+sb.AppendLine($"Dimensions after: {view.Dimensions.Count}");
+Output(sb.ToString());
+```
+
+**Output:**
+
+```
+Dimensions before: 6
+Removed: order_date (date.full_date)
+Removed: order_year (date.year)
+Removed: order_month (date.month_name)
+Dimensions after: 3
+```
+
+## See also
+
+- @semantic-bridge-add-object
+- @semantic-bridge-rename-objects
+- @semantic-bridge-serialize
diff --git a/content/how-tos/semantic-bridge-rename-objects.md b/content/how-tos/semantic-bridge-rename-objects.md
new file mode 100644
index 00000000..c7ef337e
--- /dev/null
+++ b/content/how-tos/semantic-bridge-rename-objects.md
@@ -0,0 +1,169 @@
+---
+uid: semantic-bridge-rename-objects
+title: Rename Objects in a Metric View
+author: Greg Baldini
+updated: 2025-01-27
+applies_to:
+ products:
+ - product: Tabular Editor 2
+ none: true
+ - product: Tabular Editor 3
+ since: 3.25.0
+ editions:
+ - edition: Desktop
+ none: true
+ - edition: Business
+ none: true
+ - edition: Enterprise
+ full: true
+---
+# Rename objects in a Metric View
+
+This how-to demonstrates how to rename Metric View dimensions using a copy-modify pattern for bulk transformations.
+The same patterns apply to all collections in a Metric View.
+
+[!INCLUDE [deserialize](includes/sample-metricview-deserialize.md)]
+
+## The copy-modify pattern
+
+Since Metric View dimension names are properties on objects in a collection, the cleanest approach is to:
+
+1. Create new Metric View `Dimension` objects with the modified names
+2. Clear the original collection
+3. Add the new objects
+
+This avoids issues with modifying objects while iterating.
+
+## Convert snake_case to Title Case
+
+Transform Metric View dimension names from `product_name` to `Product Name`:
+
+```csharp
+using System.Globalization;
+using MetricView = TabularEditor.SemanticBridge.Platforms.Databricks.MetricView;
+
+var view = SemanticBridge.MetricView.Model;
+var textInfo = CultureInfo.CurrentCulture.TextInfo;
+
+var sb = new System.Text.StringBuilder();
+sb.AppendLine("BEFORE");
+sb.AppendLine("------");
+foreach (var dim in view.Dimensions)
+{
+ sb.AppendLine($" {dim.Name}");
+}
+
+// Create renamed dimensions
+var renamed = view.Dimensions.Select(dim => new MetricView.Dimension
+{
+ Name = textInfo.ToTitleCase(dim.Name.Replace('_', ' ')),
+ Expr = dim.Expr
+}).ToList();
+
+// Replace the collection
+view.Dimensions.Clear();
+foreach (var dim in renamed)
+{
+ view.Dimensions.Add(dim);
+}
+
+sb.AppendLine();
+sb.AppendLine("AFTER");
+sb.AppendLine("-----");
+foreach (var dim in view.Dimensions)
+{
+ sb.AppendLine($" {dim.Name}");
+}
+
+Output(sb.ToString());
+```
+
+**Output:**
+
+```
+BEFORE
+------
+ product_name
+ product_category
+ customer_segment
+ order_date
+ order_year
+ order_month
+
+AFTER
+-----
+ Product Name
+ Product Category
+ Customer Segment
+ Order Date
+ Order Year
+ Order Month
+```
+
+## Rename using a mapping dictionary
+
+Apply specific renames using a lookup:
+
+```csharp
+using MetricView = TabularEditor.SemanticBridge.Platforms.Databricks.MetricView;
+
+var view = SemanticBridge.MetricView.Model;
+
+// Define rename mappings
+var renames = new Dictionary
+{
+ { "product_name", "Product" },
+ { "product_category", "Category" },
+ { "customer_segment", "Segment" },
+ { "order_date", "Date" },
+ { "order_year", "Year" },
+ { "order_month", "Month" }
+};
+
+var sb = new System.Text.StringBuilder();
+
+// Create renamed dimensions
+var renamed = view.Dimensions
+ .Select(
+ dim => new MetricView.Dimension
+ {
+ Name = renames.TryGetValue(dim.Name, out var newName) ? newName : dim.Name,
+ Expr = dim.Expr
+ })
+ .ToList();
+
+// Replace the collection
+view.Dimensions.Clear();
+foreach (var dim in renamed)
+{
+ view.Dimensions.Add(dim);
+}
+
+sb.AppendLine("Renamed dimensions:");
+sb.AppendLine("-------------------");
+foreach (var dim in view.Dimensions)
+{
+ sb.AppendLine($" {dim.Name,-20} <- {dim.Expr}");
+}
+
+Output(sb.ToString());
+```
+
+**Output:**
+
+```
+Renamed dimensions:
+-------------------
+ Product <- product.product_name
+ Category <- product.category
+ Segment <- customer.segment
+ Date <- date.full_date
+ Year <- date.year
+ Month <- date.month_name
+```
+
+## See also
+
+- @semantic-bridge-add-object
+- @semantic-bridge-remove-object
+- @semantic-bridge-serialize
diff --git a/content/how-tos/semantic-bridge-serialize.md b/content/how-tos/semantic-bridge-serialize.md
new file mode 100644
index 00000000..f83fcbd2
--- /dev/null
+++ b/content/how-tos/semantic-bridge-serialize.md
@@ -0,0 +1,138 @@
+---
+uid: semantic-bridge-serialize
+title: Serialize a Metric View to YAML
+author: Greg Baldini
+updated: 2025-01-27
+applies_to:
+ products:
+ - product: Tabular Editor 2
+ none: true
+ - product: Tabular Editor 3
+ since: 3.25.0
+ editions:
+ - edition: Desktop
+ none: true
+ - edition: Business
+ none: true
+ - edition: Enterprise
+ full: true
+---
+# Serialize a Metric View to YAML
+
+This how-to demonstrates how to serialize a Metric View back to YAML format, either as a string or saved to a file.
+
+> [!WARNING]
+> The MVP only supports v0.1 Metric View properties. Any v1.1 metadata present in a loaded Metric View is silently ignored and will be lost when you serialize.
+> Do not overwrite a source YAML file that contains v1.1 metadata.
+
+
+[!INCLUDE [deserialize](includes/sample-metricview-deserialize.md)]
+
+## Serialize to a string
+
+Use `Serialize()` to get the YAML representation:
+
+```csharp
+var yaml = SemanticBridge.MetricView.Serialize();
+
+var sb = new System.Text.StringBuilder();
+sb.AppendLine("YAML output:");
+sb.AppendLine("------------");
+sb.AppendLine(yaml);
+Output(sb.ToString());
+```
+
+## Save to a file
+
+Use `Save(path)` to write the YAML directly to disk:
+
+```csharp
+var path = "C:/MetricViews/updated-sales-metrics.yaml";
+
+SemanticBridge.MetricView.Save(path);
+
+Output($"Metric View saved to: {path}");
+```
+
+## Round-trip workflow
+
+A common workflow is to load, modify, and save a Metric View:
+
+```csharp
+using System.Globalization;
+using MetricView = TabularEditor.SemanticBridge.Platforms.Databricks.MetricView;
+
+// The Metric View is already loaded from the include above
+
+var view = SemanticBridge.MetricView.Model;
+var textInfo = CultureInfo.CurrentCulture.TextInfo;
+
+// Modify: rename dimensions from snake_case to Title Case
+var renamed = view.Dimensions.Select(dim => new MetricView.Dimension
+{
+ Name = textInfo.ToTitleCase(dim.Name.Replace('_', ' ')),
+ Expr = dim.Expr
+}).ToList();
+
+view.Dimensions.Clear();
+foreach (var dim in renamed)
+{
+ view.Dimensions.Add(dim);
+}
+
+// Serialize to see the result
+var yaml = SemanticBridge.MetricView.Serialize();
+
+var sb = new System.Text.StringBuilder();
+sb.AppendLine("Modified YAML:");
+sb.AppendLine("--------------");
+sb.AppendLine(yaml);
+Output(sb.ToString());
+```
+
+**Output:**
+
+```
+Modified YAML:
+--------------
+version: 0.1
+source: sales.fact.orders
+joins:
+- name: product
+ source: sales.dim.product
+ on: source.product_id = product.product_id
+- name: customer
+ source: sales.dim.customer
+ on: source.customer_id = customer.customer_id
+- name: date
+ source: sales.dim.date
+ on: source.order_date = date.date_key
+dimensions:
+- name: Product Name
+ expr: product.product_name
+- name: Product Category
+ expr: product.category
+- name: Customer Segment
+ expr: customer.segment
+- name: Order Date
+ expr: date.full_date
+- name: Order Year
+ expr: date.year
+- name: Order Month
+ expr: date.month_name
+measures:
+- name: total_revenue
+ expr: SUM(revenue)
+- name: order_count
+ expr: COUNT(order_id)
+- name: avg_order_value
+ expr: AVG(revenue)
+- name: unique_customers
+ expr: COUNT(DISTINCT customer_id)
+```
+
+## See also
+
+- @semantic-bridge-load-inspect
+- @semantic-bridge-import
+- @semantic-bridge
diff --git a/content/how-tos/semantic-bridge-validate-contextual-rules.md b/content/how-tos/semantic-bridge-validate-contextual-rules.md
new file mode 100644
index 00000000..5a6725d1
--- /dev/null
+++ b/content/how-tos/semantic-bridge-validate-contextual-rules.md
@@ -0,0 +1,228 @@
+---
+uid: semantic-bridge-validate-contextual-rules
+title: Create Contextual Validation Rules
+author: Greg Baldini
+updated: 2025-01-27
+applies_to:
+ products:
+ - product: Tabular Editor 2
+ none: true
+ - product: Tabular Editor 3
+ since: 3.25.0
+ editions:
+ - edition: Desktop
+ none: true
+ - edition: Business
+ none: true
+ - edition: Enterprise
+ full: true
+---
+# Create Contextual Validation Rules
+
+This how-to demonstrates how to create validation rules that check conditions across multiple objects using the validation context.
+These rules are for illustrative purposes only and do not necessarily reflect hard technical requirements of either Metric Views or the Semantic Bridge.
+
+## When to use contextual rules
+
+Use contextual rules when you need to:
+
+- Detect duplicate names across objects
+- Check that names don't conflict between different object types
+- Access information about previously validated objects
+
+> [!NOTE]
+> The validation process validates each Metric View object in order, so the context consists only of those items already visited in the validation.
+
+## The MakeValidationRule method
+
+The generic `MakeValidationRule` method provides access to the validation context:
+
+```csharp
+SemanticBridge.MetricView.MakeValidationRule(
+ "rule_name",
+ "category",
+ (obj, context) => {
+ // Return IEnumerable
+ // Empty collection means validation passed
+ }
+);
+```
+
+The `context` parameter provides:
+
+- `context.DimensionNames` - names of dimensions already validated
+- `context.MeasureNames` - names of measures already validated
+- `context.JoinNames` - names of joins already validated
+- `context.MakeError(message)` - create an error diagnostic
+- `context.MakeError(message, property)` - create an error diagnostic, calling out the specific property with an error
+
+Because you create the diagnostic message in the body of the validation function, you can put details about the current object being validated into the message.
+
+## Using directive for Metric View types
+
+Add this using directive to reference Metric View types:
+
+```csharp
+using MetricView = TabularEditor.SemanticBridge.Platforms.Databricks.MetricView;
+```
+
+## Rule: Metric View Measure name must not duplicate a Metric View dimension name
+
+```csharp
+using MetricView = TabularEditor.SemanticBridge.Platforms.Databricks.MetricView;
+
+var measureNotDimensionRule = SemanticBridge.MetricView.MakeValidationRule(
+ "measure_not_dimension_name",
+ "naming",
+ (measure, context) =>
+ context.DimensionNames.Contains(measure.Name)
+ ? [context.MakeError($"Measure '{measure.Name}' has the same name as a dimension")]
+ : []
+);
+```
+
+## Rule: Metric View Measure name must not duplicate another Metric View measure
+
+```csharp
+using MetricView = TabularEditor.SemanticBridge.Platforms.Databricks.MetricView;
+
+var noDuplicateMeasureRule = SemanticBridge.MetricView.MakeValidationRule(
+ "no_duplicate_measures",
+ "naming",
+ (measure, context) =>
+ context.MeasureNames.Contains(measure.Name)
+ ? [context.MakeError($"Measure '{measure.Name}' is defined more than once")]
+ : []
+);
+```
+
+## Why separate rules are better
+
+Notice that we created two separate rules instead of one combined rule. This is the recommended approach because:
+
+1. **Clearer error messages**: Each rule produces a specific, actionable message
+2. **Easier maintenance**: Rules can be added, removed, or modified independently
+3. **Simpler logic**: Each rule checks exactly one condition
+4. **Better categorization**: Rules can be grouped and filtered by purpose
+
+## Complete example
+
+This Metric View has naming conflicts that will trigger both contextual rules:
+
+```csharp
+using MetricView = TabularEditor.SemanticBridge.Platforms.Databricks.MetricView;
+
+// Create a Metric View with naming conflicts
+SemanticBridge.MetricView.Deserialize("""
+ version: 0.1
+ source: sales.fact.orders
+ dimensions:
+ # 'revenue' is used as both a dimension and measure name
+ - name: revenue
+ expr: source.revenue
+ - name: quantity
+ expr: source.quantity
+ - name: order_date
+ expr: source.order_date
+ measures:
+ # measureNotDimensionRule violation - same name as dimension
+ - name: revenue
+ expr: SUM(source.revenue)
+ # noDuplicateMeasureRule violation - 'total_quantity' appears twice
+ - name: total_quantity
+ expr: SUM(source.quantity)
+ - name: total_quantity
+ expr: COUNT(source.order_id)
+ # This one is fine
+ - name: order_count
+ expr: COUNT(source.order_id)
+ """);
+
+// Define contextual rules
+var measureNotDimensionRule = SemanticBridge.MetricView.MakeValidationRule(
+ "measure_not_dimension_name",
+ "naming",
+ (measure, context) =>
+ context.DimensionNames.Contains(measure.Name)
+ ? [context.MakeError($"Measure '{measure.Name}' has the same name as a dimension")]
+ : []
+);
+
+var noDuplicateMeasureRule = SemanticBridge.MetricView.MakeValidationRule(
+ "no_duplicate_measures",
+ "naming",
+ (measure, context) =>
+ context.MeasureNames.Contains(measure.Name)
+ ? [context.MakeError($"Measure '{measure.Name}' is defined more than once")]
+ : []
+);
+
+// Run validation
+var diagnostics = SemanticBridge.MetricView.Validate([
+ measureNotDimensionRule,
+ noDuplicateMeasureRule
+]).ToList();
+
+// Output results
+var sb = new System.Text.StringBuilder();
+sb.AppendLine("CONTEXTUAL VALIDATION RESULTS");
+sb.AppendLine("-----------------------------");
+sb.AppendLine("");
+sb.AppendLine($"Found {diagnostics.Count} issue(s):");
+sb.AppendLine("");
+
+foreach (var diag in diagnostics)
+{
+ sb.AppendLine($"[{diag.Severity}] {diag.Message}");
+}
+
+Output(sb.ToString());
+```
+
+**Output:**
+
+```
+CONTEXTUAL VALIDATION RESULTS
+-----------------------------
+
+Found 2 issue(s):
+
+[Error] Measure 'revenue' has the same name as a dimension
+[Error] Measure 'total_quantity' is defined more than once
+```
+
+## Combining with default rules
+
+You can run contextual rules alongside the default validation rules:
+
+```csharp
+using MetricView = TabularEditor.SemanticBridge.Platforms.Databricks.MetricView;
+
+var customRules = new[] {
+ SemanticBridge.MetricView.MakeValidationRule(
+ "measure_not_dimension_name",
+ "naming",
+ (measure, context) =>
+ context.DimensionNames.Contains(measure.Name)
+ ? [context.MakeError($"Measure '{measure.Name}' has the same name as a dimension")]
+ : []
+ )
+};
+
+// Run default rules first
+var defaultDiagnostics = SemanticBridge.MetricView.Validate().ToList();
+
+// Then run custom rules
+var customDiagnostics = SemanticBridge.MetricView.Validate(customRules).ToList();
+
+var sb = new System.Text.StringBuilder();
+sb.AppendLine($"Default rule issues: {defaultDiagnostics.Count}");
+sb.AppendLine($"Custom rule issues: {customDiagnostics.Count}");
+Output(sb.ToString());
+```
+
+## See also
+
+- [Semantic Bridge Validation](xref:semantic-bridge-metric-view-validation)
+- [Create Simple Validation Rules](xref:semantic-bridge-validate-simple-rules)
+- [Validate with Default Rules](xref:semantic-bridge-validate-default)
diff --git a/content/how-tos/semantic-bridge-validate-default.md b/content/how-tos/semantic-bridge-validate-default.md
new file mode 100644
index 00000000..62a08fa4
--- /dev/null
+++ b/content/how-tos/semantic-bridge-validate-default.md
@@ -0,0 +1,179 @@
+---
+uid: semantic-bridge-validate-default
+title: Validate a Metric View with Default Rules
+author: Greg Baldini
+updated: 2025-01-27
+applies_to:
+ products:
+ - product: Tabular Editor 2
+ none: true
+ - product: Tabular Editor 3
+ since: 3.25.0
+ editions:
+ - edition: Desktop
+ none: true
+ - edition: Business
+ none: true
+ - edition: Enterprise
+ full: true
+---
+# Validate a Metric View with Default Rules
+
+This how-to demonstrates how to validate a loaded Metric View using the built-in validation rules and interpret the diagnostic messages.
+
+## Default validation rules
+
+The Semantic Bridge includes these built-in validation rules:
+
+| Rule | Description |
+|--------------------------|-------------------------------------------------------------------------------|
+| JoinNameRequired | Metric View Join must have a name |
+| UniqueJoinName | Metric View Join names must be unique |
+| JoinSourceRequired | Metric View Join must have a source |
+| JoinOnOrUsingRequired | Metric View Join must specify either `on` or `using` |
+| JoinOnOrUsingExclusivity | Metric View Join cannot specify both `on` and `using` |
+| JoinOnFormat | Metric View Join `on` clause must be a valid equijoin expression |
+| JoinUsingColumnCount | Metric View Join `using` clause must have exactly one column (MVP limitation) |
+| DimensionNameRequired | Metric View Dimension must have a name |
+| UniqueDimensionName | Metric View Dimension names must be unique |
+| DimensionExprRequired | Metric View Dimension must have an expression |
+| MeasureNameRequired | Metric View Measure must have a name |
+| UniqueMeasureName | Metric View Measure names must be unique |
+| MeasureExprRequired | Metric View Measure must have an expression |
+
+## Run validation with default rules
+
+Call `Validate()` with no arguments to use the built-in validation rules.
+
+```csharp
+var diagnostics = SemanticBridge.MetricView.Validate().ToList();
+
+Output($"Validation complete: {diagnostics.Count} issue(s) found");
+```
+
+## Interpret diagnostic messages
+
+Each diagnostic message contains:
+
+- **Severity**: Error, Warning, or Information
+- **Message**: Description of the issue
+- **Path**: Location of the object in the Metric View hierarchy
+
+```csharp
+var diagnostics = SemanticBridge.MetricView.Validate().ToList();
+
+var sb = new System.Text.StringBuilder();
+sb.AppendLine("VALIDATION RESULTS");
+sb.AppendLine("------------------");
+sb.AppendLine("");
+
+if (diagnostics.Count == 0)
+{
+ sb.AppendLine("No issues found.");
+}
+else
+{
+ foreach (var diag in diagnostics)
+ {
+ sb.AppendLine($"[{diag.Severity}] {diag.Message}");
+ sb.AppendLine($" Path: {diag.Path}");
+ sb.AppendLine("");
+ }
+}
+
+Output(sb.ToString());
+```
+
+## Example with validation errors
+
+Some rules (required fields) are enforced during deserialization.
+The remaining rules check for duplicates and structural issues after deserialization.
+
+This Metric View demonstrates violations that are caught by `Validate()`:
+
+```csharp
+SemanticBridge.MetricView.Deserialize("""
+ version: 0.1
+ source: sales.fact.orders
+ joins:
+ # UniqueJoinName - duplicate name 'customer'
+ - name: customer
+ source: sales.dim.customer
+ on: customer_id = customer.customer_id
+ - name: customer
+ source: sales.dim.customer_backup
+ on: customer_id = customer_backup.customer_id
+ # JoinOnOrUsingRequired - neither on nor using
+ - name: date
+ source: sales.dim.date
+ dimensions:
+ # UniqueDimensionName - duplicate name 'category'
+ - name: category
+ expr: product.category
+ - name: category
+ expr: product.subcategory
+ - name: product_name
+ expr: product.product_name
+ measures:
+ # UniqueMeasureName - duplicate name 'total'
+ - name: total
+ expr: SUM(revenue)
+ - name: total
+ expr: SUM(quantity)
+ - name: order_count
+ expr: COUNT(order_id)
+ """);
+
+var diagnostics = SemanticBridge.MetricView.Validate().ToList();
+
+var sb = new System.Text.StringBuilder();
+sb.AppendLine($"Found {diagnostics.Count} issue(s):");
+sb.AppendLine("");
+
+foreach (var diag in diagnostics)
+{
+ sb.AppendLine($"[{diag.Severity}] {diag.Message}");
+}
+
+Output(sb.ToString());
+```
+
+**Output:**
+
+```
+Found 6 issue(s):
+
+[Error] Join 'customer' must use a simple equality condition with table prefixes (e.g. 'source.column = dimension.column')
+[Error] Duplicate join name: 'customer'
+[Error] Join 'customer' must use a simple equality condition with table prefixes (e.g. 'source.column = dimension.column')
+[Error] Join 'date' must specify either 'on' or 'using' clause
+[Error] Duplicate dimension name: 'category'
+[Error] Duplicate measure name: 'total'
+```
+
+## Filter diagnostics by severity
+
+You can filter diagnostics to focus on errors only:
+
+```csharp
+using System.Linq;
+using TabularEditor.SemanticBridge.Orchestration;
+
+var diagnostics = SemanticBridge.MetricView.Validate().ToList();
+var errors = diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error).ToList();
+
+var sb = new System.Text.StringBuilder();
+sb.AppendLine($"Errors: {errors.Count}");
+sb.AppendLine($"Total issues: {diagnostics.Count}");
+Output(sb.ToString());
+```
+
+## Next steps
+
+- [Create simple validation rules](xref:semantic-bridge-validate-simple-rules) to enforce your own conventions
+- [Create contextual validation rules](xref:semantic-bridge-validate-contextual-rules) for cross-object checks
+
+## See also
+
+- [Semantic Bridge Validation](xref:semantic-bridge-metric-view-validation)
+- [Metric View Object Model](xref:semantic-bridge-metric-view-object-model)
diff --git a/content/how-tos/semantic-bridge-validate-simple-rules.md b/content/how-tos/semantic-bridge-validate-simple-rules.md
new file mode 100644
index 00000000..14e89b29
--- /dev/null
+++ b/content/how-tos/semantic-bridge-validate-simple-rules.md
@@ -0,0 +1,199 @@
+---
+uid: semantic-bridge-validate-simple-rules
+title: Create Simple Validation Rules
+author: Greg Baldini
+updated: 2025-01-27
+applies_to:
+ products:
+ - product: Tabular Editor 2
+ none: true
+ - product: Tabular Editor 3
+ since: 3.25.0
+ editions:
+ - edition: Desktop
+ none: true
+ - edition: Business
+ none: true
+ - edition: Enterprise
+ full: true
+---
+# Create Simple Validation Rules
+
+This how-to demonstrates how to create simple predicate-based validation rules to enforce naming conventions and structural requirements.
+These rules are for illustrative purposes only and do not necessarily reflect hard technical requirements of either Metric Views or the Semantic Bridge.
+
+## The four rule helpers
+
+There is a helper method for each type of Metric View object:
+
+- `MakeValidationRuleForView` - rules for the root View object
+- `MakeValidationRuleForJoin` - rules for Join objects
+- `MakeValidationRuleForDimension` - rules for Dimension objects
+- `MakeValidationRuleForMeasure` - rules for Measure objects
+
+Each helper takes four parameters:
+
+1. **name**: unique identifier for the rule
+2. **category**: grouping for related rules
+3. **message**: error message when the rule is violated
+4. **isInvalid**: a function returning `true` if the object is invalid
+
+## Rule for View
+
+Check that the Metric View version is the expected value:
+
+```csharp
+var versionRule = SemanticBridge.MetricView.MakeValidationRuleForView(
+ "version_check",
+ "structure",
+ "Metric View version must be 0.1 or 1.1",
+ (view) => view.Version != "0.1" && view.Version != "1.1"
+);
+```
+
+## Rule for Metric View Join
+
+Check that Metric View join sources use fully qualified table names (contain a dot):
+
+```csharp
+var joinSourceRule = SemanticBridge.MetricView.MakeValidationRuleForJoin(
+ "qualified_source",
+ "structure",
+ "Join source must be a fully qualified table name (e.g., `catalog.schema.table`)",
+ (join) => !join.Source.Contains('.')
+);
+```
+
+## Rule for Metric View Dimension
+
+Check that Metric View dimension names do not contain underscores:
+
+```csharp
+var dimensionNameRule = SemanticBridge.MetricView.MakeValidationRuleForDimension(
+ "no_underscores",
+ "naming",
+ "Dimension names should use spaces, not underscores",
+ (dim) => dim.Name.Contains('_')
+);
+```
+
+## Rule for Metric View Measure
+
+Check that Metric View measure expressions do not contain SELECT (to avoid accidental subqueries):
+
+```csharp
+var measureExprRule = SemanticBridge.MetricView.MakeValidationRuleForMeasure(
+ "no_select_subquery",
+ "structure",
+ "Measure expressions should not contain SELECT subqueries",
+ (measure) => measure.Expr.ToUpper().Contains("SELECT")
+);
+```
+
+## Complete example
+
+This Metric View has violations for each of the rules defined above:
+
+```csharp
+// Create a Metric View with violations for each rule
+SemanticBridge.MetricView.Deserialize("""
+ version: 0.2
+ source: sales.fact.orders
+ joins:
+ # joinSourceRule violation - not fully qualified
+ - name: customer
+ source: customer_table
+ on: source.customer_id = customer.customer_id
+ dimensions:
+ # dimensionNameRule violations - contains underscores
+ - name: product_name
+ expr: source.product_name
+ - name: order_date
+ expr: source.order_date
+ # This one is fine
+ - name: Category
+ expr: source.category
+ measures:
+ # measureExprRule violation - contains SELECT subquery
+ - name: complex_calc
+ expr: (SELECT MAX(price) FROM products)
+ # This one is fine
+ - name: total_revenue
+ expr: SUM(source.revenue)
+ """);
+
+var versionRule = SemanticBridge.MetricView.MakeValidationRuleForView(
+ "version_check",
+ "structure",
+ "Metric View version must be 0.1 or 1.1",
+ (view) => view.Version != "0.1" && view.Version != "1.1"
+);
+
+var joinSourceRule = SemanticBridge.MetricView.MakeValidationRuleForJoin(
+ "qualified_source",
+ "structure",
+ "Join source must be a fully qualified table name (e.g., `catalog.schema.table`)",
+ (join) => !join.Source.Contains('.')
+);
+
+var dimensionNameRule = SemanticBridge.MetricView.MakeValidationRuleForDimension(
+ "no_underscores",
+ "naming",
+ "Dimension names should use spaces, not underscores",
+ (dim) => dim.Name.Contains('_')
+);
+
+var measureExprRule = SemanticBridge.MetricView.MakeValidationRuleForMeasure(
+ "no_select_subquery",
+ "structure",
+ "Measure expressions should not contain SELECT subqueries",
+ (measure) => measure.Expr.ToUpper().Contains("SELECT")
+);
+
+// Run validation with custom rules
+var diagnostics = SemanticBridge.MetricView.Validate([
+ versionRule,
+ joinSourceRule,
+ dimensionNameRule,
+ measureExprRule
+]).ToList();
+
+// Output results
+var sb = new System.Text.StringBuilder();
+sb.AppendLine("CUSTOM VALIDATION RESULTS");
+sb.AppendLine("-------------------------");
+sb.AppendLine("");
+sb.AppendLine($"Found {diagnostics.Count} issue(s):");
+sb.AppendLine("");
+
+foreach (var diag in diagnostics)
+{
+ sb.AppendLine($"[{diag.Severity}] {diag.Path}: {diag.Message}");
+}
+
+Output(sb.ToString());
+```
+
+**Output:**
+
+```
+CUSTOM VALIDATION RESULTS
+-------------------------
+
+Found 5 issue(s):
+
+[Error] MetricView: Metric View version must be 0.1 or 1.1
+[Error] MetricView.Joins['customer']: Join source must be a fully qualified table name (e.g., `catalog.schema.table`)
+[Error] MetricView.Dimensions['product_name']: Dimension names should use spaces, not underscores
+[Error] MetricView.Dimensions['order_date']: Dimension names should use spaces, not underscores
+[Error] MetricView.Measures['complex_calc']: Measure expressions should not contain SELECT subqueries
+```
+
+## Next steps
+
+- [Create contextual validation rules](xref:semantic-bridge-validate-contextual-rules) for cross-object checks like duplicate detection
+
+## See also
+
+- [Semantic Bridge Validation](xref:semantic-bridge-metric-view-validation)
+- [Validate with Default Rules](xref:semantic-bridge-validate-default)
diff --git a/content/how-tos/toc.md b/content/how-tos/toc.md
index be0f84c8..28fafa4e 100644
--- a/content/how-tos/toc.md
+++ b/content/how-tos/toc.md
@@ -1,38 +1,49 @@
# Scripting and Automation
## [Advanced Scripting](Advanced-Scripting.md)
-## [Script Reference Objects](script-reference-objects.md)
+## [Script Reference Objects](script-reference-objects.md)
# Model Management and Deployment
-## [Deploy Current Model](deploy-current-model.md)
-## [Connect to SSAS](connect-ssas.md)
-## [Load and Save](load-save.md)
-## [Folder Serialization](folder-serialization.md)
+## [Deploy Current Model](deploy-current-model.md)
+## [Connect to SSAS](connect-ssas.md)
+## [Load and Save](load-save.md)
+## [Folder Serialization](folder-serialization.md)
## [Master Model Pattern](Master-model-pattern.md)
## [Update compatibility level](update-compatibility-level.md)
# Data Import and Tables
-## [Importing Tables (TE2)](Importing-Tables.md)
-## [Importing Tables from Excel (TE2)](importing-tables-from-excel.md)
-## [Replace Tables](replace-tables.md)
+## [Importing Tables (TE2)](Importing-Tables.md)
+## [Importing Tables from Excel (TE2)](importing-tables-from-excel.md)
+## [Replace Tables](replace-tables.md)
# Model Organization and Structure
-## [Advanced Filtering of Explorer Tree](Advanced-Filtering-of-the-Explorer-Tree.md)
-## [Drag and Drop](drag-drop.md)
-## [Duplicate and Batch Rename](duplicate-batchrename.md)
-## [Edit Properties](edit-properties.md)
+## [Advanced Filtering of Explorer Tree](Advanced-Filtering-of-the-Explorer-Tree.md)
+## [Drag and Drop](drag-drop.md)
+## [Duplicate and Batch Rename](duplicate-batchrename.md)
+## [Edit Properties](edit-properties.md)
# Translations and Perspectives
-## [Import/Export Translations](import-export-translations.md)
-## [Perspectives and Translations](perspectives-translations.md)
+## [Import/Export Translations](import-export-translations.md)
+## [Perspectives and Translations](perspectives-translations.md)
# Data Security and Roles
-## [Roles and Row-Level Security](roles-rls.md)
+## [Roles and Row-Level Security](roles-rls.md)
# Connectivity and Integration
-## [XMLA Analysis Services Connectivity](xmla-as-connectivity.md)
-## [Power BI XMLA PBIX Workaround](powerbi-xmla-pbix-workaround.md)
+## [XMLA Analysis Services Connectivity](xmla-as-connectivity.md)
+## [Power BI XMLA PBIX Workaround](powerbi-xmla-pbix-workaround.md)
# Model Maintenance
-## [Formula Fixup Dependencies](formula-fixup-dependencies.md)
-## [Metadata Backup](metadata-backup.md)
-## [Undo and Redo](undo-redo.md)
\ No newline at end of file
+## [Formula Fixup Dependencies](formula-fixup-dependencies.md)
+## [Metadata Backup](metadata-backup.md)
+## [Undo and Redo](undo-redo.md)
+
+# [Semantic Bridge](semantic-bridge-how-tos.md)
+## @semantic-bridge-load-inspect
+## @semantic-bridge-import
+## @semantic-bridge-validate-default
+## @semantic-bridge-validate-simple-rules
+## @semantic-bridge-validate-contextual-rules
+## @semantic-bridge-add-object
+## @semantic-bridge-remove-object
+## @semantic-bridge-rename-objects
+## @semantic-bridge-serialize