Embed Power BI in your product for your customers (.NET Framework Embed Report)
Initialize
- Install nuget Microsoft.PowerBI.Api.
- Install nuget Microsoft.PowerBI.JavaScript.
Models
- PBISetting.cs
public class PBISetting { public string ApplicationId { get; set; } public List<Guid> WorkspaceId { get; set; } public List<List<Guid>> ReportId { get; set; } public string AuthenticationType { get; set; } public string Username { get; set; } public string Password { get; set; } public string ApplicationSecret { get; set; } public string Tenant { get; set; } }
- EmbedReport.cs
public class EmbedReport { // Id of Power BI report to be embedded public Guid ReportId { get; set; } // Name of the report public string ReportName { get; set; } // Embed URL for the Power BI report public string EmbedUrl { get; set; } }
- ReportEmbedConfig.cs
public class ReportEmbedConfig { // Report to be embedded public List<EmbedReport> EmbedReports { get; set; } // Embed Token for the Power BI report public EmbedToken EmbedToken { get; set; } }
- ErrorModel.cs
public class ErrorModel { public string ErrorMessage { get; internal set; } }
Services
- AadService.cs
public class AadService { private static readonly string m_authorityUrl = "https://login.microsoftonline.com/organizations"; private static readonly string[] m_scope = "https://analysis.windows.net/powerbi/api/.default".Split(';'); /// <summary> /// Get Access token /// </summary> /// <returns>Access token</returns> [Obsolete] public static async Task<string> GetAccessToken(PBISetting setting) { AuthenticationResult authenticationResult = null; if (setting.AuthenticationType.Equals("masteruser", StringComparison.InvariantCultureIgnoreCase)) { IPublicClientApplication clientApp = PublicClientApplicationBuilder .Create(setting.ApplicationId) .WithAuthority(m_authorityUrl) .Build(); var userAccounts = await clientApp.GetAccountsAsync(); try { authenticationResult = await clientApp.AcquireTokenSilent(m_scope, userAccounts.FirstOrDefault()).ExecuteAsync(); } catch (MsalUiRequiredException) { SecureString secureStringPassword = new SecureString(); foreach (var key in setting.Password) { secureStringPassword.AppendChar(key); } authenticationResult = await clientApp.AcquireTokenByUsernamePassword(m_scope, setting.Username, secureStringPassword).ExecuteAsync(); } } // Service Principal auth is recommended by Microsoft to achieve App Owns Data Power BI embedding else if (setting.AuthenticationType.Equals("serviceprincipal", StringComparison.InvariantCultureIgnoreCase)) { // For app only authentication, we need the specific tenant id in the authority url var tenantSpecificURL = m_authorityUrl.Replace("organizations", setting.Tenant); IConfidentialClientApplication clientApp = ConfidentialClientApplicationBuilder .Create(setting.ApplicationId) .WithClientSecret(setting.ApplicationSecret) .WithAuthority(tenantSpecificURL) .Build(); authenticationResult = await clientApp.AcquireTokenForClient(m_scope).ExecuteAsync(); } return authenticationResult.AccessToken; } }
- PBIService.cs
public class PBIService { public static string CheckSettingError(PBISetting setting) { string message = null; Guid result; // Application Id must have a value. if (string.IsNullOrWhiteSpace(setting.ApplicationId)) { message = "ApplicationId is empty. please register your application as Native app in https://dev.powerbi.com/apps and fill client Id in setting."; } // Application Id must be a Guid object. else if (!Guid.TryParse(setting.ApplicationId, out result)) { message = "ApplicationId must be a Guid object. please register your application as Native app in https://dev.powerbi.com/apps and fill application Id in setting."; } // Workspace Id must have a value. else if (setting.WorkspaceId.Where(x => x == Guid.Empty).Count() > 0) { message = "WorkspaceId is empty or not a valid Guid. Please fill its Id correctly in setting"; } // Report Id must have a value. else if (setting.ReportId.Where(x => x.Where(y => y == Guid.Empty).Count() > 0).Count() > 0) { message = "ReportId is empty or not a valid Guid. Please fill its Id correctly in setting"; } else if (setting.AuthenticationType.Equals("masteruser", StringComparison.InvariantCultureIgnoreCase)) { // Username must have a value. if (string.IsNullOrWhiteSpace(setting.Username)) { message = "Username is empty. Please fill Power BI username in setting"; } // Password must have a value. if (string.IsNullOrWhiteSpace(setting.Password)) { message = "Password is empty. Please fill password of Power BI username in setting"; } } else if (setting.AuthenticationType.Equals("serviceprincipal", StringComparison.InvariantCultureIgnoreCase)) { if (string.IsNullOrWhiteSpace(setting.ApplicationSecret)) { message = "ApplicationSecret is empty. please register your application as Web app and fill appSecret in setting."; } // Must fill tenant Id else if (string.IsNullOrWhiteSpace(setting.Tenant)) { message = "Invalid Tenant. Please fill Tenant ID in Tenant under setting"; } } else { message = "Invalid authentication type"; } return message; } private static Guid GetParamGuid(string param) { Guid paramGuid = Guid.Empty; Guid.TryParse(param, out paramGuid); return paramGuid; } }
- EmbedService.cs
public static class EmbedService { private static readonly string urlPowerBiServiceApiRoot = "https://api.powerbi.com"; public static async Task<PowerBIClient> GetPowerBiClient(PBISetting setting) { var tokenCredentials = new TokenCredentials(await AadService.GetAccessToken(setting), "Bearer"); return new PowerBIClient(new Uri(urlPowerBiServiceApiRoot), tokenCredentials); } /// <summary> /// Get embed params for a report /// </summary> /// <returns>Wrapper object containing Embed token, Embed URL, Report Id, and Report name for single report</returns> public static async Task<ReportEmbedConfig> GetEmbedParams(PBISetting setting, Guid workspaceId, Guid reportId, [Optional] Guid additionalDatasetId) { using (var pbiClient = await GetPowerBiClient(setting)) { // Get report info var pbiReport = pbiClient.Reports.GetReportInGroup(workspaceId, reportId); /* Check if dataset is present for the corresponding report If no dataset is present then it is a RDL report */ bool isRDLReport = String.IsNullOrEmpty(pbiReport.DatasetId); EmbedToken embedToken; if (isRDLReport) { // Get Embed token for RDL Report embedToken = await GetEmbedTokenForRDLReport(setting, workspaceId, reportId); } else { // Create list of dataset var datasetIds = new List<Guid>(); // Add dataset associated to the report datasetIds.Add(Guid.Parse(pbiReport.DatasetId)); // Append additional dataset to the list to achieve dynamic binding later if (additionalDatasetId != Guid.Empty) { datasetIds.Add(additionalDatasetId); } // Get Embed token multiple resources embedToken = await GetEmbedToken(setting, reportId, datasetIds, workspaceId); } // Add report data for embedding var embedReports = new List<EmbedReport>() { new EmbedReport { ReportId = pbiReport.Id, ReportName = pbiReport.Name, EmbedUrl = pbiReport.EmbedUrl } }; // Capture embed params var embedParams = new ReportEmbedConfig { EmbedReports = embedReports, EmbedToken = embedToken }; return embedParams; } } /// <summary> /// Get embed params for multiple reports for a single workspace /// </summary> /// <returns>Wrapper object containing Embed token, Embed URL, Report Id, and Report name for multiple reports</returns> /// <remarks>This function is not supported for RDL Report</remakrs> public static async Task<ReportEmbedConfig> GetEmbedParams(PBISetting setting, Guid workspaceId, IList<Guid> reportIds, [Optional] IList<Guid> additionalDatasetIds) { // Note: This method is an example and is not consumed in this sample app using (var pbiClient = await GetPowerBiClient(setting)) { // Create mapping for reports and Embed URLs var embedReports = new List<EmbedReport>(); // Create list of datasets var datasetIds = new List<Guid>(); // Get datasets and Embed URLs for all the reports foreach (var reportId in reportIds) { // Get report info var pbiReport = pbiClient.Reports.GetReportInGroup(workspaceId, reportId); // Append to existing list of datasets to achieve dynamic binding later datasetIds.Add(Guid.Parse(pbiReport.DatasetId)); // Add report data for embedding embedReports.Add(new EmbedReport { ReportId = pbiReport.Id, ReportName = pbiReport.Name, EmbedUrl = pbiReport.EmbedUrl }); } // Append to existing list of datasets to achieve dynamic binding later if (additionalDatasetIds != null) { datasetIds.AddRange(additionalDatasetIds); } // Get Embed token multiple resources var embedToken = await GetEmbedToken(setting, reportIds, datasetIds, workspaceId); // Capture embed params var embedParams = new ReportEmbedConfig { EmbedReports = embedReports, EmbedToken = embedToken }; return embedParams; } } /// <summary> /// Get Embed token for single report, multiple datasets, and an optional target workspace /// </summary> /// <returns>Embed token</returns> /// <remarks>This function is not supported for RDL Report</remakrs> public static async Task<EmbedToken> GetEmbedToken(PBISetting setting, Guid reportId, IList<Guid> datasetIds, [Optional] Guid targetWorkspaceId) { using (var pbiClient = await GetPowerBiClient(setting)) { // Create a request for getting Embed token // This method works only with new Power BI V2 workspace experience var tokenRequest = new GenerateTokenRequestV2( reports: new List<GenerateTokenRequestV2Report>() { new GenerateTokenRequestV2Report(reportId) }, datasets: datasetIds.Select(datasetId => new GenerateTokenRequestV2Dataset(datasetId.ToString())).ToList(), targetWorkspaces: targetWorkspaceId != Guid.Empty ? new List<GenerateTokenRequestV2TargetWorkspace>() { new GenerateTokenRequestV2TargetWorkspace(targetWorkspaceId) } : null ); // Generate Embed token var embedToken = pbiClient.EmbedToken.GenerateToken(tokenRequest); return embedToken; } } /// <summary> /// Get Embed token for multiple reports, datasets, and an optional target workspace /// </summary> /// <returns>Embed token</returns> /// <remarks>This function is not supported for RDL Report</remakrs> public static async Task<EmbedToken> GetEmbedToken(PBISetting setting, IList<Guid> reportIds, IList<Guid> datasetIds, [Optional] Guid targetWorkspaceId) { // Note: This method is an example and is not consumed in this sample app using (var pbiClient = await GetPowerBiClient(setting)) { // Convert reports to required types var reports = reportIds.Select(reportId => new GenerateTokenRequestV2Report(reportId)).ToList(); // Convert datasets to required types var datasets = datasetIds.Select(datasetId => new GenerateTokenRequestV2Dataset(datasetId.ToString())).ToList(); // Create a request for getting Embed token // This method works only with new Power BI V2 workspace experience var tokenRequest = new GenerateTokenRequestV2( datasets: datasets, reports: reports, targetWorkspaces: targetWorkspaceId != Guid.Empty ? new List<GenerateTokenRequestV2TargetWorkspace>() { new GenerateTokenRequestV2TargetWorkspace(targetWorkspaceId) } : null ); // Generate Embed token var embedToken = pbiClient.EmbedToken.GenerateToken(tokenRequest); return embedToken; } } /// <summary> /// Get Embed token for multiple reports, datasets, and optional target workspaces /// </summary> /// <returns>Embed token</returns> /// <remarks>This function is not supported for RDL Report</remakrs> public static async Task<EmbedToken> GetEmbedToken(PBISetting setting, IList<Guid> reportIds, IList<Guid> datasetIds, [Optional] IList<Guid> targetWorkspaceIds) { // Note: This method is an example and is not consumed in this sample app using (var pbiClient = await GetPowerBiClient(setting)) { // Convert report Ids to required types var reports = reportIds.Select(reportId => new GenerateTokenRequestV2Report(reportId)).ToList(); // Convert dataset Ids to required types var datasets = datasetIds.Select(datasetId => new GenerateTokenRequestV2Dataset(datasetId.ToString())).ToList(); // Convert target workspace Ids to required types IList<GenerateTokenRequestV2TargetWorkspace> targetWorkspaces = null; if (targetWorkspaceIds != null) { targetWorkspaces = targetWorkspaceIds.Select(targetWorkspaceId => new GenerateTokenRequestV2TargetWorkspace(targetWorkspaceId)).ToList(); } // Create a request for getting Embed token // This method works only with new Power BI V2 workspace experience var tokenRequest = new GenerateTokenRequestV2( datasets: datasets, reports: reports, targetWorkspaces: targetWorkspaceIds != null ? targetWorkspaces : null ); // Generate Embed token var embedToken = pbiClient.EmbedToken.GenerateToken(tokenRequest); return embedToken; } } /// <summary> /// Get Embed token for RDL Report /// </summary> /// <returns>Embed token</returns> public static async Task<EmbedToken> GetEmbedTokenForRDLReport(PBISetting setting, Guid targetWorkspaceId, Guid reportId, string accessLevel = "view") { using (var pbiClient = await GetPowerBiClient(setting)) { // Generate token request for RDL Report var generateTokenRequestParameters = new GenerateTokenRequest( accessLevel: accessLevel ); // Generate Embed token var embedToken = pbiClient.Reports.GenerateTokenInGroup(targetWorkspaceId, reportId, generateTokenRequestParameters); return embedToken; } } }
Controllers
public class HomeController : Controller { private string m_errorMessage; [Obsolete] public async Task<ActionResult> Index() { //Get User UserModel user = new UserModel(); var users = crm.User.Where(x => x.IsActive == true).ToList(); user = users.Where(x => x.ID.Equals(SessionHelper.UserID)).FirstOrDefault(); if (user == null) { return RedirectToAction("Login", "Account"); } //Get ClientProfile ClientProfileModel client = new ClientProfileModel(); client = crm.ClientProfile.Where(x => x.ID.Equals(SessionHelper.ClientProfileID)).FirstOrDefault(); if (client == null) { return RedirectToAction("Login", "Account"); } //Get Workspaces List<PowerBIWorkspaceModel> workspace = new List<PowerBIWorkspaceModel>(); var workspaces = crm.PowerBIWorkspace.Where(x => x.IsActive == true).ToList(); if (user.PowerBIWorkspaceID != null) { workspace = workspaces.Where(x => user.PowerBIWorkspaceID.Split(',').ToList().Contains(x.ID.ToString())).ToList(); } //Get Embeded List<PowerBIEmbedModel> embeds = new List<PowerBIEmbedModel>(); List<List<Guid>> EmbedId = new List<List<Guid>>(); List<List<string>> EmbedType = new List<List<string>>(); List<List<string>> EmbedTitle = new List<List<string>>(); embeds = crm.PowerBIEmbed.Where(x => x.IsActive == true).ToList(); foreach (var w in workspace) { var e = embeds.Where(x => x.WorkspaceID.ToString() == w.ID.ToString()); EmbedId.Add(e.Select(x => new Guid(x.EmbedID)).ToList()); } string ApplicationId = client.PowerBIApplicationID; List<Guid> WorkspaceId = workspace.Select(x => new Guid(x.WorkspaceID)).ToList(); string AuthenticationType = "masteruser"; string ApplicationSecret = ""; string Tenant = ""; string Username = workspace.Select(x => x.PBIUsername).ToList().FirstOrDefault(); string Password = workspace.Select(x => x.PBIPassword).ToList().FirstOrDefault(); PBISetting setting = new PBISetting(); setting.ApplicationId = ApplicationId; setting.WorkspaceId = WorkspaceId; setting.EmbedId = EmbedId; setting.EmbedType = EmbedType; setting.EmbedTitle = EmbedTitle; setting.AuthenticationType = AuthenticationType; setting.Username = Username; setting.Password = Password; setting.ApplicationSecret = ApplicationSecret; setting.Tenant = Tenant; m_errorMessage = PBIService.CheckSettingError(setting); if (!m_errorMessage.IsNullOrWhiteSpace()) { return View("Error", BuildErrorModel(m_errorMessage)); } try { EmbedConfig results = new EmbedConfig(); List<ReportEmbedConfig> reportList = new List<ReportEmbedConfig>(); List<DashboardEmbedConfig> dashboardList = new List<DashboardEmbedConfig>(); List<TileEmbedConfig> tileList = new List<TileEmbedConfig>(); for (int w = 0; w < setting.WorkspaceId.Count; w++) { var embedReport = await EmbedService.GetEmbedParams(setting, setting.WorkspaceId[w], setting.EmbedId[w]); reportList.Add(embedReport); } results.Reports = reportList; results.Dashboards = dashboardList; results.Tiles = tileList; return View(results); } catch (Exception ex) { //m_errorMessage = "Operation failed: system error. please contact your system administrator."; m_errorMessage = ex.Message; return View("Error", BuildErrorModel(m_errorMessage)); } } private ErrorModel BuildErrorModel(string errorMessage) { return new ErrorModel { ErrorMessage = errorMessage }; } }
View
@if (Model.Count > 0) { for (int i = 0; i < Model.Count; i++) { for (int j = 0; j < Model[i].EmbedReports.Count; j++) { string id = "embedContainer_" + i + "_" + j; <div id="@id" class="mb-3" style="height:@height"></div> } } } <script src="~/Scripts/powerbi-client/dist/powerbi.min.js"></script> @if (Model.Count > 0) { <script> // Get a reference to the embedded report HTML element const reportContainer = []; @for (int i = 0; i < Model.Count; i++) { for (int j = 0; j < Model[i].EmbedReports.Count; j++) { @:reportContainer.push($(`#embedContainer_@(i)_@(j)`)[0]); } } // Read embed application token from Model const accessToken = []; @for (int i = 0; i < Model.Count; i++) { for (int j = 0; j < Model[i].EmbedReports.Count; j++) { @:accessToken.push(`@(Model[i].EmbedToken.Token)`); } } // You can embed different reports as per your need by changing the index // Read embed URL from Model const embedUrl = []; @for (int i = 0; i < Model.Count; i++) { for (int j = 0; j < Model[i].EmbedReports.Count; j++) { @:embedUrl.push(`@(Model[i].EmbedReports[j].EmbedUrl)`); } } // Read report Id from Model const embedReportId = []; @for (int i = 0; i < Model.Count; i++) { for (int j = 0; j < Model[i].EmbedReports.Count; j++) { @:embedReportId.push(`@`(Model[i].EmbedReports[j].ReportId)`); } } // Use the token expiry to regenerate Embed token for seamless end user experience // Refer https://aka.ms/RefreshEmbedToken const tokenExpiry = []; @for (int i = 0; i < Model.Count; i++) { for (int j = 0; j < Model[i].EmbedReports.Count; j++) { @:embedReportId.push(`@(Html.Raw(Model[i].EmbedToken.Expiration))`); } } // Get models. models contains enums that can be used. const 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://github.com/Microsoft/PowerBI-JavaScript/wiki/Embed-Configuration-Details. for (let i = 0; i < reportContainer.length; i++) { const config = { type: 'report', tokenType: models.TokenType.Embed, accessToken: accessToken[i], embedUrl: embedUrl[i], id: embedReportId[i], permissions: models.Permissions.All, settings: { // Enable this setting to remove gray shoulders from embedded report // background: models.BackgroundType.Transparent, filterPaneEnabled: true, navContentPaneEnabled: true } }; // Embed the report and display it within the div container. const report = powerbi.embed(reportContainer[i], config); } </script> }