Мульти-аренда в Power BI Embedded - PullRequest
0 голосов
/ 30 октября 2019

У меня мультитенантное веб-приложение, и я использую подход базы данных для каждого арендатора. Веб-приложение также будет использовать Power BI Embedded для отображения отчетов, основанных на данных для этого конкретного арендатора, и все отчеты для каждого арендатора будут иметь одинаковый формат, но источник данных будет отличаться.

От того, что явидно, что в Power BI нет простого способа реализации мультитенантности, например, передачи источника данных в качестве параметра. Мне удалось найти два способа, как сделать Power BI встроенным мультитенантным. Либо используйте защиту на уровне строк, что означало бы, что мне нужно иметь единое хранилище данных для всех данных арендатора, и это не вариант для меня. Другой вариант будет иметь рабочее пространство для каждого арендатора.

Для второго варианта у меня будет рабочее пространство шаблона, из которого будет создаваться копия для каждого нового арендатора. В этом руководстве описывается, как это сделать: https://powerbi.microsoft.com/fr-fr/blog/duplicate-workspaces-using-the-power-bi-rest-apis-a-step-by-step-tutorial/.

Можно ли сделать то же самое с помощью Power BI C # SDK? Мне также необходимо изменить источник данных, используемый для каждой рабочей области. Как я могу сделать это для всех отчетов в моей рабочей области?

Наконец, кто-то нашел более простой способ реализации мультитенантности со встроенной Power BI или это все?

1 Ответ

1 голос
/ 30 октября 2019

Это зависит от типа источника данных (SQL Server, SSAS, CSV-файлы и т. Д.) И режима подключения к данным (импорт, прямой запрос и т. Д.). Если вы можете использовать параметры, то один из вариантов - разрешить вновь клонированному отчету самостоятельно переключать свой источник данных, используя параметры подключения . Для этого откройте Power Query Editor, нажав Edit Queries и в Manage Parameters определите два новых текстовых параметра, назовите их ServerName и DatabaseName:

enter image description here

enter image description here

Установите их текущие значения так, чтобы они указывали на один из ваших источников данных, например, SQLSERVER2016 и AdventureWorks2016. Затем щелкните правой кнопкой мыши ваш запрос в отчете и откройте Advanced Editor. Найдите имя сервера и имя базы данных в M-коде:

enter image description here

и замените их параметрами, определенными выше, так что M-код будет выглядеть следующим образом:

enter image description here

Теперь вы можете закрывать и применять изменения, и ваш отчет должен работать как прежде. Но теперь, когда вы хотите изменить источник данных, сделайте это, используя Edit Parameters:

enter image description here

и измените имя сервера и / или базы данных на точкук другому источнику данных, который вы хотите использовать для отчета:

enter image description here

После изменения значений параметров Power BI Desktop попросит вас применитьизменения и перезагрузите данные из нового источника данных. Чтобы изменить значения параметров (т. Е. Источник данных) отчета, опубликованного в Power BI Service, перейдите в настройки набора данных и введите имя нового сервера и / или базы данных (проверьте также настройки шлюза, если это локальный источник данных):

enter image description here

После изменения источника данных обновите набор данных, чтобы получить данные из нового источника данных. С учетной записью Power BI Pro вы можете делать это 8 раз в сутки, в то время как если набор данных находится в выделенной емкости, этот предел увеличивается до 48 раз в сутки.

Чтобы сделать это программно, используйте Update Parameters / Update Parameters In Group и Refresh Dataset / Refresh Dataset In Group вызовы API REST. Например, вы можете сделать это с помощью PowerShell следующим образом:

Import-Module MicrosoftPowerBIMgmt
Import-Module MicrosoftPowerBIMgmt.Profile

$password = "xxxxx" | ConvertTo-SecureString -asPlainText -Force
$username = "xxxxx@yyyyy.com" 
$credential = New-Object System.Management.Automation.PSCredential($username, $password)

Connect-PowerBIServiceAccount -Credential $credential

Invoke-PowerBIRestMethod -Url 'groups/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/datasets/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/UpdateParameters' -Method Post -Body '{
  "updateDetails": [
    {
      "name": "ServerName",
      "newValue": "SQLSERVER2019"
    },
    {
      "name": "DatabaseName",
      "newValue": "AdventureWorks2019"
    }
  ]
}'
Invoke-PowerBIRestMethod -Url 'groups/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/datasets/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/refreshes' -Method Post

