10/23/2021 Admin

Create View and Edit Power BI Reports In Blazor


image

You can easily create, view, and edit embedded Power BI reports in a Microsoft Server Side Blazor Application.

Microsoft Blazor is a technology that lets you build interactive web applications using C#.

Power BI allows you to create enterprise business intelligence (BI) dashboards and reports. This article demonstrates a method that allows you to display, edit, and create Power BI reports without requiring the user to have a Power BI account.

Note: On 10/22/2021 this tutorial was updated to use the Microsoft Authentication Library (MSAL) rather than the deprecated Authentication Library (ADAL) See: Update your applications to use Microsoft Authentication Library and Microsoft Graph API - Microsoft Tech Community

 

image

In the sample application, available on the Downloads page of this site, you can select View Report from the menu, then select a Workspace and a Report in the selected Workspace.

 

image

The selected Power BI report will display.

 

image

Selecting Edit Report will allow you to select a Power BI report, but this time the report will be in Edit mode.

 

image

Changes can be made to the report and those changes can be saved in the original Power BI report or as a new Power BI report (in the same Workspace).

 

image

Finally, the Create Report link allows you to choose a Workspace, an existing Dataset, and create a new Power BI report.

 

Set-Up

image

To set up the sample application, we follow the directions in this article: Register an app to embed Power BI content in a Power BI embedded analytics application - Power BI | Microsoft Docs.

 

image

When configuring the required application in Portal.Azure.com ensure the following permissions are set:

  • Microsoft Graph (Delegated)
    • User.Read
  • Power BI Service (Delegated)
    • Capacity.ReadWrite.All
    • Content.Create
    • Dashboard.ReadWrite.All
    • Dataset.ReadWrite.All
    • Report.ReadWrite.All
    • Workspace.ReadWrite.All

Also ensure that you click the Grant admin consent button after adding the permissions.

 

image

Download the code and open it in Visual Studio.

Open the appsetiing.json file and fill in values for:

 

View A Report

image

When you click on the View Report link, the Workspaces dropdown is populated with all the Workspaces that the configured user account has access to in Power BI.

The following code runs in the Razor page:

 

    protected override async Task OnInitializedAsync()
    {
        ShowProgressBar = true;
        StateHasChanged();
        var result = await @PowerBIService.GetWorkspacesAsync();
        workspacesOptions.Add(new DropdownOptions()
        {
            OptionName = "Select...",
            OptionValue = "0"
        });
        foreach (var workspace in result.workspacesList)
        {
            workspacesOptions.Add(
            new DropdownOptions()
            {
                OptionName = workspace.WorkspaceName,
                OptionValue = workspace.Id
            });
        }
        SelectedWorkspaceId = "0";
        ShowProgressBar = false;
        StateHasChanged();
    }

 

This code calls the following GetWorkspacesAsync() method:

 

        public async Task<GetWorkspacesResults> GetWorkspacesAsync()
        {
            GetWorkspacesResults groupsResults = new GetWorkspacesResults();
            groupsResults.totalRows = 0;
            List<DTOWorkspaces> colWorkspaces = new List<DTOWorkspaces>();
            string accessToken = GetPowerBIAccessToken();
            var tokenCredentials = new TokenCredentials(accessToken, "Bearer");
            using (var client = new PowerBIClient(
                new Uri("https://api.powerbi.com/"), tokenCredentials))
            {
                Groups groups = await client.Groups.GetGroupsAsync();
                if (groups.Value.Count() > 0)
                {
                    foreach (Group workspace in groups.Value)
                    {
                        DTOWorkspaces objWorkspace = new DTOWorkspaces();
                        objWorkspace.Id = workspace.Id.ToString();
                        objWorkspace.WorkspaceName = workspace.Name;
                        objWorkspace.Type = workspace.Type;
                        colWorkspaces.Add(objWorkspace);
                    }
                }
                colWorkspaces = colWorkspaces.OrderBy(x => x.WorkspaceName).ToList();
                groupsResults.workspacesList = colWorkspaces;
                groupsResults.totalRows = colWorkspaces.Count();
            }
            return groupsResults;
        }

 

