Estos meses atrás no me ha dado la vida para publicar en mi querido blog, aunque eso no significa que no tenga ciento de cosas que contarte 🙂 He estado metida en miles de pruebas de concepto para clientes, nuevas tecnologías, la universidad y siempre con muchas ganas de seguir aprendiendo más y más. Espero poder retomar el blog a partir de ahora y seguir compartiendo contigo las cosas chulas que me van pasado cada semana.
Hoy quería contarte una de las pruebas de concepto en la que estuve trabajando: cómo consumir Azure Analysis Services desde una Web API. Este escenario puede ser interesante cuando necesitas acceder a Azure Analysis Services desde una aplicación que no tiene conector con esta tecnología, como por ejemplo Java, o bien cuando quieres modularizar esta funcionalidad en un servicio aparte de la lógica de tu aplicación/es.
Todo el código de esta prueba de concepto está alojado en mi cuenta de GitHub. Aquí te muestro los pasos que deberías de seguir para conseguir nuestro objetivo:
Crea una cuenta de Azure Analysis Services
Los parámetros que se necesitan para poder crear la cuenta son los siguientes:
Server Name: El nombre de tu Analysis Server.
Subscription: La suscripción de Microsoft Azure que quieres utilizar.
Resource group: El grupo de recursos donde quieres almacenar el nuevo servicio.
Location: El datacenter en el cual quieres desplegarlo.
Pricing tier: La opción B1 sería más que suficiente para probar esta demo.
Administrator: Tu cuenta es elegida por defecto.
Backup Storage Settings: Necesitas seleccionar o crear una nueva cuenta de almacenamiento para el backup.
Storage key expiration: Para esta demo podemos dejarlo con la opción Never.
Crear un modelo de Adventure Works
Para mostrar este ejemplo de la manera más sencilla y rápida posible, vamos a utilizar el modelo de Adventure Works. Para ello solamente tienes que hacer clic sobre el botón + New model.
Los datos de ejemplo son seleccionados por defecto y sólo debes hacer clic en el botón Add.
Crear una aplicación nativa en Azure Active Directory
El objetivo de esta aplicación es poder solicitar tokens en Azure Active Directory para el cliente que va a hacer uso de esta API. Accede al servicio Azure Active Directory a través del portal de Azure y haz clic en la sección App registrations y genera una nueva a través de + New application registration. Necesitarás:
Name: Elige un nombre descriptivo para tu aplicación
Application type: Native
Redirect URI: Puedes elegir la URI que quieras. No es necesario que apunte a ningún sitio real.
El paso final es dar acceso a la aplicación al servicio de Azure Analysis Services. Haz clic en el apartado Settings:
Accede a la sección Required permissions, haz clic en + Add, busca Azure Analysis Services y seleccionalo.
Habilita el check Read and Write all Models y finaliza pulsando en Done.
Configurar AADAccessToken
El proyecto de AADAccessToken es el cliente que va a comunicarse con la Web API. El código es bastante sencillo ya que simplemente lanza el login para poder recuperar el token y una vez que la validación ha sido satisfactoria queda a la espera de que escribas cualquier query, se la lance a la API y muestre el resultado:
[csharp]using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Configuration;
using System.Diagnostics;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace AADAccessToken
{
class Program
{
static void Main(string[] args)
{
string ApiUrl = ConfigurationManager.AppSettings["apiUrl"];
var token = GetBearerToken().GetAwaiter().GetResult();
Debug.WriteLine(token);
if (token != null)
{
DisplayMessage("CONNECTED TO AZURE ANALYSIS SERVICES", ConsoleColor.White, ConsoleColor.DarkGreen);
DisplayMessage("ENTER YOUR QUERY (TYPE ‘quit’ TO EXIT):", ConsoleColor.Yellow);
var query = Console.ReadLine();
while (query != "quit")
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = client.GetAsync(new Uri($"{ApiUrl}{query}"));
string content = response.Result.Content.ReadAsStringAsync().Result;
DisplayMessage(content,ConsoleColor.DarkYellow);
DisplayMessage("ENTER YOUR QUERY (TYPE ‘quit’ TO EXIT):",ConsoleColor.Yellow);
query = Console.ReadLine();
}
}
else
{
DisplayMessage("ERROR DURING CONNECTION", ConsoleColor.White, ConsoleColor.Red);
}
}
static void DisplayMessage(string message, ConsoleColor foregroundColor = ConsoleColor.White, ConsoleColor backgroundColor = ConsoleColor.Black)
{
Console.BackgroundColor = backgroundColor;
Console.ForegroundColor = foregroundColor;
Console.WriteLine(message);
Console.ResetColor();
}
///
<summary>
/// Gets the access token to invoke Azure Analysis Services
/// </summary>
/// <returns></returns>
static async Task<string> GetBearerToken()
{
var authority = ConfigurationManager.AppSettings["aad:Authority"];
var resourceUri = ConfigurationManager.AppSettings["aad:ResourceUri"];
var clientId = ConfigurationManager.AppSettings["aad:ClientId"];
var redirectUri = new Uri(ConfigurationManager.AppSettings["aad:RedirectUri"]);
var ctx = new AuthenticationContext(authority);
var token = await ctx.AcquireTokenAsync(resourceUri, clientId, redirectUri, new PlatformParameters(PromptBehavior.Auto));
return token.AccessToken;
}
}
}[/csharp]
Haciendo uso de los servicios que hemos creado anteriormente, necesitas configurar los siguientes valores en la sección AppSettings del archivo de configuración de la aplicación de consola:
<!--Azure Active Directory values-->
<add key="aad:RedirectUri" value="[Reply URL from you Azure Active Directory Native App]"/>
<add key="aad:Authority" value="https://login.windows.net/common"/>
<add key="aad:ClientId" value="[Your Azure Active Directory App Id]"/>
<!-- Azure Analysis Services -->
<add key="aad:ResourceUri" value="https://westeurope.asazure.windows.net"/>
<!-- End Azure Active Directory values-->
<!-- Web API URL-->
<add key="apiUrl" value="http://localhost:50117/api/values/?query="/>
Con esto ya quedaría el cliente listo para logarse contra tu Azure Active Directory y realizar las llamadas a la API.
Configurar WebAPI
La lógica implementada para la Web API está localizada en el controlador ValuesController. Recuerda que se trata de una prueba de concepto y hay ciertos valores hardcodeados que deben de ser reubicados además de que es necesario implementar un sistema de autenticación para dicha API.
[csharp]using Microsoft.AnalysisServices.AdomdClient;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using System.Web.Http;
using System.Web.Script.Serialization;
namespace ADOMDWebAPI.Controllers
{
public class ValuesController : ApiController
{
readonly string asDataSource = ConfigurationManager.AppSettings["asDataSource"];
// GET api/values
public object Get(string query)
{
using (var ssasConnection = GetConnection()) //It closes Azure Analysis Services Connection when it finishes
{
var cmd = new AdomdCommand(query)
{
Connection = ssasConnection
};
var json = CreateJsonFromDataReader(cmd);
return (json);
}
}
private object CreateJsonFromDataReader(AdomdCommand cmd)
{
var sb = new StringBuilder();
var sw = new StringWriter(sb);
var fieldVal = string.Empty;
var prevFieldVal = string.Empty;
var columnName = string.Empty;
var curColumn = new List<string>();
using (JsonWriter json = new JsonTextWriter(sw))
{
using (var reader = cmd.ExecuteReader())
{
json.WriteStartArray();
while (reader.Read())
{
json.WriteStartObject();
for (int i = 0; i < reader.FieldCount; i++)
{
if (reader[i] != null)
{
fieldVal = reader[i].ToString();
if (i != 0 && reader[i – 1] != null)
prevFieldVal = reader[i – 1].ToString();
else prevFieldVal = "First";
if ((fieldVal == null || fieldVal.ToLower().Trim() == "undefined" || fieldVal.ToLower().Trim() == "unknown") && (prevFieldVal == null || prevFieldVal.ToLower().Trim() == "undefined" || prevFieldVal.ToLower().Trim() == "unknown"))
{
continue;
}
else
{
columnName = reader.GetName(i).Replace(".[MEMBER_CAPTION]", "").Trim();
curColumn = columnName.Split(new string[] { "." }, StringSplitOptions.None).ToList();
columnName = curColumn[curColumn.Count – 1].Replace("[", "").Replace("]", "");
json.WritePropertyName(columnName);
json.WriteValue(reader[i]);
}
}
}
json.WriteEndObject();
}
json.WriteEndArray();
}
var json_serializer = new JavaScriptSerializer();
return json_serializer.DeserializeObject(sw.ToString());
}
}
private AdomdConnection GetConnection()
{
/*User account token*/
var header = Request.Headers.GetValues("Authorization").First();
var token = header.ToString().Substring(header.ToString().LastIndexOf(‘ ‘)).Trim();
var connectionString = $"Provider=MSOLAP;Data Source={asDataSource};Initial Catalog=adventureworks;User ID=;Password={token};Persist Security Info=True;Impersonation Level=Impersonate";
var ssasConnection = new AdomdConnection(connectionString);
ssasConnection.Open();
return ssasConnection;
}
}
}[/csharp]
El único parámetro que necesitas configurar en la sección en el AppSettings es el data source de Analysis Services (asDataSource).
<!-- Azure Analysis Services -->
<add key="asDataSource" value="[Azure Analysis Services Data Source - ex: asazure://westeurope.asazure.windows.net/returngis]"/>
<!-- End Azure Analysis Services -->
Una vez completados todos los pasos ya estás listo para consultar Azure Analysis Services desde la Web API.
Pruébalo
Ejecuta los dos proyectos e inicia sesión con tus credenciales en la aplicación de consola. Una vez que te has logado correctamente verás el siguiente mensaje
Si has seguido el ejemplo, y estás usando el modelo Adventure Works, puedes probarlo con estas queries:
Evaluate TOPN(10,Customer,Customer[Customer Id],1)
Evaluate(ROW(«Count»,COUNTROWS(‘Product’)))
Todo el código está disponible en mi cuenta de GitHub. Además de la aplicación de consola, existe un tercer proyecto que permite el consumo de la Web API en un cliente web.
¡Saludos!


