6/28/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.

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.
(Note: This will not display Power BI Paginated Reports, for Power BI Paginated Reports see: Blazor Power BI Paginated Reports (blazorhelpwebsite.com)).

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.

Use the embed for your customers solution, also known as app owns data, and a Master user account.

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:

  • TenantId – Your Azure Tenant ID
  • ApplicationId – Created in the earlier step
  • ApplicationSecret – Created in the earlier step
  • UserNameUsername of a user that has Admin access to the Power BI workspaces and a Power BI Pro license
  • PasswordPassword of a user that has Admin access to the Power BI workspaces and a Power BI Pro license

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
									Task<GetWorkspacesResults> GetWorkspacesAsync()
								
									{
								
									GetWorkspacesResults groupsResults =
									new
									GetWorkspacesResults();
								
									groupsResults.totalRows = 0;
								

								
									List<DTOWorkspaces> colWorkspaces =
									new
									List<DTOWorkspaces>();
								

								
									string
									accessToken = GetPowerBIAccessToken().Result;
								
									var tokenCredentials =
									new
									TokenCredentials(accessToken, "Bearer");
								

								
									using
									(var client =
									new
									PowerBIClient(new
									Uri(_PowerBISettings.ApiUrl), tokenCredentials))
								
									{
								
									Groups groups = client.Groups.GetGroupsAsync().Result;
								

								
									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
									Task.FromResult(groupsResults);
								
									}

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

								
									public
									async Task<string> GetPowerBIAccessToken()
								
									{
								
									var TenantId = _PowerBISettings.TenantId;
								

								
									var AuthorityUrl =
								
									$@"https://login.microsoftonline.com/{TenantId}/oauth2/token";
								

								
									using
									(var client =
									new
									HttpClient())
								
									{
								
									var form =
									new
									Dictionary<string,
									string>();
								
									form["grant_type"] = "password";
								
									form["resource"] = _PowerBISettings.ResourceUrl;
								
									form["username"] = _PowerBISettings.UserName;
								
									form["password"] = _PowerBISettings.Password;
								
									form["client_id"] = _PowerBISettings.ApplicationId.ToString();
								
									form["client_secret"] = _PowerBISettings.ApplicationSecret;
								
									form["scope"] = "openid";
								

								
									client.DefaultRequestHeaders.TryAddWithoutValidation(
								
									"Content-Type", "application/x-www-form-urlencoded");
								

								
									using
									(var formContent =
									new
									FormUrlEncodedContent(form))
								

								
									using
									(var response = client.PostAsync(AuthorityUrl, formContent).Result)
								
									{
								
									var body = await response.Content.ReadAsStringAsync();
								
									var jsonBody = JObject.Parse(body);
								

								
									var errorToken = jsonBody.SelectToken("error");
								
									if
									(errorToken !=
									null)
								
									{
								
									throw
									new
									Exception(errorToken.Value<string>());
								
									}
								

								
									return
									jsonBody.SelectToken("access_token").Value<string>();
								
									}
								
									}
								
									}

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
									Task<GetReportsResults> GetReportsAsync(string
									workspaceID)
								
									{
								
									GetReportsResults reportsResults =
									new
									GetReportsResults();
								
									reportsResults.totalRows = 0;
								

								
									List<DTOReports> colReports =
									new
									List<DTOReports>();
								

								
									string
									accessToken = GetPowerBIAccessToken().Result;
								
									var tokenCredentials =
									new
									TokenCredentials(accessToken, "Bearer");
								

								
									using
									(var client =
									new
									PowerBIClient(new
									Uri(_PowerBISettings.ApiUrl), tokenCredentials))
								
									{
								
									Reports reports = client.Reports.GetReportsInGroupAsync(new
									Guid(workspaceID)).Result;
								

								
									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
									Task.FromResult(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.GetPowerBIAccessToken();
								
									var tokenCredentials =
									new
									TokenCredentials(accessToken, "Bearer");
								

								
									using
									(var client =
									new
									PowerBIClient(
								
									new
									Uri(PowerBISettings.ApiUrl),
								
									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
									Task<GetDatasetsResults> GetDatasetsAsync(string
									workspaceID)
								
{
								
									GetDatasetsResults datasetsResults =
									new
									GetDatasetsResults();
								
									datasetsResults.totalRows = 0;
								

								
									List<DTODatasets> colDatasets =
									new
									List<DTODatasets>();
								

								
									string
									accessToken = GetPowerBIAccessToken().Result;
								
									var tokenCredentials =
									new
									TokenCredentials(accessToken, "Bearer");
								

								
									using
									(var client =
									new
									PowerBIClient(new
									Uri(_PowerBISettings.ApiUrl), tokenCredentials))
								
									{
								
									Datasets datasets = client.Datasets.GetDatasetsInGroupAsync(new
									Guid(workspaceID)).Result;
								

								
									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
									Task.FromResult(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.GetPowerBIAccessToken();
								
									var tokenCredentials =
									new
									TokenCredentials(accessToken, "Bearer");
								

								
									using
									(var client =
									new
									PowerBIClient(
								
									new
									Uri(PowerBISettings.ApiUrl),
								
									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

Power BI embedded analytics Client APIs | Microsoft Docs

Power BI Playground

Embedding Power BI in Blazor

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 error has occurred. This application may no longer respond until reloaded. Reload 🗙