Using XML\A and C# to Query SAP BW

Need to query SAP BW from your .NET application? This is a trivial task with XML\A and C#. XML\A is a specification that allows multidimentional datasources to be queried by applications. It is how Reporting Services talks to SAP BW (http://technet.microsoft.com/en-us/library/bb508810(SQL.90).aspx).

Step 1: Find out the path to your SAP BW XML\A service, Mine looks something like this "http://<Server>:<Port>/sap/bw/xml/soap/xmla"

Step 2: Open a .NET project and create a web reference to that service, I called mine MsXmlAnalysis

Step 3: Modify the WebService Proxy Class, When .NET generates the proxy class it declares the Execute Mehtod with two arguments the ExecuteCommand and a PropertiesType. The PropertiesType is a base class for the DiscoverProperties object, but has no properties. To fix this we actually want to pass a DiscoverProperties object to the Execute Method. While this compiles fine the XML serializer has a problem serializing the request. To fix this add "[XmlElement(typeof(DiscoverProperties))]" before the PropertiesType argument in the Execute Methods Signature. It should look like this "public System.Xml.XmlElement Execute([System.Xml.Serialization.XmlElementAttribute(IsNullable=true)] ExecuteCommand Command,  [XmlElement(typeof(DiscoverProperties))]PropertiesType Properties) {"

Step 4: Execute the MDX Query:
using (MsXmlAnalysis.MsXmlAnalysis ws = new MsXmlAnalysis.MsXmlAnalysis())
{
 //the _userId and _password are your login credentials to the SAP Server
 NetworkCredential nc = new NetworkCredential(_userId, _password);
 //the url is the path you found in Step 1
 ws.Credentials = nc.GetCredential(new Uri(url), "Basic");
 ws.Url = url;

 MsXmlAnalysis.ExecuteCommand cmd = new MsXmlAnalysis.ExecuteCommand();
 cmd.Statement = "MDX QUERY HERE";

 MsXmlAnalysis.DiscoverProperties properties = new MsXmlAnalysis.DiscoverProperties();
 properties.PropertyList = new MsXmlAnalysis.DiscoverPropertiesPropertyList();
 properties.PropertyList.LocalIdentifier = "1033";
 properties.PropertyList.DataSourceInfo = "default";
 properties.PropertyList.Content = "SchemaData";
 properties.PropertyList.Format = "Tabular";

 XmlElement elem =  ws.Execute(cmd, properties);
 //Do something with the element here
}

You can also query metadata out of the SAP BW using the XML\A

Here is a list of different requests you can do: DISCOVER_DATASOURCES, DISCOVER_PROPERTIES, MDSCHEMA_CUBES, MDSCHEMA_CUBES, MDSCHEMA_DIMENSIONS, MDSCHEMA_HIERARCHIES, SAP_VARIABLES, MDSCHEMA_MEASURES, MDSCHEMA_PROPERTIES. If you want to limit your search to a specific cube specify a catalog and cube specify those values also (some of the queries require catalog and cube to be specified).

using (MsXmlAnalysis.MsXmlAnalysis ws = new MsXmlAnalysis.MsXmlAnalysis())
{
 NetworkCredential nc = new NetworkCredential(_userId, _password);
 ws.Credentials = nc.GetCredential(new Uri(url), "Basic");
 ws.Url = url;

 MsXmlAnalysis.DiscoverProperties properties = new MsXmlAnalysis.DiscoverProperties();
 properties.PropertyList = new MsXmlAnalysis.DiscoverPropertiesPropertyList();
 properties.PropertyList.LocalIdentifier = "1033";
 properties.PropertyList.DataSourceInfo = "default";
 properties.PropertyList.Content = "SchemaData";
 properties.PropertyList.Format = "Tabular";

 MsXmlAnalysis.DiscoverRestrictions restrictions = new MsXmlAnalysis.DiscoverRestrictions();
 restrictions.RestrictionList = new MsXmlAnalysis.DiscoverRestrictionsRestrictionList();

 if (catalog != null && cube != null
     && catalog.Length > 0 && cube.Length > 0)
 {
     restrictions.RestrictionList.CATALOG_NAME = catalog;
     restrictions.RestrictionList.CUBE_NAME = cube;
 }

 XmlElement elem = ws.Discover(request, restrictions, properties);
 //Do something with the element here
}

