Recently I had to read some data from different OLAP cubes and show this information in our application. This could be accomplished using ADOMD.NET client library, which is the multidimensional equivalent to the ADO.NET libraries.
This library provides a set of classes (Connections, Commands and Readers) that allow developers to connect, query and get data from these multidimensional sources in the same way the classical ADO.NET library does.
Maybe the easiest way to read data is using the class AdomdDataReader. This object retrieves a read-only, forward-only, flattened stream of data from an analytical data source. Also implements the IDataReader interface (like the good-old DataReader).
In non dynamic scenarios, we developers, tend to use ORMs like NHibertate, EF or Dapper (my favorite), to translate data from our models to classes. But these ORMs doesn’t support multidimensional sources, because after all, they are only supersets or extensions of the ADO.NET model and are based on this existing object model.
So, our goal here will be replicate in a multidimensional environment the way Dapper allows you to connect to a server, read some data and (the most important part) return these data as a typed List of objects.
Testing the query
Let’s see how it works before dig into the details:
using(varconn=newAdomdConnection(connectionString)){conn.Open();varcommandText=@"SELECT
{
[Measures].[Gains Period],
[Measures].[Gains YTD],
[Measures].[Amount]
} ON COLUMNS,
[Valuation Dates Accumulated].[Hierarchy].[Year].Members ON ROWS
FROM Accumulated
Where ({ @PortfolioId })";vardata=conn.Query<MyResultDTO>(commandText,newAdomdParameterMultipleValues<int>(){ParameterName="@PortfolioId",MemberName="[Portfolios].[Port Code]",Values=newList<int>(){282,185}});conn.Close();}
First of all we have a SQL script (highligted lines above) that returns some sample data of 3 different measures year by year (gains in period, gains year-to-date and the amount of money).
Our goal here is translate these data into a generic List of our own DTO:
Notice that in C# properties can not contain spaces so we previously have to ‘map’ the DataReader column names into valid property names. The trick here is create an attribute for decorating each property with the equivalent column name in the reader.
The magic here is inside the generic Query Method. This method is just an extension method of the AdomdConnection object (Dapper style) and it’s mission will be:
Replace the parameters in the command string “…Where ({ @PortfolioId })”
Now let’s focus on the third point and create another extension method of the IDataReader interface. This method will transform the returned result set into a generics List
OMG dude! Code on interfaces? Yes… I’ll burn in hell, I know it 😄
As you can see the method iterates over the reader’s data and uses reflection to create an object per each row.
Then map each column’s value to a property and add the new object to the returned collection (*). Easy!
(*) image courtesy of oz-code, the best debugger addin for visual studio #imho ;)