TMDL 入门

适用于: SQL Server 2016 及更高版本的 Analysis Services Azure Analysis Services Fabric/Power BI Premium

在开始本文之前,请务必全面了解 表格模型定义语言(TMDL)概述中所述的概念。

浏览 TMDL 的最简单方法是引用 Analysis Services 管理对象(AMO)Nuget 包,并使用 TMDL API 方法对 TMDL 进行序列化和反序列化。

获取 Nuget 包

获取 TMDL 模型表示形式

以下代码示例演示如何在 Power BI Premium 工作区中获取语义模型的 TMDL 模型表示形式:

var workspaceXmla = " <Workspace XMLA address>";
var datasetName = "<dataset name>";
var outputPath = System.Environment.CurrentDirectory;

using (var server = new Microsoft.AnalysisServices.Tabular.Server())
{
    server.Connect(workspaceXmla);

    var database = server.Databases.GetByName(datasetName);

    var destinationFolder = $"{outputPath}\\{database.Name}-tmdl";

    Microsoft.AnalysisServices.Tabular.TmdlSerializer.SerializeDatabaseToFolder(database.Model, destinationFolder);

}

输出是具有模型 TMDL 表示形式的文件夹,如下所示:

具有模型 TMDL 表示形式的 文件夹

序列化到文件夹中后,使用文本编辑器编辑 TMDL 文件。 例如,通过使用 Visual Studio Code,我们可以添加新度量值,[销售金额(计算机)]

/// Sales data for year over year analysis
table Sales        

    partition 'Sales-Part1' = m
        mode: Import        
        source =
            let
                …
            in
                #"Filtered Rows1"

    measure 'Sales Amount' = SUMX('Sales', [Quantity] * [Net Price])
        formatString: $ #,##0

    measure 'Sales Amount (Computers)' = CALCULATE([Sales Amount], 'Product'[Category] = "Computers")
        formatString: $ #,##0

为了获得更好的体验,可以安装 Visual Studio Code TMDL 语言扩展

部署 TMDL 模型表示形式

以下代码示例演示如何将模型的 TMDL 模型表示形式部署到 Power BI Premium 工作区:

var xmlaServer = "<Workspace XMLA address>";

var tmdlFolderPath = $"{System.Environment.CurrentDirectory}\\Contoso-tmdl";

var model = Microsoft.AnalysisServices.Tabular.TmdlSerializer.DeserializeModelFromFolder(tmdlFolderPath);            

using (var server = new Microsoft.AnalysisServices.Tabular.Server())
{
    server.Connect(xmlaServer);

    using (var remoteDatabase = server.Databases[model.Database.ID])
    {
        model.CopyTo(remoteDatabase.Model);

        remoteDatabase.Model.SaveChanges();
    }               
}

执行后,新度量值将部署到模型。

数据集 销售额(计算机)度量值

处理 TMDL 序列化错误

当在 TMDL 序列化方法中检测到错误时,除了引发一些常见的 .NET 异常(如 ArgumentExceptionInvalidOperationException)外,还会返回特定于 TMDL 的异常。

  • 如果 TMDL 文本不是有效的语法,则会引发 TmdlFormatException。 例如,无效的关键字或缩进。

  • 如果 TMDL 文本有效,但违反了 TOM 元数据逻辑,则会引发 TmdlSerializationException。 例如,值类型与预期类型不匹配。

除了异常详细信息外,还包括以下内容:

  • document path:TMDL 文件的路径,并出现错误。
  • line number:有错误的行号。
  • line text:带错误的行文本。

处理 TmdlFormatException的代码示例:

try
{
    var tmdlPath = "<TMDL Folder Path>";

    var model = Microsoft.AnalysisServices.Tabular.TmdlSerializer.DeserializeDatabaseFromFolder(tmdlPath);
}
catch (Microsoft.AnalysisServices.Tabular.Tmdl.TmdlFormatException ex)
{
    Console.WriteLine($"Error on Deserializing TMDL '{ex.Message}', document path: '{ex.Document}'  line number: '{ex.Line}', line text: '{ex.LineText}'");

    throw;
}    

对象文本序列化

下面的代码示例演示如何将列序列化为 TMDL:


var output = Microsoft.AnalysisServices.Tabular.TmdlSerializer.SerializeObject(model.Tables["Product"].Columns["ProductKey"], qualifyObject: true);

Console.WriteLine(output);

输出:

ref table Product

 column ProductKey
  dataType: int64
  isKey
  formatString: 0
  isAvailableInMdx: false
  lineageTag: 4184d53e-cd2d-4cbe-b8cb-04c72a750bc4
  summarizeBy: none
  sourceColumn: ProductKey

  annotation SummarizationSetBy = Automatic

流序列化

下面的代码示例演示如何将语义模型序列化为单个文本变量:

var output = new StringBuilder();

foreach (Microsoft.AnalysisServices.Tabular.Serialization.MetadataDocument document in model.ToTmdl())
{
    using (TextWriter writer = new StringWriter(output))
    {
        document.WriteTo(writer);
    }
}

Console.WriteLine(output.ToString());

下面的代码示例演示如何从 TMDL 反序列化,不包括角色:

var context = Microsoft.AnalysisServices.Tabular.Serialization.MetadataSerializationContext.Create(MetadataSerializationStyle.Tmdl);

var files = Directory.GetFiles("[TMDL Directory Path]", "*.tmdl", SearchOption.AllDirectories);

foreach (var file in files)
{
    if (file.Contains("/roles/"))
        continue;

    using (TextReader reader = File.OpenText(file))
    {                    
        context.ReadFromDocument(file, reader);
    }
}

var model = context.ToModel();