Published Thursday, June 14, 2007 11:04 AM by sweisfeld
Filed under: , , ,

Comments

# re: Using XML\A and C# to Query SAP BW

Hi,

I'm trying to integrate a SAPBW connection into my .net website using a SAPBW xml/a adapter. (I've added a web reference to this so I have a .wdsl).

However, I'm stuck with step 3 which shows how to implement the solution for an application rather then a website so it talks about modify the generated webservice proxy??

"Step 3: Modify the WebService Proxy Class, When .NET generates the proxy class it declares the Execute Mehtod with two arguments the ExecuteCommand and a PropertiesType. The PropertiesType is a base class for the DiscoverProperties object, but has no properties. To fix this we actually want to pass a DiscoverProperties object to the Execute Method. While this compiles fine the XML serializer has a problem serializing the request. To fix this add "[XmlElement(typeof(DiscoverProperties))]" before the PropertiesType argument in the Execute Methods Signature. It should look like this "public System.Xml.XmlElement Execute([System.Xml.Serialization.XmlElementAttribute(IsNul lable=true)] ExecuteCommand Command, [XmlElement(typeof(DiscoverProperties))]PropertiesType Properties) {"

"

How do I make this sort of change in a .net website. Do I need to modify the webreference wdsl file?

Any help would be very much appreciated because I can't seem to get over this... (please find my code below).

Imports System

Imports System.Data

Imports System.Xml

Imports System.Collections.Generic

Imports System.Text

Imports System.Net

Imports Microsoft

Imports SAPBW

Imports Microsoft.AnalysisServices.AdomdClient

Imports System.Xml.Serialization

Imports System.Xml.Schema

Imports System.IO

'Namespace SAPBW

<XmlInclude(GetType(_Default))> _

<XmlInclude(GetType(PropertiesType))> _

<XmlInclude(GetType(DiscoverProperties))> _

Partial Class _Default

Inherits System.Web.UI.Page

'Global Variables

Private BWQuery As MsXmlAnalysis

Private Command As ExecuteCommand

Private result As XmlElement

Private convert_result As XmlNodeReader

Private properties As DiscoverProperties

'Private properties As PropertiesType

Private login As NetworkCredential

Private cellset_result As CellSet

Private tuples_on_columns As TupleCollection

Private i As Integer

Private nr_tuples_on_columns As Integer

Protected UN As String = "UserName"

Protected Pass As String = "PassWord"

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

InitializeVariables()

SetVariables()

WriteCommand()

Commands()

End Sub

Private Sub InitializeVariables()

BWQuery = New MsXmlAnalysis()

Command = New ExecuteCommand()

properties = New DiscoverProperties()

properties.PropertyList = New DiscoverPropertiesPropertyList()

login = New NetworkCredential()

End Sub

Private Sub SetVariables()

login.UserName = UN

login.Password = Pass

BWQuery.Credentials = login

BWQuery.PreAuthenticate = True

properties.PropertyList.Content = "SchemaData"

properties.PropertyList.Format = "Multidimensional"

properties.PropertyList.DataSourceInfo = "default"

End Sub

Private Sub WriteCommand()

Command.Statement = "SELECT NON EMPTY { [Measures].[3YIJG796Q2HIRX2ZBG2MMQZNN] } ON COLUMNS, NON EMPTY {[FEEEARNER].[LEVEL01].ALLMEMBERS } ON ROWS FROM [OP_CTIM3/FI_CTIM3_001_V01]"

End Sub

Private Sub Commands()

'Dim s As XmlSerializer = new XmlSerializer(typeof(result))

'<XmlElement(typeof(DiscoverProperties))>

Try

Dim propType As PropertiesType

/************************************************** ******

**This is the problem as Execute expects (Command, Property)

**however i've populated Discoverproperties attribute but

**cannout pass it without implementing step 3. So I am trying

**to pass BWQuery.Execute(Command, properties)

************************************************** ******/

result = BWQuery.Execute(Command, propType)

convert_result = New XmlNodeReader(result)

cellset_result = CellSet.LoadXml(convert_result)

Dim i As Integer = 1

For i = 1 To nr_tuples_on_columns

Response.Write(tuples_on_columns(i).Members(0).Cap tion)

Next

'Console.ReadLine()

Catch ex As Exception

Response.Write(ex.ToString())

End Try

End Sub

End Class

'End Namespace

Tuesday, April 22, 2008 8:26 AM by johni2i

# re: Using XML\A and C# to Query SAP BW

Shawn,

Great and helpful article. I have one question on the Xml that comes back from SAP(like below). The problem is that the descriptive column names are in the Schema section and the column names in the actual xml are in a "C00000#" format.

How do I would convert this data into a DataSet or DataTable object that has the columns named with the names in the schema?

<root xmlns="urn:schemas-microsoft-com:xml-analysis:rowset" xmlns:xsd="www.w3.org/.../XMLSchema" xmlns:xsi="www.w3.org/.../XMLSchema-instance">">www.w3.org/.../XMLSchema-instance">

<xsd:schema xmlns="urn:schemas-microsoft-com:xml-analysis:rowset" targetNamespace="urn:schemas-microsoft-com:xml-analysis:rowset" xmlns:xsi="www.w3.org/.../XMLSchema-instance" xmlns:xsd="www.w3.org/.../XMLSchema" xmlns:sql="urn:schemas-microsoft-com:xml-sql" elementFormDefault="qualified">

<xsd:element name="root">

 <xsd:complexType>

  <xsd:sequence minOccurs="0" maxOccurs="unbounded">

   <xsd:element name="row" type="row" />

  </xsd:sequence>

 </xsd:complexType>

</xsd:element>

<xsd:simpleType name="uuid">

 <xsd:restriction base="xsd:string">

  <xsd:pattern value="[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}" />

 </xsd:restriction>

</xsd:simpleType>

<xsd:complexType name="row">

 <xsd:sequence minOccurs="0" maxOccurs="unbounded">

<xsd:element name="C000000" type="xsd:string" sql:field="[ZFISQUART].[LEVEL01].[MEMBER_CAPTION]" />

<xsd:element name="C000001" type="xsd:string" sql:field="[ZDBBUS].[LEVEL01].[MEMBER_CAPTION]" />

<xsd:element name="C000002" type="xsd:string" sql:field="[Measures].[ZDBOPINC]" />

<xsd:element name="C000003" type="xsd:string" sql:field="[Measures].[ZDBORONA]" />

<xsd:element name="C000004" type="xsd:string" sql:field="[Measures].[ZDBORONAR]" />

<xsd:element name="C000005" type="xsd:string" sql:field="[Measures].[ZDBNETASS]" />

 </xsd:sequence>

</xsd:complexType>

</xsd:schema>

<row><C000000>FY04Q1</C000000><C000001>Sales</C000001><C000002>10,000.00 USD</C000002><C000003>0.000</C000003><C000004>0.000</C000004><C000005>100,000.00 USD</C000005></row>

<row><C000000>FY04Q1</C000000><C000001>Marketing</C000001><C000002>1,200.00 USD</C000002><C000003>0.000</C000003><C000004>0.000</C000004><C000005>125,000.00 USD</C000005></row>

</root>

Wednesday, August 06, 2008 10:23 AM by schwinbp