How-to Use SAP NCo 3 Connector with .Net 4 in Visual Studio 2013

SAP Business Connector
SAP Business Connector

SAP offers several technologies to interface with its ECC system. Of those varied technologies, RFC (or Remote Function Call) is one of the most popular. SAP has developed many implementation for the RFC, including COM, Java and .Net. SAP initially created a Connector using Java, called the Jco or (Java Connector) as an alternative to their flagship ABAP language. As the .Net framework and platform became more prevalent, SAP created a RFC Connector for.Net, titled Nco (.Net Connector). SAP recently released an updated version of their .Net Connector for .Net Framework 4.

SAP .Net Connector Overwiev
SAP .Net Connector Overwiev

Install the connector on your machine
To interface with SAP using the SAP Nco 3.0.15.0 for .Net Framework 4.0 and Visual Studio 2013, you will need to download the Connector from the SAP Marketplace web site.
Note that you need to be a SAP customer with a valid customer id and password: http://service.sap.com/connectors/

For Visual Studio 2013, you will need to download the latest one:
sapnco30dotnet40P_15-20007347.zip for 32 bit applications
sapnco30dotnet40P_15-20007348.zip for 64 bit applications

Unzip and install to a convenient location on your machine.

Create an App
For the purposes of this tutorial, I will create a Console application using the C# language to retrieve a list of customers from SAP. I will also create a C# class to handle the operations and a class to manage the connections to the different SAP systems. If you have Visual Studio 2013, follow these steps:

Create a Visual Studio 2013 Windows Console Application. I name mine SAP_Customers, but you can name it anything you want.

SAP NCo DLL Version Information
SAP NCo DLL Version Information

Create SAP Connection
Once the Project is setup, create a new C# class, SAPSystemConnect, to implement the “IDestinationConfiguration” interface. This class will manage the configuration and connection to the SAP system. To be able to implement the “IDestinationConfiguration” interface, you will need to add a couple of references.

  • Right click on project and select “Add Reference”
  • When window opens, select “Browse” and navigate to the folder where you installed the SAP Nco Connector.
  • You will need to select the following dll:
    Sapnco.dll
    Sapnco_utils.dll

 

Add the Connector Reference to the Class
Add the Connector Reference to the Class

Next in the SAPSystemConnect class file, add a reference to the Connector SAP.Middleware.Connector.

To connect to a SAP system, we need to implement “IDestinationConfiguration“ interface and define connection configuration parameters.

Using the SAPSystemConnect class, add the IDestinationConfiguration and implicitly implement its methods. The following code snippet show how the code should look like after the methods are implemented.
An easy way to implement methods and properties of an interface is to place your cursor at the end of the class name and type a colon “:”.
Then start typing the interface name and IntelliSense should popup and provide some suggestions, or you can press Ctrl+Spacebar to bring up the IntelliSense menu. Once the interface name is entered, IntelliSense will add an underscore or squiggly just under the first couple of letters as a prompt for you to take further action.

Click on the squiggly and select to “implicitly…” implement the interface’s methods and IntelliSense will add the necessary methods, events and other properties that are in the interface.

Code Snippet of SAPSystemConnect class
Code Snippet of SAPSystemConnect class

To define an RFCDestination, we will need to change the code in the GetParameters method. Several important parameters need to be created and initialized to be able to connect to SAP and return an RFCDestination. First create a new RfcConfigParameters object, parms, to hold our connection details.

This class will manage the connections to the SAP system through a pooling manager, thus allowing for several threaded connections. Next, if you plan on using the same program for different destinations, you can test for the destination using an “if” statement or a “switch”. In the following example, I am using an “if” expression.

To define a destination, we will need to set some parameters as the following code snippet demonstrates.

SAP RFCConnection Parameters
SAP RFCConnection Parameters

SAP BAPI Explorer
SAP’s BAPI Explorer (tcode is BAPI) is your source of all the functions, objects, fields and source code to help you. BAPI Explorer is more than a documentation repository. It also provides access to the source code of the RFCs; provides detailed information on the import and export parameters, structures and tables. You can create and test new functions and you can run existing BAPIs to review the data that is being returned. A handy tool is the BAPI list generator. It searches and creates a list of all BAPIs for a particular object.