That method calls this method to retrieve an Auth token to make the call to get the Workspaces:

 

        private const string AuthorityFormat = "https://login.microsoftonline.com/{0}/v2.0";
        private const string MSGraphScope = "https://analysis.windows.net/powerbi/api/.default";
        public string GetPowerBIAccessToken()
        {
            var TenantId = _PowerBISettings.TenantId;
            IConfidentialClientApplication daemonClient;
            daemonClient = ConfidentialClientApplicationBuilder.Create(_PowerBISettings.ApplicationId)
                .WithAuthority(string.Format(AuthorityFormat, _PowerBISettings.TenantId))
                .WithClientSecret(_PowerBISettings.ApplicationSecret)
                .Build();
            AuthenticationResult authResult =
                 daemonClient.AcquireTokenForClient(new[] { MSGraphScope }).ExecuteAsync().Result;
            return authResult.AccessToken;
        }

 

This fills the Workspaces dropdown on the .razor page:

 

<div class="row">
    <div class="col-md-1">
        <label class="control-label">Workspaces:</label>
    </div>
    <div class="col-md-11">
        <RadzenDropDown AllowClear="true"
                        Placeholder="Select..."
                        Data="workspacesOptions"
                        TextProperty="OptionName"
                        ValueProperty="OptionValue"
                        class="form-control"
                        @bind-Value="SelectedWorkspaceId"
                        Change="@(args => WorkspaceSelected(args))">
        </RadzenDropDown>
    </div>
</div>

 

When a Workspace is selected in the dropdown, the WorkspaceSelected(args) method is called:

 

    public async Task WorkspaceSelected(object value)
    {
        var workspaceID = value is IEnumerable<Object> ? 
            string.Join(", ", (IEnumerable<Object>)value) : value;
        reportsOptions = new List<DropdownOptions>();
        if (workspaceID != null)
        {
            ShowProgressBar = true;
            StateHasChanged();
            GetReportsResults result = 
                await @PowerBIService.GetReportsAsync(workspaceID.ToString());
            reportsOptions.Add(new DropdownOptions() { 
                OptionName = "Select...", OptionValue = "0" });
            foreach (var report in result.reportsList)
            {
                reportsOptions.Add(
                    new DropdownOptions() { 
                        OptionName = report.Reportname, 
                        OptionValue = report.Id });
            }
            SelectedReportId = "0";
            ShowProgressBar = false;
            StateHasChanged();
        }
    }

 

This code calls the following GetReportsAsync method:

 

        public async Task<GetReportsResults> GetReportsAsync(string workspaceID)
        {
            GetReportsResults reportsResults = new GetReportsResults();
            reportsResults.totalRows = 0;
            List<DTOReports> colReports = new List<DTOReports>();
            string accessToken = GetPowerBIAccessToken();
            var tokenCredentials = new TokenCredentials(accessToken, "Bearer");
            using (var client = new PowerBIClient(
                new Uri("https://api.powerbi.com/"), tokenCredentials))
            {
                Reports reports = await client.Reports.GetReportsInGroupAsync(new Guid(workspaceID));
                if (reports.Value.Count() > 0)
                {
                    foreach (Report report in reports.Value)
                    {
                        if (report.ReportType == "PowerBIReport")
                        {
                            DTOReports objReport = new DTOReports();
                            objReport.Id = report.Id.ToString();
                            objReport.Reportname = report.Name;
                            colReports.Add(objReport);
                        }
                    }
                }
                colReports = colReports.OrderBy(x => x.Reportname).ToList();
                reportsResults.reportsList = colReports;
                reportsResults.totalRows = colReports.Count();
            }
            return reportsResults;
        }

 

This populates the Reports dropdown:

 

