Convertir de modo Import a Direct Lake en OneLake
Objetivo del script
Este script convierte tablas en modo Import a Direct Lake en OneLake (DL/OL). Tal y como se indica en el artículo de orientación sobre Direct Lake, debemos reemplazar la partición (o particiones) de esas tablas por una única EntityPartition, que especifica el nombre y el esquema de la tabla o vista materializada en el Lakehouse o Warehouse de Fabric, mientras hace referencia a una expresión compartida que usa el conector AzureStorage.DataLake (OneLake).
Requisitos previos
Necesitará el Workspace ID, así como el Resource ID de su Warehouse o Lakehouse de Fabric. Ambos son GUID que forman parte de la URL al navegar al Warehouse o Lakehouse en el portal de Fabric:

En la captura de pantalla anterior, el Workspace ID del Lakehouse se resalta en azul, mientras que el Resource ID se resalta en verde.
Si se conecta a un Warehouse de Fabric o a un Lakehouse que admita esquemas, también deberá conocer el Schema de la tabla/vista materializada a la que desea conectarse.
Warning
Las tablas en modo Import pueden definir transformaciones dentro de sus particiones (expresadas mediante SQL o M). Estas transformaciones se perderán al convertir a Direct Lake en OneLake, ya que las particiones de Direct Lake deben contener una asignación 1:1 de las columnas de la tabla/vista materializada de origen. Por lo tanto, asegúrese de que la tabla/vista materializada de origen tenga el mismo nombre en el Warehouse o Lakehouse de Fabric que en el modelo semántico, y de que las asignaciones de columnas sean correctas antes de ejecutar este script.
Script
Convertir tablas en modo Import a Direct Lake en OneLake
// ==================================================================
// Convert Import to Direct Lake on OneLake
// ----------------------------------------
//
// This script converts the selected (import) tables, or all tables
// in the model, if nothing is selected, to Direct Lake on OneLake
// tables.
//
// WARNING: The script assumes that tables have the same name in the
// Fabric Warehouse or Lakehouse, as they do in the semantic model.
// Moreover, any transformations (M or SQL based) in the import
// partitions, will be lost, as Direct Lake mode tables must contain
// 1:1 the same columns as the source table/materialized view.
//
// You will need the Workspace ID and the ID of your Fabric Warehouse
// or Lakehouse (both are GUIDs).
// ==================================================================
// Find the Shared Expression that is being used by EntityPartitions on the model:
using System.Windows.Forms;
using System.Drawing;
using System.Data;
IEnumerable<Table> tableSource = Selected.Context.HasFlag(Context.Tables) ? (IEnumerable<Table>)Selected.Tables : Model.Tables;
var importTables = tableSource.Where(t => t.Partitions.All(p =>
(p.SourceType == PartitionSourceType.Query || p.SourceType == PartitionSourceType.M) &&
(p.Mode == ModeType.Import || (p.Mode == ModeType.Default && Model.DefaultMode == ModeType.Import))))
.ToList();
WaitFormVisible = false;
Application.UseWaitCursor = false;
if(importTables.Count == 0)
{
Warning("Model or selection does not contain any tables in import mode");
return;
}
else
{
var result = MessageBox.Show("The following tables will be converted:\r\n\r\n" + string.Join("\r\n", importTables.Select(t => " - " + t.Name)) +
"\r\n\r\nProceed?",
"Confirm conversion?", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
if (result == DialogResult.Cancel) return;
}
string workspaceId = string.Empty;
string resourceId = string.Empty;
var sharedExpression = Model.Expressions.FirstOrDefault(e => e.Expression.Contains("AzureStorage.DataLake"));
if(sharedExpression != null)
{
// Extract existing workspace ID and resource ID
var ix = sharedExpression.Expression.IndexOf("onelake.dfs.fabric.microsoft.com");
var url = sharedExpression.Expression.Substring(ix + 33, 73);
var guids = url.Split('/');
Guid g;
if(Guid.TryParse(guids[0], out g) && Guid.TryParse(guids[1], out g))
{
workspaceId = guids[0];
resourceId = guids[1];
}
}
var promptDialog = new UrlNameDialog(workspaceId, resourceId);
if(promptDialog.ShowDialog() == DialogResult.Cancel) return;
const string mTemplate = @"let
Source = AzureStorage.DataLake(""https://onelake.dfs.fabric.microsoft.com/%workspaceId%/%resourceId%"", [HierarchicalNavigation=true])
in
Source";
if(promptDialog.WorkspaceId.Text != workspaceId || promptDialog.ResourceId.Text != resourceId)
{
if (sharedExpression == null) sharedExpression = Model.AddExpression("DatabaseQuery");
sharedExpression.Expression = mTemplate.Replace("%workspaceId%", promptDialog.WorkspaceId.Text).Replace("%resourceId%", promptDialog.ResourceId.Text);
}
foreach(var table in importTables)
{
var ep = table.AddEntityPartition();
ep.EntityName = table.Name;
ep.ExpressionSource = sharedExpression;
ep.SchemaName = promptDialog.Schema.Text;
ep.Mode = ModeType.DirectLake;
foreach(var p in table.Partitions.ToList()) if(p != ep) p.Delete();
ep.Name = table.Name;
}
Info("Tables converted to Direct Lake on OneLake mode.");
public class UrlNameDialog : Form
{
public TextBox WorkspaceId { get; private set; }
public TextBox ResourceId { get; private set; }
public TextBox Schema { get; private set; }
private Button okButton;
public UrlNameDialog(string workspaceId, string resourceId)
{
Text = "Convert Direct Lake on SQL to OneLake";
AutoSize = true;
AutoSizeMode = AutoSizeMode.GrowAndShrink;
StartPosition = FormStartPosition.CenterParent;
Padding = new Padding(20);
var mainLayout = new TableLayoutPanel
{
ColumnCount = 1,
RowCount = 3,
Dock = DockStyle.Fill,
AutoSize = true,
AutoSizeMode = AutoSizeMode.GrowAndShrink
};
Controls.Add(mainLayout);
// Workspace ID
mainLayout.Controls.Add(new Label { Text = "Workspace ID (GUID):", AutoSize = true });
WorkspaceId = new TextBox { Width = 1000, Text = workspaceId };
mainLayout.Controls.Add(WorkspaceId);
// Resource ID
mainLayout.Controls.Add(new Label { Text = "Fabric Warehouse / Lakehouse ID (GUID):", AutoSize = true, Padding = new Padding(0, 20, 0, 0) });
ResourceId = new TextBox { Width = 1000, Text = resourceId };
mainLayout.Controls.Add(ResourceId);
// Schema
mainLayout.Controls.Add(new Label { Text = "Schema (optional):", AutoSize = true, Padding = new Padding(0, 20, 0, 0) });
Schema = new TextBox { Width = 1000 };
mainLayout.Controls.Add(Schema);
// Buttons
var buttonPanel = new FlowLayoutPanel
{
Padding = new Padding(0, 20, 0, 0),
FlowDirection = FlowDirection.RightToLeft,
Dock = DockStyle.Fill,
AutoSize = true
};
okButton = new Button { Text = "OK", DialogResult = DialogResult.OK, AutoSize = true, Enabled = false };
var cancelButton = new Button { Text = "Cancel", DialogResult = DialogResult.Cancel, AutoSize = true };
buttonPanel.Controls.Add(okButton);
buttonPanel.Controls.Add(cancelButton);
AcceptButton = okButton;
CancelButton = cancelButton;
mainLayout.Controls.Add(buttonPanel);
WorkspaceId.TextChanged += Validate;
ResourceId.TextChanged += Validate;
this.Shown += Validate;
}
private void Validate(object sender, EventArgs e)
{
Guid g;
okButton.Enabled = Guid.TryParse(WorkspaceId.Text, out g) && Guid.TryParse(ResourceId.Text, out g);
}
}
Explicación
El script determina primero si debe convertir todas las tablas en modo Import del modelo o solo las que el usuario haya seleccionado. A continuación, comprueba si existe alguna tabla de este tipo y solicita confirmación al usuario antes de continuar.
A continuación, el script intenta localizar una expresión compartida que use el conector AzureStorage.DataLake. Si existe una expresión de este tipo, extrae el ID del Workspace y el ID del recurso de su expresión. Si no se encuentra ninguna expresión de ese tipo, crea una nueva.
A continuación, se pide al usuario que introduzca el ID del Workspace y el ID del recurso del Warehouse o Lakehouse de Fabric, así como un nombre de esquema opcional. El script reemplaza la expresión compartida existente por una nueva que use los ID proporcionados, si se han modificado.
Por último, para cada tabla de Import mode, el script crea una nueva EntityPartition con el nombre y el esquema especificados, haciendo referencia a la Shared Expression. Luego elimina cualquier partición existente en la tabla que no sea la EntityPartition recién creada.