Table of Contents

How to Navigate the TOM Object Hierarchy

Every C# script starts from the Model object or the C# Scripts Selected object. These expose the Tabular Editor TOM wrapper, which wraps the Microsoft Analysis Services Tabular Object Model (TOM). See the (xref:TabularEditor.TOMWrapper) API reference for the full wrapper documentation.

Quick reference

// Direct path to a specific object
var table    = Model.Tables["Sales"];
var measure  = Model.Tables["Sales"].Measures["Revenue"];
var column   = Model.Tables["Sales"].Columns["Amount"];
var hierarchy = Model.Tables["Date"].Hierarchies["Calendar"];
var partition = Model.Tables["Sales"].Partitions["Sales-Part1"];

// Cross-table shortcut collections
Model.AllMeasures          // every measure across all tables
Model.AllColumns           // every column across all tables
Model.AllHierarchies       // every hierarchy across all tables
Model.AllPartitions        // every partition across all tables
Model.AllLevels            // every level across all hierarchies
Model.AllCalculationItems  // every calculation item across all calculation groups

// Top-level collections
Model.Tables               // all tables
Model.Relationships        // all relationships
Model.Perspectives         // all perspectives
Model.Roles                // all security roles
Model.Cultures             // all translation cultures
Model.DataSources          // all data sources
Model.CalculationGroups    // all calculation group tables

Accessing objects by name

Use the indexer ["name"] on any collection to retrieve an object by its exact name. This throws an exception if the name does not exist.

var salesTable = Model.Tables["Sales"];
var revenueM  = salesTable.Measures["Revenue"];
var amountCol = salesTable.Columns["Amount"];

Use FirstOrDefault() when the object may not exist:

var table = Model.Tables.FirstOrDefault(t => t.Name == "Sales");
if (table == null) { Error("Table not found"); return; } // return exits the script early

Every object holds a reference to its parent. Use these to walk up the hierarchy.

var measure = Model.AllMeasures.First(m => m.Name == "Revenue");
var parentTable = measure.Table;          // Table that contains this measure
var model = measure.Model;                // The Model root

var level = Model.AllLevels.First();
var hierarchy = level.Hierarchy;           // parent hierarchy
var table = level.Table;                   // parent table (via hierarchy)

// Navigate up to Model and back down to a different table
var m = Model.AllMeasures.First(m => m.Name == "Revenue");
var otherCol = m.Table.Model.Tables["Product"].Columns.First();
Note

The last example demonstrates that you can navigate up to Model from any child object and back down to any table in the model.

Each Table exposes typed collections for its child objects.

var table = Model.Tables["Sales"];

Output(table.Columns);                     // ColumnCollection
Output(table.Measures);                    // MeasureCollection
Output(table.Hierarchies);                 // HierarchyCollection
Output(table.Partitions);                  // PartitionCollection

Searching with predicates

Use LINQ methods on any collection to find objects by property values.

// Find all fact tables
var factTables = Model.Tables.Where(t => t.Name.StartsWith("Fact"));

// Find all hidden measures
var hiddenMeasures = Model.AllMeasures.Where(m => m.IsHidden);

// Find the first column with a specific data type
var dateCol = Model.AllColumns.First(c => c.DataType == DataType.DateTime);

Calculation groups and calculation items

Calculation group tables are a subtype of Table. Access them through Model.CalculationGroups and iterate their items.

foreach (var cg in Model.CalculationGroups)
{
    foreach (var item in cg.CalculationItems)
    {
        Info(item.Name + ": " + item.Expression);
    }
}

Relationships

Relationships live on the Model, not on tables. Each relationship references its from/to columns and tables.

foreach (var rel in Model.Relationships)
{
    var fromTable  = rel.FromTable;
    var fromColumn = rel.FromColumn;
    var toTable    = rel.ToTable;
    var toColumn   = rel.ToColumn;
}

Dynamic LINQ equivalent

In Best Practice Analyzer (BPA) rule expressions and TOM Explorer tree filters, you access properties directly on the object in context. Parent navigation uses dot notation.

C# script Dynamic LINQ (BPA)
measure.Table.Name Table.Name
column.Table.IsHidden Table.IsHidden
table.Columns.Count() Columns.Count()
table.Measures.Any(m => m.IsHidden) Measures.Any(IsHidden)
Note

Dynamic LINQ expressions in BPA rules evaluate against a single object at a time. You do not have access to Model or cross-table collections. Use the rule's Applies to scope to select which object type the expression runs against.

See also