<div class="row">
    <div class="col-md-1">
        <label class="control-label">Reports:</label>
    </div>
    <div class="col-md-11">
        <RadzenDropDown AllowClear="true"
                        Placeholder="Select..."
                        Data="reportsOptions"
                        TextProperty="OptionName"
                        ValueProperty="OptionValue"
                        class="form-control"
                        @bind-Value="SelectedReportId"
                        Change="@(args => DisplayReportAsync())">
        </RadzenDropDown>
    </div>
</div>

 

When a Report is selected in the dropdown, the DisplayReportAsync() method is called:

 

    async void DisplayReportAsync()
    {
        var accessToken = await @PowerBIService.GetPowerBIAccessTokenAsync();
        var tokenCredentials = new TokenCredentials(accessToken, "Bearer");
        using (var client = new PowerBIClient(
            new Uri("https://api.powerbi.com/"), tokenCredentials))
        {
            var report =
            await client.Reports.GetReportInGroupAsync(
                new Guid(SelectedWorkspaceId),
                new Guid(SelectedReportId));
            var generateTokenRequestParameters =
                new GenerateTokenRequest(accessLevel: "view");
            var tokenResponse =
                await client.Reports.GenerateTokenAsync(
                    new Guid(SelectedWorkspaceId),
                    new Guid(SelectedReportId),
                    generateTokenRequestParameters);
            await Interop.ShowReport(
                JSRuntime,
                PowerBIElement,
                tokenResponse.Token,
                report.EmbedUrl,
                report.Id.ToString());
        }
    }

 

This uses Blazor JavaScript interop to invoke the following JavaScript that will display the Power BI report:

 

    showReport: function (reportContainer, accessToken, embedUrl, embedReportId) {
        // Get models. models contains enums that can be used.
        var models = window['powerbi-client'].models;
        // Embed configuration used to describe the what and how to embed.
        // This object is used when calling powerbi.embed.
        // This also includes settings and options such as filters.
        // You can find more information at
        // https://bit.ly/3vZO703
        var config = {
            type: 'report',
            tokenType: models.TokenType.Embed,
            accessToken: accessToken,
            embedUrl: embedUrl,
            id: embedReportId,
            permissions: models.Permissions.All,
            settings: {
                filterPaneEnabled: true,
                navContentPaneEnabled: true
            }
        };
        // Embed the report and display it within the div container.
        powerbi.embed(reportContainer, config);
    }

 

Edit A Report

image

The code for the Edit Report page is basically the same as the View Report page, except that the GenerateTokenRequest method specifies edit as the accesslevel and enables the Save As ability:

 

    var generateTokenRequestParameters =
        new GenerateTokenRequest(accessLevel: "edit", allowSaveAs: true);

 

Also, the JavaScript sets the viewMode to Edit (using the models.ViewMode object):

 

    editReport: function (reportContainer, accessToken, embedUrl, embedReportId) {
        // Get models. models contains enums that can be used.
        var models = window['powerbi-client'].models;
        // Embed configuration used to describe the what and how to embed.
        // This object is used when calling powerbi.embed.
        // This also includes settings and options such as filters.
        // You can find more information at
        // https://bit.ly/3vZO703
        var config = {
            type: 'report',
            tokenType: models.TokenType.Embed,
            accessToken: accessToken,
            embedUrl: embedUrl,
            id: embedReportId,
            permissions: models.Permissions.All,
            viewMode: models.ViewMode.Edit,
            settings: {
                filterPaneEnabled: true,
                navContentPaneEnabled: true
            }
        };
        // Embed the report and display it within the div container.
        powerbi.embed(reportContainer, config);
    }

 

Create A Report

image

The Create Report page requires you to first select an existing Dataset after selecting a Workspace (rather than selecting a Report).