The BAPI Explorer tutorial is beyond the scope of this tutorial.

BAPI Explorer
BAPI Explorer
Customer BAPI
Customer BAPI

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Customer Class Properties
Customer Class Properties

Using the RFCDestination
The next step in this tutorial is to actually use the RFCDestination to connect to a Repository and query the Customer Master Data to return a list of customers and some extra details. Four BAPIs (functions) that will give us the required information are:

BAPI_CUSTOMER_GETLIST
BAPI_CUSTOMER_GETSALESAREAS
BAPI_CUSTOMER_GETDETAIL1
BAPI_CUSTOMER_GETDETAIL2

Create a new C# class: Customers

Add the SAP Connector in the reference

To hold the data from SAP, define a series of protected properties. The code has been truncated for brevity but the complete source code is included at the end of the tutorial:

Next define method to perform the operations of connecting and retrieving the data from SAP: GetCustomerDetail. The method will take a RfcDestination parameter to pass in the destination from the main program, see section “Putting the Pieces Together” later in this tutorial.

The Connector provides several Exception classes that we will implement using a try…catch statement. The exception classes are:

  • RfcCommunicationException
  • We couldn’t obtain a connection with the system.
  • RfcLogonException
  • We couldn’t logon.
  • RfcAbapRuntimeException
  • A runtime error has occurred
  • RfcAbapBaseException
  • A General Abap error has occurred.

Within the try…catch operation, define an RfcRepository object, repo. Next create a RfcFunction to return a list of customers, customerList and pass in the “BAPI_CUSTOMER_GETLIST” function to return. Before we can use the function, we need to invoke it, see code snippet below.

Code Snippet of Creating Function
Code Snippet of Creating Function
Setting the idRange parameters
Setting the idRange parameters

Now that we have access to the function, we need to tell it what range of values to return. Create an IRFCTable object and set the GetTable property for the CustomerList function. Set the value to “IdRange”. For the purposes of this example, I will use the following parameters:

  • Sign = “I”
  • Options= “BT”, meaning “between”
  • Low = “”, or smallest value
  • High=”9999999”, the highest possible value

Here is a look at the code snippet:

Add idRange to BAPI function
Add idRange to BAPI function

Once these values are set, you will need to add the table to the function. Before invoking the function again to return the list of customers, you will need to tell the function which table of data you want to return. The current function can return “AddressData” and “Return” and “SpecialData”. I will use the “AddressData” for this example.

Once we have a list of customers, you will be able to loop through the list, extracting any needed data. I will create and destroy and explicitly call the garbage collector for each row in the list otherwise you will run into memory issues. You could use a “Using” statement to loop through the list and manage the object resources, but I have had issues with that design as well, so I will use the tried and true “for each”.

Also I will create (call or initialize) three new functions to get all the necessary information on the customers: “BAPI_CUSTOMER_GETSALESAREAS”, “BAPI_CUSTOMER_GETDETAIL1” and “BAPI_CUSTOMER_GETDETAIL2”.

Once the function are created and invoked, passing in any parameters as required, you can access the data as using the GetString property of the RFC function. Also bear in mind that a SAP function can return either a table or a structure. You will need to consult the documentation or through the Visual Studio debugger, “locals” window to determine which is which because the documentation may not always tell which is which form my experience. In the following example, the “CustomerGeneralDetail” in the “customerDetail2” function is a structure, while the “SalesAreas” in the “customerHierachy” function is a table. I have found that when accessing a table, it is better to test if there are any rows; otherwise the program throws an error.

This is the complete code for the Customers class:

Customers Class Code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SAP.Middleware.Connector;