Disconnect-PowerBIServiceAccount

Если вы не можете использовать параметры, например, прямое подключение к SSAS, строку подключения можно изменить с помощью Обновить источники данных в группе вызов API REST. В PowerShell это можно сделать следующим образом:

Import-Module MicrosoftPowerBIMgmt
Import-Module MicrosoftPowerBIMgmt.Profile

$password = "xxxxx" | ConvertTo-SecureString -asPlainText -Force
$username = "xxxxx@yyyyy.com" 
$credential = New-Object System.Management.Automation.PSCredential($username, $password)

Connect-PowerBIServiceAccount -Credential $credential

Invoke-PowerBIRestMethod -Url 'groups/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/datasets/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Default.UpdateDatasources' -Method Post -Body '{
  "updateDetails": [
    {
      "datasourceSelector": {
        "datasourceType": "AnalysisServices",
        "connectionDetails": {
          "server": "My-As-Server",
          "database": "My-As-Database"
        }
      },
      "connectionDetails": {
        "server": "New-As-Server",
        "database": "New-As-Database"
      }
    }
  ]
}'

Disconnect-PowerBIServiceAccount

Обратите внимание, что вам необходимо предоставить как старые, так и новые имена серверов и баз данных.

В C # вы можете сделать то же самое в оченьАналогично, даже без клиента Power BI:

var group_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
var dataset_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);

var restUrlUpdateParameters = $"https://api.powerbi.com/v1.0/myorg/groups/{group_id}/datasets/{dataset_id}/Default.UpdateParameters";
var postData = new { updateDetails = new[] { new { name = "ServerName", newValue = "NEWSERVER" }, new { name = "DatabaseName", newValue = "Another_AdventureWorks2016" } } };
var responseUpdate = client.PostAsync(restUrlUpdateParameters, new StringContent(JsonConvert.SerializeObject(postData), Encoding.UTF8, "application/json")).Result;

var restUrlRefreshDataset = $"https://api.powerbi.com/v1.0/myorg/groups/{group_id}/datasets/{dataset_id}/refreshes";
var responseRefresh = client.PostAsync(restUrlRefreshDataset, null).Result;

Использование клиента Power BI C # может облегчить вам жизнь, например, обновление отчета может быть выполнено следующим образом:

var group_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
var dataset_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
var credentials = new TokenCredentials(accessToken, "Bearer");

using (var client = new PowerBIClient(new Uri("https://api.powerbi.com"), credentials))
{
    client.Datasets.RefreshDatasetInGroup(group_id, dataset_id);
}

КогдаДля вызова API необходимо предоставить токен доступа. Чтобы приобрести его, используйте библиотеки ADAL или MSAL , например, с таким кодом:

private static string resourceUri = "https://analysis.windows.net/powerbi/api";
private static string authorityUri = "https://login.windows.net/common/"; // It was https://login.windows.net/common/oauth2/authorize in prior versions
private static string clientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; // Register at https://dev.powerbi.com/apps
private static string groupId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
private static string reportId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";

private static AuthenticationContext authContext = new AuthenticationContext(authorityUri, new TokenCache());


public string Authenticate()
{
    AuthenticationResult authenticationResult = null;

    // First check is there token in the cache
    try
    {
        authenticationResult = authContext.AcquireTokenSilentAsync(resourceUri, clientId).Result;
    }
    catch (AggregateException ex)
    {
        AdalException ex2 = ex.InnerException as AdalException;
        if ((ex2 == null) || (ex2 != null && ex2.ErrorCode != "failed_to_acquire_token_silently"))
        {
            MessageBox.Show(ex.Message);
            return;
        }
    }

    if (authenticationResult == null)
    {
        var uc = new UserPasswordCredential("user@example.com, "Strong password");
        try
        {
            authenticationResult = authContext.AcquireTokenAsync(resourceUri, clientId, uc).Result;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message + ex.InnerException == null ? "" : Environment.NewLine + ex.InnerException.Message);
            return;
        }
    }

    if (authenticationResult == null)
        MessageBox.Show("Call failed.");
    else
    {
        return authenticationResult.AccessToken;
    }
}       
...