The code to get the Datasets for the selected Workspace is as follows:

 

        public async Task<GetDatasetsResults> GetDatasetsAsync(string workspaceID)
        {
            GetDatasetsResults datasetsResults = new GetDatasetsResults();
            datasetsResults.totalRows = 0;
            List<DTODatasets> colDatasets = new List<DTODatasets>();
            string accessToken = GetPowerBIAccessToken();
            var tokenCredentials = new TokenCredentials(accessToken, "Bearer");
            using (var client = new PowerBIClient(
                new Uri("https://api.powerbi.com/"), tokenCredentials))
            {
                Datasets datasets = await client.Datasets.GetDatasetsInGroupAsync(new Guid(workspaceID));
                if (datasets.Value.Count() > 0)
                {
                    foreach (Dataset dataset in datasets.Value)
                    {
                        DTODatasets objDataset = new DTODatasets();
                        objDataset.Id = dataset.Id;
                        objDataset.Datasetname = dataset.Name;
                        objDataset.CreateReportEmbedURL = dataset.CreateReportEmbedURL;
                        colDatasets.Add(objDataset);
                    }
                }
                colDatasets = colDatasets.OrderBy(x => x.Datasetname).ToList();
                datasetsResults.datasetsList = colDatasets;
                datasetsResults.totalRows = colDatasets.Count();
            }
            return datasetsResults;
        }

 

Notice that this returns a CreateReportEmbedURL for each Dataset.

This is required for the DisplayReportAsync() method. That method needs to pass the CreateReportEmbedURL and the SelectedDatasetId to the JavaScript:

 

    async void DisplayReportAsync()
    {
        var accessToken = await @PowerBIService.GetPowerBIAccessTokenAsync();
        var tokenCredentials = new TokenCredentials(accessToken, "Bearer");
        using (var client = new PowerBIClient(
            new Uri("https://api.powerbi.com/"), tokenCredentials))
        {
            var generateTokenRequestParameters =
            new GenerateTokenRequest(
                accessLevel: TokenAccessLevel.Create,
                datasetId: SelectedDatasetId,
                allowSaveAs: true);
            // For help see: https://bit.ly/2TbYjFl
            var tokenResponse =
                await client.Reports.GenerateTokenForCreateAsync(
                    new Guid(SelectedWorkspaceId),
                    generateTokenRequestParameters);
            string CreateReportEmbedURL =
                colDatasets.Where(x => x.Id == SelectedDatasetId)
                .FirstOrDefault().CreateReportEmbedURL;
            await Interop.CreateReport(
                JSRuntime,
                PowerBIElement,
                tokenResponse.Token,
                CreateReportEmbedURL,
                SelectedDatasetId);
        }
    }

 

image

The JavaScript uses the values to display the blank Power BI report with the dataset:

 

createReport: function (reportContainer, accessToken, embedUrl, datasetId) {
        // Get models. models contains enums that can be used.
        var models = window['powerbi-client'].models;
        // Embed configuration used to describe the what and how to embed.
        // This object is used when calling powerbi.embed.
        // This also includes settings and options such as filters.
        // You can find more information at
        // https://bit.ly/3vZO703
        // https://bit.ly/3wVBUuB
        var config = {
            tokenType: models.TokenType.Embed,
            accessToken: accessToken,
            embedUrl: embedUrl,
            datasetId: datasetId,
            settings: {
                filterPaneEnabled: true,
                navContentPaneEnabled: true
            }
        };
        // Create report
        powerbi.createReport(reportContainer, config);
    }

 

 

If You Have Problems

If you have problems, see this Microsoft article for more details: Troubleshooting your Power BI embedded analytics application - Power BI | Microsoft Docs

 

Links

Embedding Power BI in Blazor

Blazor Power BI Paginated Reports

Power BI embedded analytics Client APIs | Microsoft Docs

Power BI Playground

 

Download

The project is available on the Downloads page on this site.

You must have Visual Studio 2019 Version 16.3 (or higher) installed to run the code.

An unhandled error has occurred. Reload 🗙