SAP .NET Connector 2.0 offered a couple of different sample projects that were instrumental in my learning process. As I mentioned in my post detailing the steps to build an RFC client, SAP no longer supplies code samples with SAP .NET Connector (NCo) 3.0. So I decided to make available some code examples.
This blog describes how to build a simple RFC Server using the SAP NCo 3.0. The sample program implements RFC STFC_CONNECTION. STFC_CONNECTION is a good example to use because it contains both importing and exporting parameters.
Along with the SAP .Net Connector 3.0, we are using Microsoft Visual Studio 2010 and the Microsoft .Net Framework 4.0 to build our sample. Prior to starting, you will have to download and install NCo 3.0 (OSS login required).
Setting up the Project
Using Visual Studio 2010, create a new console project. In the project properties, be sure to set the Target Framework to .Net Framework 4.0.
Next, add references to the SAP .Net Connector 3.0. There are two DLLs, sapnco.dll and sapnco_utils.dll.
Using App.Config to define the SAP Connections
There are several methods you can use in your solutions to define a particular SAP host. For this simple example, we are using an app.config file to define our SAP host connection information.
One of the biggest differences between NCo 3.0 and NCo 2.0 is how function metadata is retrieved and stored. With NCo 2.0 the function metadata was downloaded from an SAP system at design time and persisted as proxy objects that were compiled with the rest of your solution. NCo 3.0 needs to access this metadata at runtime. There are two methods for providing this metadata. One method is to create the metadata in your .NET code. The other method is to rely on the metadata stored in the SAP system. For this example, we will rely on the SAP system to provide RFC metadata. This metadata is retrieved using special built-in RFCs. Therefore, not only will our program act as an RFC server, it will also need to act as an RFC client.
For basic RFC server configuration, there are two configuration sections that are used, <ClientSettings> and <ServerSettings>. Ensure that the app.config file has a <sectionGroup> tag for both client and server settings.
An RFC server configuration is linked to a RFC repository by use of the REPOSITORY_DESTINATION attribute. To be able to access function metadata, this attribute must match the name of a destination configuration in the client settings section.
All SAP NCo 3.0 RFC servers communicate with SAP through a SAP service called the gateway. The server settings configuration section defines how the program connects to the SAP gateway. In the servers section, the NAME field identifies this app.config entry to our program. The SAP .Net Connector 3.0 will use the values in the GWHOST, GWSERV, and PROGRAM_ID fields to register our program on the SAP Application Gateway. The PROGRAM_ID specified here must match the Registered Server Program ID specified in the RFC destination.
The SAP RFC Function Interface
SAP transaction SE37 can be used to determine the parameters for the STFC_CONNECTION function. This describes what parameters will be passed to our function, and what results are expected.
In this example, we expect to receive parameter REQUTEXT, which will be returned to the caller in ECHOTEXT. We also return a text string to the caller in field RESPTEXT.
SAP transactoin SM59 is used to configure the SAP RFC Destination. In this example, the name of the RFC Destination (NIMBUS) is the name of the development computer that is running the RFC Server. The registered program ID (NCO3_RFC_SERVER) matches our app.config file.
The Code
As an Nco developer, one of my favorite changes brought about by Nco 3.0 is how RFC server functionality is implemented. Instead of overriding generated stub method, we need to implement one or more class methods that will process the incoming RFC calls. For this there are several possibilities:
- The first option is to create one static method corresponding to each SAP function module to be implemented. Assigning a SAP function module to a method is done by decorating the method with the RfcServerFunction attribute.
- The second method is to create one static method for that handles all SAP function modules. This is called the “default” method. Use the same RfcServerFunction method decoration as the previous option, but denote that the method is the defualt RFC handler. The Name attribute is irrelevant in this case. These two methods for RFC Function handling can be combined in the same project.
It is also possible to use either of the two previous options with instance methods instead of static methods. For the purposes of this example, we will use a static method to implement our RFC Server.
We will create a class that exposes a method that can be called via SAP RFC. In our example that class is named DxsRfcServer and it contains one method that will supply the STFC_CONNECTION functionality. Now perform the following steps to implement class.
- Provide the RFC callable code. This is done in our DxsRfcServer class, where we have a method defined. The RfcServerFunction code attribute on line 59 identifies this function to the SAP .Net Connector as the method that implements SAP function STFC_CONNECTION. On line 60 is where we are passed two parameters that contain all of the information we need to execute our function. The IRfcFunction interface provides access to parameter values. In this case we use the .GetString() method on line 69 to extract the value of the REQUTEXT parameter. We also can return values to the RFC caller using the IRfcFunction interface by using the .SetValue() method as seen in lines 69-70.
- Create an array of types, with just one element that refers to our DxsRfcServer class.
- Register the array with an RfcServer object. Notice that on line 22, the first parameter to the .GetServer() method is the NAME field of the servers section in app.config.
- Start the RFC server.
- At this time, the SAP system has visibility of our program running on the application gateway. (SAP transaction SMGW Goto menu->Logged On Clients)
- Use the test function screen in transaction SE37 to test the new function. The RFC Target System is set to the name of the RFC destination created in the previous step (in this case it is our development system name).
Here are the results:
- The command console will also display some useful information:
The rest of the code is regular Windows Console programming and is very straight forward.
hi,
i’ve implemented your solution. it worked as you detailed. thanks. now i’ll start to tailor it to my needs and see what happens.
how would i run this as a service? i need this listening for a call from SAP on demand. i don’t want a dialogue box open on the server at all times. my thought is not to write anything to the console. also i should comment out the saphost shutdown command line? would it be good measure to create another console executable to do the saphost shutdown so i can run that if i need to?
Hi,
I’ve implemented your solution but when I want to launch the RFC Server, I’ve this error :
Error encounterd while starting the server:
LOCATION CPIC (TCP/IP) on local host with Unicode
ERROR service ‘?’ unknown
TIME Tue Dec 13 16:39:56 2011
RELEASE 720
COMPONENT NI (network interface)
VERSION 40
RC -3
DETAIL NiErrSet
COUNTER 2
Do you know which is the service concerned ?
Thanks
check the gatewayserice in the class implementing IServerConfiguration
e.g, RfcConfigParameters.GatewayService, “SAPGW00”)
Now, you have to add below line inside C:Windowssystem32driversetcservices file
sapgw00 3300/tcp
cheers!!!!!
busy converting a sap rfc server written against the sap connector 2 to one based on the 3 version; all ok except when sap call one of my functions with a table I only see it in the meta data, GetTable tells me its not there?
gratefull for any clues
[…] The SAP RFC Function Interface All SAP NCo 3.0 RFC servers communicate with SAP through a SAP service called the gateway. The server settings configuration section defines how the program connects to the SAP gateway. In the servers section, the NAME field identifies this app.config entry to our program. Build an RFC Server with NCo 3.0 – A Step-By-Step Guide | SAP Experts: VMware Virtualization | Con… […]
Hi
I can able to build RFC server using sap connector 3.0 and also service are shows in smgw, it is in active state.but How can i debug function mouduile in .net while we exceute programe in SAP,
Hi guys,
I developed a RFC-Server in NCo 3.0 and tried to use the option “Start on Front End Workstation”. The exe is on my PC. When I do a connection test, my exe starts and the RFC Server starts, but in SAP I get a timeout after a few seconds.
Does anybody have a tip for me how it´s possible to use the “Start on Front End Workstation” with my RFC server?
With thanks,
Veit
The “Start on Front End Workstation” option uses the SAP GUI to start an EXE and coordinate communication between the executable on the end user’s computer and the NetWeaver server. For example, if you make the command “notepad.exe” and tested the RFC connection in SM59, Notepad will start on your machine (assuming you’re using a Windows OS). Beyond that, unfortunately, I am unsure how the communication works. I do not believe that the NCo 3.0 library has anything to assist with this style of RFC communication.
Hi Craig,
I think, I got the solution to use the Start on Front-End Workstation option.
In your .NET application you have to pass the start arguments of the exe over to your RFCservre component.
Like this (C# code):
…
static void Main (string [] args)
{
…
RFCServer.Start (args);
…
}
Then the connection test works well in the SM59 and the RFCServer is accessible from SAP!
Best regards
Veit
Hi Craig,
We have implemented NC0_30 for use with SAP 70 (among others of course) and this helped nicely.
Thanks for posting this valuable piece.
Tim van Steenbergen
Hi,
I’m unable to download the source code.
Can you please fix it?
Thanks,
Homer
After filling out the download request form, the sample source code will be sent to you via email. Please check your junk mail folder for an email from info@dataxstream.com. You may need to add a rule to allow email with attachments from that address.
Hi Craig,
This turns into a chat (-:
I’ve done today several tries to two email addresses.
In both emails the spam folder is empty…
I’m sending another form just now (o**ico**n@gmail.com – I don’t want to publish my email).
Regards,
Homer
I’ve just sent you an email from my account (cstasila@dataxstream.com). Let me know if you don’t get it in the next couple of minutes.
Got it, thanks
Hi Craig, is it support SAP Load ballancing, how?
NCo RFC Servers do support calls from load balanced SAP systems. All of the necessary configuration and setup to support calls from load balanced SAP systems occurs in SAP–the .NET code does not have to change. The key is to make sure that the RFC destination is configured correctly to use the SAP gateway on the central instance. Talk to your BASIS administrator for help on setting up the RFC destination with gateway parameters.
Hi Craig,
I have implemented a .Net RFC server using .Net Framework 4.5 x64 and .Net Connector 3.0. I also have a function module defined in SAP that is used to test the RFC server. In other words, from SAP, I can invoke the function module, and have my .Net code execute. The RFC server works well most of the time, however, there are times when I execute the function module to invoke my RFC server, and it hangs. Do you know what is causing it to hang. On the .net server side, there are NO LOOPS, or anything that can cause the code to execute in an infinite loop. All I am doing is reading a value from memory, and populating an IRFCStructure. Additionally, there are a few utility methods that read application configuration settings from the app.config file. I have included my code below. The GetMarketData static method is the entry point from SAP. Do you know why there are times, when the RFC function module hangs??
Here are the configuration settings I have on the .Net server side
public class T4RFCMethodLibrary
{
private static void SetStatusMessage(IRfcFunction function, string statusType, string statusId, string statusNumber, string statusMsg)
{
IRfcTable rfcStatusTable = null;
rfcStatusTable = function.GetTable(“TBL_RETURN”);
rfcStatusTable.Append();
rfcStatusTable.SetValue(“TYPE”, statusType);
rfcStatusTable.SetValue(“ID”, statusId);
rfcStatusTable.SetValue(“NUMBER”, statusNumber);
rfcStatusTable.SetValue(“MESSAGE”, statusMsg);
}
AndeLogger _logger;
[RfcServerFunction(Name = “Z_HDO_T4_GETMARKET_DATA”)]
//To run from sap, set RFC_TARGET_SYS = T4_CONNECT_D
public void GetMarketData(RfcServerContext serverContext, IRfcFunction function)
{
string sapMarketId = string.Empty;
AndeMarket market = null;
sapMarketId = function.GetString(“IM_MARKETID”);
sapMarketId = sapMarketId.ToUpper();
IRfcStructure outputStructure = null;
_logger = new AndeLogger();
try
{
outputStructure = function.GetStructure(“EX_PRICE_INFO”);
if (Utility.T4IsDownForSystemMaintenance())
{
outputStructure.SetValue(“PRICE_BID”, “0”);
outputStructure.SetValue(“PRICE_OFFER”, “0”);
outputStructure.SetValue(“PRICE_LAST”, “0”);
outputStructure.SetValue(“PRICE_SETL”, “0”);
outputStructure.SetValue(“MARKET_STATUS”, “CTS_DOWN”); //T4 is down
SetStatusMessage(function, “I”, “00”, “398”, “T4 is down for system maintenance”);
return;
}
/*
if (Utility.MarketIsOpen())
outputStructure.SetValue(“MARKET_STATUS”, “MARKET_OPEN”);//market is open
else if (Utility.MarketIsSuspended())
{
outputStructure.SetValue(“MARKET_STATUS”, “MARKET_SUSPENDED”);//market is suspended
}
else
{
outputStructure.SetValue(“MARKET_STATUS”, “MARKET_CLOSE”);//market is closed
}
*/
//We did not time out…
market = T4DataStore.GetDataStore().GetAndeMarket(sapMarketId);
if (market == null && Utility.MarketIsOpen())
{
outputStructure.SetValue(“MARKET_STATUS”, “MARKET_ERROR”); //Set error condition for market
SetStatusMessage(function, “I”, “00”, “398”, “Market is open or suspended but no price tick exists”);
}
/*
if (market == null)
{
//outputStructure.SetValue(“MARKET_STATUS”, “MARKET_ERROR”); //Set error condition for market
_logger.WriteErrorMessage(Utility.ERROR_MESSAGE_TITLE, “MarketID ” + market.MarketID + ” Market status: ” + market.AndeMarketMode.ToString());
//SetStatusMessage(function, “I”, “00”, “398”, “Market is open or suspended but no price tick exists”);
}
*/
if (market != null)
{
//outputStructure.SetValue(“MARKET_STATUS”, market.AndeMarketMode.ToString()); //Set error condition for market
_logger.WriteErrorMessage(Utility.ERROR_MESSAGE_TITLE, “MarketID ” + market.MarketID + ” Market status: ” + market.AndeMarketMode.ToString());
//Markets are open dont show settlment price
if (string.IsNullOrEmpty(market.BidPrice))
{
outputStructure.SetValue(“PRICE_BID”, “0”);
}
else
{
outputStructure.SetValue(“PRICE_BID”, market.BidPrice);
}
if (string.IsNullOrEmpty(market.OfferPrice))
{
outputStructure.SetValue(“PRICE_OFFER”, “0”);
}
else
{
outputStructure.SetValue(“PRICE_OFFER”, market.OfferPrice);
}
if (string.IsNullOrEmpty(market.LastTradePrice))
{
outputStructure.SetValue(“PRICE_LAST”, “0”);
}
else
{
outputStructure.SetValue(“PRICE_LAST”, market.LastTradePrice);
}
if (string.IsNullOrEmpty(market.Currency))
{
outputStructure.SetValue(“CURR_KEY”, string.Empty);
}
else
{
outputStructure.SetValue(“CURR_KEY”, market.Currency);
}
//Show settlement price when the market is closed. Don’t show settlement price when market is open or suspended
if (!Utility.MarketIsOpen() && !Utility.MarketIsSuspended())
{
if (string.IsNullOrEmpty(market.SettlementPrice))
{
outputStructure.SetValue(“PRICE_SETL”, “0”);
}
else
{
outputStructure.SetValue(“PRICE_SETL”, market.SettlementPrice);
}
}
/*
if (market.AndeMarketMode != AndeMarketMode.Open && market.AndeMarketMode != AndeMarketMode.Suspended)
{
if (string.IsNullOrEmpty(market.SettlementPrice))
{
outputStructure.SetValue(“PRICE_SETL”, “0”);
}
else
{
outputStructure.SetValue(“PRICE_SETL”, market.SettlementPrice);
}
}
* */
if (string.IsNullOrEmpty(market.BidPrice) && string.IsNullOrEmpty(market.OfferPrice) && market.LastTradePrice == “0”)
{
/*
if (market.AndeMarketMode == AndeMarketMode.Open)
SetStatusMessage(function, “I”, “00”, “398”, “Market is open. We only have 0s coming down from the exchange”);
else if (market.AndeMarketMode == AndeMarketMode.Suspended)
SetStatusMessage(function, “I”, “00”, “398”, “Market is suspended. We only have 0s coming down from the exchange”);
else
SetStatusMessage(function, “I”, “00”, “398”, “Market is closed. We only have 0s coming down from the exchange”);
*/
if (Utility.MarketIsOpen())
SetStatusMessage(function, “I”, “00”, “398”, “Market is open. We only have 0s coming down from the exchange”);
else if (Utility.MarketIsSuspended())
SetStatusMessage(function, “I”, “00”, “398”, “Market is suspended. We only have 0s coming down from the exchange”);
else
SetStatusMessage(function, “I”, “00”, “398”, “Market is closed. We only have 0s coming down from the exchange”);
}
else
{
/*
if (market.AndeMarketMode == AndeMarketMode.Open)
SetStatusMessage(function, “I”, “00”, “398”, “Market is open.”);
else if (market.AndeMarketMode == AndeMarketMode.Suspended)
SetStatusMessage(function, “I”, “00”, “398”, “Market is suspended.”);
else
SetStatusMessage(function, “I”, “00”, “398”, “Market is closed.”);
*/
if (Utility.MarketIsOpen())
SetStatusMessage(function, “I”, “00”, “398”, “Market is open.”);
else if (Utility.MarketIsSuspended())
SetStatusMessage(function, “I”, “00”, “398”, “Market is suspended.”);
else
SetStatusMessage(function, “I”, “00”, “398”, “Market is closed.”);
}
}
}
catch (Exception ex)
{
_logger.WriteErrorMessage(Utility.ERROR_MESSAGE_TITLE, ex.Message, ex.StackTrace);
SetStatusMessage(function, “E”, “00”, “398”, ex.Message);
}
}
}
sorry to bother you but we want to register on a message server, providing a gateway results in a connect to 33xx, the message server sits on port 36xx and so we left out the gateway, oops, GATEWAY_HOST missing on rfc server start … at 3.0.11 right now, any clue? tia
When used as an RFC server, the .NET Connector must communicate with an SAP Gateway. SAP Gateways communicate in port range 33NN where NN is your system number.
In cases where there are multiple application servers, you need to configure your NCo Server program to connect to one of the servers (usually the CI, but any highly available server in the cluster will do). To do this, you need to specify the gateway to which your NCo server is registered in the RFC destination (transaction SM59).
https://www.dataxstream.com/wp-content/uploads/SM59-Gateway-Options.png.webp
In this example, all calls to the RFC destination will be forwarded to host SAPD17 on port 3300 (sapgw00).
Hello Craig!
Thank you for the tutorial, it is the only usable tutorial I found in the whole web, regarding NCo 3 and RFC Server. I was unable to download the source code, maybe your download service is stopped. But the code sniplets in the tutorial were almost enough. In my opinion, these additional 3 rows should be added to the tutorial:
Instead of just
SapHost.Start();
There should be some more rows of code:
SapHost.Start();
Console.WriteLine(“Server running. Press key to stop…”);
Console.ReadLine();
SapHost.Shutdown(true);
BR/ Lars
Hello Craig!
Thank you for your wonderful Step-by-step Guide . I have few doubts which are stated below . Please help us to resolve it.
Our Scenario:
Our SAP system is running on UNIX OS. Our ABAP program wants to execute one .Net exe application installed in a separate Windows server ( Windows 2008 R2 64 bit) . Both the SAP server and This windows server are exist in the same server zone and when ping windows server from SAP server we get reply from windows server. We are using SAP .NET Connector (NCo) 3.0 ( x64)
Doubts:
1. Do we have to install SAP gateway (stand alone) in windows server to execte exe file from SAP. If yes then what to do?
2. If we need not to install any separate gateway for our purpose then what are the steps do we have to follow, mainly the configuration parts in SM59?
Please help us to clear our doubts. Thank you again for your “Build an RFC Client with NCo 3.0 – A Step-By-Step Guide” > Following this guide , we are able to access SAP FM from Client application.
-pkb
Hi
how do you read the file app.config.
It’s possiple to bring the Information from app.config to RfcHandlers because it’ s type.
I don’t found anything in the sample
Peggy
Hi Craig,
Thanks for posting this.
I have gone through and executed both your Buiding RfcClient and RfcServer step by step guide.
Actually my requirement to host RfcServer and handle incoming requests. I am not able to trigger any RfcMethod from SAP since I am directly working with SAP systems or even I have access to.
In that case, I was hoping that I will develop RfcClient myself and make a dummy call to STFC_CONNECTION to receive that call on RfcServer sample I am running. This scenario does not seem to work.
I think I am missing something. RfcClient does not hae programId , GWHost etc to specify and redirect the call to my locally hosted RfcServer
Please suggest if you have any idea about this scenario or if at all, this is possible.
Hi,
thanks for your tutorial. But I have a problem with the start of STFC_CONNECTION. When I execute STFC_CONNECTION nothing in the RFC Server happend. In the TA SMGW the RFC Server is listening.
Could you help?
regards
Tobias
Dear all,
i need all crud operations using SAP.NET RFC using C#.NET .any one help me .am new to this .am interesting to use it.can help me .
Hi,
We are using the RFC SDK for RFC Server and in that we are calling the SDK function RFC_REMOTE_EXEC to pass a table parameter to the RFC server…
Now I want to move on to NCo 3.0 platform and I want to use the same server function RFC_REMOTE_EXEC as I dont want to discriminate the Servers running NCO and SAP SDK . Can anyone help me out in implementing this….
I am using the Below ABAP code at SAP:
CALL FUNCTION ‘RFC_REMOTE_EXEC’ DESTINATION DEST
EXPORTING
COMMAND = ‘LISTN’
TABLES
PIPEDATA = TABLE //Passing The Table
EXCEPTIONS
SYSTEM_FAILURE = 2.
ENDIF.
NCo Server Code:
[RfcServerFunction(Name = “RFC_REMOTE_EXEC”)]
public static void RfcServerFunction(RfcServerContext ctx, IRfcFunction function)
{
string reqtext = null;
RfcDestination rfcDestination = RfcDestinationManager.GetDestination(“Qua”);
RfcRepository rfcRepository = rfcDestination.Repository;
IRfcStructure rfcStructure = rfcRepository.GetStructureMetadata(“S_TABLE”).CreateStructure();
Console.WriteLine(“Received function call {0} from system {1}.”,
function.Metadata.Name,
ctx.SystemAttributes.SystemID);
IRfcTable rfcServer = function.GetTable(“TABLE”);
if (rfcServer.Count == 1)
{
reqtext = rfcServer.GetValue(“SHNUMBER”).ToString();
Console.WriteLine(“REQUTEXT = {0}n”, reqtext.ToString());
}
}
When I am calling this function at DEST SAP is giving SYSTEM_FAILURE excepection.
Hello,
The download request form does not exist anymore.
Could you please send me the source code?
Thanks!
Hi Craig,
Thanks for posting this.
I have gone through and executed your RfcServer step by step guide, when i am debugging it is executing without errors on .net side but the rfc server is not visible in SMGW T-code on SAP side and connection is not established.
Can u please help.
Thanks
Shubham