Programming Power BI datasets with the Tabular Object Model - c#

I try to build Azure Function that getting some data from Power BI dataset,
I do it according to this https://learn.microsoft.com/en-us/analysis-services/tom/tom-pbi-datasets?view=sql-analysis-services-2022
I got error : Exception: Microsoft.AnalysisServices.ConnectionException: The connection string is not valid. ---> System.FormatException: Input string was not in a correct format.
I tried several thing without success.
I have app registration that have full access to workspace and can perform actions by REST API, and the workspace is PPU workspace, so the XMLA endpoint is enabled and all permissions defined.
I tried several connection strings I will list all of them
What should be a connection string or I'm missing something else?
using System.Net;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using Microsoft.AnalysisServices.Tabular;
using System;
using RestSharp;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Net.Http;
namespace GetRLSDetails
{
public class Function1
{
private readonly ILogger _logger;
public Function1(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<Function1>();
}
[Function("Function1")]
[Obsolete]
public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req)
{
_logger.LogInformation("C# HTTP trigger function processed a request.");
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
string datasetname = Environment.GetEnvironmentVariable("datasetname");
string tenantId = Environment.GetEnvironmentVariable("tenantId");
string appId = Environment.GetEnvironmentVariable("appId");
string appSecret = Environment.GetEnvironmentVariable("appSecret");
string workspaceConnection = $"powerbi://api.powerbi.com/v1.0/{tenantId}/BI Management TEST";
Server server = new Server();
//first version
string connectStringUser = $"Provider = MSOLAP;Data source = {workspaceConnection};initial catalog={datasetname};User ID=app:{appId};Password={appSecret};";
//second version
string connectStringUser = $"Provider = MSOLAP;Data Source ={workspaceConnection};Initial Catalog ={datasetname};User ID =app:{appId}#{tenantId}; Password ={appSecret}; Persist Security Info = True; Impersonation Level = Impersonate";
//third version
string connectStringUser = $"Provider=MSOLAP;Data Source={workspaceConnection};User ID=app:{appId}#{tenantId};Password={appSecret};";
//fourth version
string connectStringUser = $"Data Source={workspaceConnection};User ID=app:{appId}#{tenantId};Password={appSecret};";
//using PBI access token
string connectStringUser = $"Provider=MSOLAP;Data Source={workspaceConnection};UserID=;Password={accessToken};";
server.Connect(connectStringUser);
string response_text = "";
foreach (Database database in server.Databases)
{
response_text= response_text+database.Name+',';
}
response.WriteString(response_text);
return response;
}

Check what you are using for tenantId here.
string workspaceConnection = $"powerbi://api.powerbi.com/v1.0/{tenantId}/BI Management TEST";
In most cases this should be myorg, not anything specific, so I'm not sure why you have a variable there. You can check the entire address in Power BI following the instructions from your link.
If that's not it, I would connect to my Power BI dataset from Excel to build a Connection string that works.
In Excel, select Insert > PivotTable > From Power BI then pick your model. Once you've connected, you can get the connection string from Data > Queries & Connections then click on Connections in the Queries & Connections pane, then right click your connection and select Properties... then switch to the Definitions tab:
Just copy the connection string from here, or maybe it will help you see what's off in what you are trying.

Related

Does ActiveMQ Artemis support getting messages from the ActiveMQ.Advisory.Queue?

I have a .NETcore application/client which has several defined endpoints to get data from the broker. One end point is to get a list of all of the addresses but when I run it it gives back an empty response. We are using amq7. Would the C# code need to be refactored to leverage Artemis? How would I be able to get Broker/Queue metrics in Artemis with .NET?
Below is an example of the code the code we are using from the ActiveMQ NMS website
using System;
using Apache.NMS;
using Apache.NMS.Util;
using Apache.NMS.ActiveMQ;
using Apache.NMS.ActiveMQ.Commands;
namespace AdvisoryExample
{
class AdvisoryExample
{
private IConnection connection;
private ISession session;
public const String QUEUE\_ADVISORY\_DESTINATION = "ActiveMQ.Advisory.Queue";
public const String TOPIC\_ADVISORY\_DESTINATION = "ActiveMQ.Advisory.Topic";
public const String TEMPQUEUE\_ADVISORY\_DESTINATION = "ActiveMQ.Advisory.TempQueue";
public const String TEMPTOPIC\_ADVISORY\_DESTINATION = "ActiveMQ.Advisory.TempTopic";
public const String ALLDEST\_ADVISORY\_DESTINATION = QUEUE\_ADVISORY\_DESTINATION + "," +
TOPIC\_ADVISORY\_DESTINATION + "," +
TEMPQUEUE\_ADVISORY\_DESTINATION + "," +
TEMPTOPIC\_ADVISORY\_DESTINATION;
AdvisoryExample()
{
IConnectionFactory factory = new ConnectionFactory();
connection = factory.CreateConnection();
connection.Start();
session = connection.CreateSession();
}
void EnumerateQueues()
{
Console.WriteLine("Listing all Queues on Broker:");
IDestination dest = session.GetTopic(QUEUE\_ADVISORY\_DESTINATION);
using(IMessageConsumer consumer = session.CreateConsumer(dest))
{
IMessage advisory;
while((advisory = consumer.Receive(TimeSpan.FromMilliseconds(2000))) != null)
{
ActiveMQMessage amqMsg = advisory as ActiveMQMessage;
if(amqMsg.DataStructure != null)
{
DestinationInfo info = amqMsg.DataStructure as DestinationInfo;
if(info != null)
{
Console.WriteLine(" Queue: " + info.Destination.ToString() );
}
}
}
}
Console.WriteLine("Listing Complete.");
}
The default broker.xml disables advisory support, e.g.:
<acceptor name="artemis">tcp://0.0.0.0:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;amqpMinLargeMessageSize=102400;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;amqpCredits=1000;amqpLowCredits=300;amqpDuplicateDetection=true;supportAdvisory=false;suppressInternalManagementObjects=false</acceptor>
The key parameter is supportAdvisory=false which you can read more about in the documentation. You can set supportAdvisory=true or omit it entirely at which point you should be able to use advisories from your client.
Regarding other ways to get broker/queue metrics, ActiveMQ Artemis supports a number of different ways to access this data (e.g. JMX, HTTP via Jolokia, management messages, web console, CLI tools). You can read more about this in the documentation. The broker also supports exporting metrics via plugins so you can integrate with solutions like Prometheus.

Error when trying to add an Azure Blob Storage URI to an Azure Storage Table

I'm trying to add images to Azure blob storage, (this part of the program works correctly).
Once each image is in the blob storage I want to create an Azure storage table with an entity having two columns. The image category, and the image URI.
When I try to insert into the Azure Storage Table the application enters "break mode".
Ultimately this will be used by a mobile app that uses the table to select image URIs as the imagesource for a grid view so I need the full URI.
I've hardcoded the category to try and narrow down the problem.
I'm using the Azure Storage Emulator during development. FYI.
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.Queue;
using Microsoft.WindowsAzure.Storage.Table;
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace AzureEmulatorTests
{
class Program
{
static async Task Main(string[] args)
{
const string localPath = "./data/";
const string containerName = "mycontainer";
const string tableName = "mytable";
var storageAccount = CloudStorageAccount.Parse(#"UseDevelopmentStorage=true");
var blobClient = storageAccount.CreateCloudBlobClient();
var container = blobClient.GetContainerReference(containerName);
await container.CreateIfNotExistsAsync();
var tableClient = storageAccount.CreateCloudTableClient();
var table = tableClient.GetTableReference(tableName);
await table.CreateIfNotExistsAsync();
string[] filenames = Directory.GetFiles(localPath);
foreach (var images in filenames)
{
string _imageBlobReference = Guid.NewGuid() + ".jpg";
var blob = container.GetBlockBlobReference(_imageBlobReference);
await blob.UploadFromFileAsync(images);
string blobUrl = blob.Uri.AbsoluteUri;
ImageFile image = new ImageFile("Birthday", blobUrl);
await table.ExecuteAsync(TableOperation.Insert(image));
}
Console.WriteLine("Files Uploaded... Press any key to continue.");
Console.ReadKey();
}
}
class ImageFile : TableEntity
{
public ImageFile(string Category, string ImageURL)
{
PartitionKey = Category;
RowKey = ImageURL;
}
}
}
Basically the issue is with the value of your RowKey attribute as it contains invalid character (/).
For a list of unallowed characters in PartitionKey and RowKey, please see this page: https://learn.microsoft.com/en-us/rest/api/storageservices/understanding-the-table-service-data-model.
To solve this problem, please URL encode the entire URL and then try to save it. That should work.
The solution I found is to not put the URL in the PartitionKey or Rowkey properties.
When I added a string property to the table, I was able to add the URL without issue.

Create task from template via API in Microsoft Team Foundation Server 2018

I'm creating an integration from our testing framework (Selenium) to Team Foundation Server (TFS) 2018 using C# (our tests are already written in it) - the integration will generate work items in TFS based on test results. I'd like to create the work item from a template, and I can't seem to find any documentation on doing so. I'm using the TFS client library from Microsoft found here.
I can pull a template from TFS:
var client = new HttpClient()
// Client auth stuff removed
var method = new HttpMethod("GET");
var httpRequestMessage = new HttpRequestMessage(method, "http://server:port/tfs/collection/team/project/_api/wit/templates/12345abc");
var httpResponseMessage = client.SendAsync(httpRequestMessage).Result;
WorkItemTemplate tfs_template = httpResponseMessage.Content.ReadAsAsync<WorkItemTemplate>().Result;
The API for creating new work items here looks fairly straightforward, but I can't find any way to connect the two actions, or a way to apply a template using this call. Is it possible to create a work item via API with a template, and if so, is there any documentation for it?
You can not create work item from work item template. You can use work item templates to see which fields contains some default values. This example for new work item from template with rest api:
using Microsoft.TeamFoundation.Core.WebApi;
using Microsoft.TeamFoundation.Core.WebApi.Types;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
using Microsoft.VisualStudio.Services.WebApi.Patch;
using Microsoft.VisualStudio.Services.WebApi.Patch.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static string ServiceURL = "https://your_name.visualstudio.com";
static string PAT = "your pat";
static void Main(string[] args)
{
string projectName = "your project";
string templateName = "Critical bug";
/*connect to service. I use VSTS. In your case:
VssConnection connection = new VssConnection(new Uri(ServiceURL), new VssCredential());
https://learn.microsoft.com/ru-ru/azure/devops/integrate/get-started/client-libraries/samples?view=vsts
*/
VssConnection connection = new VssConnection(new Uri(ServiceURL), new VssBasicCredential(string.Empty, PAT));
//get clients
var WitClient = connection.GetClient<WorkItemTrackingHttpClient>();
var ProjectClient = connection.GetClient<ProjectHttpClient>();
var project = ProjectClient.GetProject(projectName).Result;
//get context for default project team
TeamContext tmcntx = new TeamContext(project.Id, project.DefaultTeam.Id);
//get all template for team
var templates = WitClient.GetTemplatesAsync(tmcntx).Result;
//get tempate through its name
var id = (from tm in templates where tm.Name == templateName select tm.Id).FirstOrDefault();
if (id != null)
{
var template = WitClient.GetTemplateAsync(tmcntx, id).Result;
JsonPatchDocument patchDocument = new JsonPatchDocument();
foreach (var key in template.Fields.Keys) //set default fields from template
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/" + key,
Value = template.Fields[key]
});
//add any additional fields
patchDocument.Add(new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/fields/System.Title",
Value = "My critical bug"
});
var newWorkItem = WitClient.CreateWorkItemAsync(patchDocument, projectName, template.WorkItemTypeName).Result;
}
}
}
}
My template:

Azure Function App C# HTTP Trigger connected to an Azure Database

I try to create a serverless backend for mobile app, following that sample (cheers James) : https://blog.xamarin.com/creating-a-serverless-backend-for-mobile-apps/
Everything works fine for me, until I want to connect my script to a database.
I would like to use that Azure Function App with an Azure Database. For instance, I want my mobile app to call an http request (that triggers the function app), the function app makes a query to the database, and then send an http request back to the mobile.
I tried to follow that video : https://channel9.msdn.com/Series/Windows-Azure-Web-Sites-Tutorials/Create-an-event-processing-Azure-Function?ocid=player
In this video, the developer connects its scripts to a database and performs a query to the database.
It is that step that makes me fail :(
Here is my code :
#r "System.Configuration"
#r "System.Data"
using System.Net;
using System.Configuration;
using System.Data.Sql;
using System.Threading.Tasks;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
if (req == null)
log.Info("req is null");
else
log.Info("req is not null");
//log.Info($"C# HTTP trigger function processed a request. RequestUri={req.RequestUri}");
string sConnexionString = "Name=MS_TableConnectionString";
log.Info(sConnexionString);
if (ConfigurationManager.ConnectionStrings[sConnexionString] == null)
log.Info("ConfigurationManager.ConnectionStrings[sConnexionString] is null");
else
log.Info("ConfigurationManager.ConnectionStrings[sConnexionString] is not null");
var str = ConfigurationManager.ConnectionStrings[sConnexionString].ConnectionString;
if (str == null)
log.Info("str is null");
else
log.Info("str is not null");
using (SqlConnection conn = new SqlConnection(str))
{
if (conn == null)
log.Info("conn is null");
else
log.Info("conn is not null");
conn.Open();
var text = "INSERT INTO MyEasyTable(id) values (1)";
using (SqlCommand cmd = new SqlCommand(text, conn))
{
var rows = await cmd.ExecuteNonQueryAsync();
log.Info($"{rows} inserted.");
}
}
// parse query parameter
string name = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
.Value;
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
// Set name to query string or body data
name = name ?? data?.name;
return name == null
? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
: req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
}
And here is the output log :
2016-10-24T13:19:09.452 Compilation succeeded.
2016-10-24T13:19:13.257 Function started (Id=6ffaade4-58b0-4005-8fe7-7dc06b96c2b7)
2016-10-24T13:19:14.018 req is not null
2016-10-24T13:19:14.018 Name=MS_TableConnectionString
2016-10-24T13:19:14.018 ConfigurationManager.ConnectionStrings[sConnexionString] is null
2016-10-24T13:19:14.018 Function completed (Failure, Id=6ffaade4-58b0-4005-8fe7-7dc06b96c2b7)
2016-10-24T13:19:14.037 Exception while executing function: Functions.HttpTriggerCSharp1. HttpTriggerCSharp1: Object reference not set to an instance of an object.
Thanks to that log, I understand that ConfigurationManager cannot connect to my Azure database :(
I tried many string for sConnexionString :
"Data Source=tcp:BDDSERVERNAME.database.windows.net,1433;Initial Catalog=BDD_NAME;User ID=USER#BDDSERVERNAME;Password=MYPASSWORD"
"MS_TableConnectionString"
"Name=MS_TableConnectionString"
And it never works :(
Please, anybody has an idea ?
Okay I found the answer...
I need to go to the Function App's settings, and add the Connection String in the settings, as mentioned here : Azure Functions Database Connection String
Then, I need to use this line of code :
string sConnexionString = "MS_TableConnectionString";
And it works well !
Thanks to Adrian Hall that allowed me to refine my searches

How to change sql server connection string dynamically in service stack

I am working on Asp.Net MVC and ServiceStack. I am trying to connect to the sql server database using servicestack ormlite. like
var connectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;
container.Register<IDbConnectionFactory>(
new OrmLiteConnectionFactory(connectionString,
SqlServerOrmLiteDialectProvider.Instance)
{
ConnectionFilter = x => new ProfiledDbConnection(x, Profiler.Current)
});
I am able to connect to database, But in my scenario i need to change the connection string dynamically.. That means i need to read the content from Request body and prepare a connection string. In servicestack we configure sql server connection string in AppHost class that means at app start. But i need to set the connection string in my controller. I have tried like place it in session and use that session in ClassLibrary SeviceBase class. But I am unable to use asp.Net sessions in class libraries.How to change sql server connection string dynamically in service stack. so please guide me.
I would change the IDbConnectionFactory to be reused in the scope of the request, instead of the current default, which shares it among all requests.
I have also created a static method (GetDatabaseConnectionFactory()) which returns the instance of OrmLiteConnectionFactory to the IoC container with the custom connection string.
To determine the connection string, I have used a request filter, which simply reads the parameter connectionstring. If it is not set it will use a default value. This value is then
set in the RequestContext.Items collection, which can be accessed by the GetDatabaseConnectionFactory() method.
Remember exposing connection strings this way is dangerous, always check any connection string values thoroughly to ensure they don't contain malicious values. i.e. Ensure they don't try to connect to administrative databases, or a different server, or change default setting overrides etc.
In your AppHost:
ServiceStack V3:
public override void Configure(Container container)
{
container.Register<IDbConnectionFactory>(c => GetDatabaseConnectionFactory()).ReusedWithin(ReuseScope.Request);
RequestFilters.Add((req,res,obj) => {
// Default value
var defaultConnectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;
// Get the connection string from the connectionstring parameter, or use default
var dbConnectionString = req.GetParam("connectionstring") ?? defaultConnectionString;
// You should perform some checks here to make sure the connectionstring isn't something it shouldn't be
// ...
// Save the connection string to the HostContext.Instance.Items collection, so we can read it later
HostContext.Instance.Items.Add("ConnectionString", dbConnectionString);
});
}
public static IDbConnectionFactory GetDatabaseConnectionFactory()
{
// Read the connection string from our HostContext Items
var dbConnectionString = HostContext.Instance.Items["ConnectionString"];
if(dbConnectionString == null)
throw new Exception("Connection string has not been set");
// Return the connection factory for the given connection string
return new OrmLiteConnectionFactory(dbConnectionString, SqlServerOrmLiteDialectProvider.Instance) {
ConnectionFilter = x => new ProfiledDbConnection(x, Profiler.Current)
});
}
Usings:
using System;
using Funq;
using ServiceStack.ServiceInterface;
using ServiceStack.ServiceHost;
using ServiceStack.WebHost.Endpoints;
using ServiceStack.OrmLite;
using ServiceStack.Common;
ServiceStack V4:
public override void Configure(Container container)
{
container.Register<IDbConnectionFactory>(c => GetDatabaseConnectionFactory()).ReusedWithin(ReuseScope.Request);
GlobalRequestFilters.Add((req,res,obj) => {
// Default value
var defaultConnectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;
// Get the connection string from the connectionstring parameter, or use default
var dbConnectionString = req.GetParam("connectionstring") ?? defaultConnectionString;
// You should perform some checks here to make sure the connectionstring isn't something it shouldn't be
// ...
// Save the connection string to the RequestContext.Items collection, so we can read it later
HostContext.RequestContext.Items.Add("ConnectionString", dbConnectionString);
});
}
public static IDbConnectionFactory GetDatabaseConnectionFactory()
{
// Read the connection string from our Items
var dbConnectionString = HostContext.RequestContext.Items["ConnectionString"];
if(dbConnectionString == null)
throw new Exception("Connection string has not been set");
// Return the connection factory for the given connection string
return new OrmLiteConnectionFactory(dbConnectionString, SqlServerOrmLiteDialectProvider.Instance) {
ConnectionFilter = x => new ProfiledDbConnection(x, Profiler.Current)
});
}
Usings:
using System;
using Funq;
using ServiceStack;
using ServiceStack.Data;
using ServiceStack.OrmLite;
using ServiceStack.OrmLite.Sqlite;

Categories