namespace SAP_Customers
{
class Customers
{
protected string CustomerNo;
protected string CustomerName;
protected string Address;
protected string City;
protected string StateProvince;
protected string CountryCode;
protected string PostalCode;
protected string Region;
protected string Industry;
protected string District;
protected string SalesOrg;
protected string DistributionChannel;
protected string Division;





public void GetCustomerDetails(RfcDestination destination)
{


try
{
RfcRepository repo = destination.Repository;
IRfcFunction customerList = repo.CreateFunction("BAPI_CUSTOMER_GETLIST");


customerList.Invoke(destination);

IRfcTable idRange = customerList.GetTable("IdRange");
idRange.SetValue("SIGN", "I");
idRange.SetValue("OPTION", "BT");
idRange.SetValue("LOW", "");
idRange.SetValue("HIGH", "999999");


//add selection range to customerList function to search for all customers
customerList.SetValue("idrange", idRange);


IRfcTable addressData = customerList.GetTable("AddressData");
customerList.Invoke(destination);

for (int cuIndex = 0; cuIndex < addressData.RowCount; cuIndex++)
{

addressData.CurrentIndex = cuIndex;
IRfcFunction customerHierachy = repo.CreateFunction("BAPI_CUSTOMER_GETSALESAREAS");
IRfcFunction customerDetail1 = repo.CreateFunction("BAPI_CUSTOMER_GETDETAIL1");
IRfcFunction customerDetail2 = repo.CreateFunction("BAPI_CUSTOMER_GETDETAIL2");

this.CustomerNo = addressData.GetString("Customer");
this.CustomerName = addressData.GetString("Name");
this.Address = addressData.GetString("Street");
this.City = addressData.GetString("City");
this.StateProvince = addressData.GetString("Region");
this.CountryCode = addressData.GetString("CountryISO");
this.PostalCode = addressData.GetString("Postl_Cod1");

customerDetail2.SetValue("CustomerNo", this.CustomerNo);
customerDetail2.Invoke(destination);
IRfcStructure generalDetail = customerDetail2.GetStructure("CustomerGeneralDetail");

this.Region = generalDetail.GetString("Reg_Market");
this.Industry = generalDetail.GetString("Industry");


customerDetail1.Invoke(destination);
IRfcStructure detail1 = customerDetail1.GetStructure("PE_CompanyData");

this.District = detail1.GetString("District");


customerHierachy.Invoke(destination);
customerHierachy.SetValue("CustomerNo", this.CustomerNo);
customerHierachy.Invoke(destination);

IRfcTable otherDetail = customerHierachy.GetTable("SalesAreas");

if (otherDetail.RowCount > 0)
{
this.SalesOrg = otherDetail.GetString("SalesOrg");
this.DistributionChannel = otherDetail.GetString("DistrChn");
this.Division = otherDetail.GetString("Division");
}

customerHierachy = null;
customerDetail1 = null;
customerDetail2 = null;
GC.Collect();
GC.WaitForPendingFinalizers();



}


}
catch (RfcCommunicationException e)
{

}
catch (RfcLogonException e)
{
// user could not logon...
}
catch (RfcAbapRuntimeException e)
{
// serious problem on ABAP system side...
}
catch (RfcAbapBaseException e)
{
// The function module returned an ABAP exception, an ABAP message
// or an ABAP class-based exception...
}

}
}
}

Putting the Pieces Together

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SAP.Middleware.Connector;

namespace SAP_Customers
{
class Program
{
static void Main(string[] args)
{
SAPSystemConnect sapCfg = new SAPSystemConnect();


RfcDestinationManager.RegisterDestinationConfiguration(sapCfg);
RfcDestination rfcDest=null;

for (int i = 0; i < args.Length; i++)
{ 
// arg[i] = Dev
rfcDest = RfcDestinationManager.GetDestination(args[i]);
}


Customers customer = new Customers();
customer.GetCustomerDetails(rfcDest); 

System.Environment.Exit(0);

}


}
}

In Summary

Creating, invoking and extracting data from either a structure or table is very easy. The hardest part is finding the right function, import parameters and which tables or structures contains the proper information. Also it is important to bear in mind the fact that the functions use the same field names as in the SAP tables, so I sometimes, you will need to open the program to see which fields are being retuned. For this and finding the functions, tables, structures, import and export parameters, the BAPI Explorer is an invaluable tool.

 

 

Author: