Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
thomas_weiss
Active Participant


SAP .NET Connector 3.0 is now released and you surely want to know more about it. In this weblog I want to

  • Give you a very short overview on what is new with the SAP .NET Connector 3.0

  • Show a very simple demo application on how to use the .NET Connector as a stateless client

  • Present a simple example of a stateless NCo server scenario.


By learning some relevant details of the two demo applications you will get some understanding of the new programming model of the .NET Connector 3.0. In addition, I mention some other use cases such as stateful or transactional scenarios without going into any details.

The Benefits of .NET Connector 3.0


For mass transactions or heavy load scenarios the NCo is an important alternative to Web Services or SAP Process Integration. Maybe you are still using NCo 2.0 and do not really know if and why you should upgrade to the next NCo release. So what is the benefit of upgrading to NCo 3.0 and why should you invest in this procedure. After reading this weblog you should be convinced that the payoff for upgrading to NCo 3.0 is worth it. In particular you will learn that NCo 3.0:

  • Is more stable, robust and more secure

  • Enforces a better design of your application by decoupling the connection handling from the .NET application proper

  • Encourages separation of concerns: infrastructure embedding is clearly separated from business application logic

  • Reduces memory consumption

  • Allows dynamic programming without the need for proxy generation

  • Can dynamically look up metadata (so if something changes in the related ABAP system - e.g. if parameters in a function module signatures are added or the ABAP system is switched from Non-Unicode to Unicode, you no longer need to regenerate the proxies and recompile your solution)

  • Is no longer bound to a special Visual Studio version.


NCo 3.0 brings a complete redesign of the NCo API and a new architecture that also requires some redesign of the applications using the NCo. But as you will learn in this weblog, it is worth redesigning your applications so that they can use the new .NET Connector 3.0. The redesign of the NCo is the result of a long experience with NCo 2.0 as well as with SAP Java Connector 2.1 and 3.0. SAP has capitalized on the benefits of the NCo 2.0 and improved its strengths and as a result we have a far more powerful NCo.

This weblog is a spotlight that should give you an impression of what using the new .NET Connector 3.0 is like and a first idea of how to work with this new release of the NCO. If you are interested in some more details look at this overview document on which my weblog is based.

If you want to download the .NET Connector 3.0 and you have a user in the SAP Service Marketplace you can download the new NCo and its documentation here: https://service.sap.com/connectors -> SAP Connector for Microsoft .NET.

The Client Programming Model – Providing the Configuration


SAP has spent quite some effort on making the new NCo more secure and has placed a special emphasis on security concerns. In particular it is now much more difficult for an intruder

  • To read the logon data,

  • To replace the logon data in order to get a backend session on the ABAP server with more authorizations and

  • To get access to an open connection that was opened for another application.


In order to store configuration data in a secure way you can now keep it on a central LDAP server and read it on the fly from an LDAP by using the following approach:

When the .NET Connector needs logon data to open an RFC connection to an Application Server ABAP, it looks into your code for the relevant information. In order to provide it you need to implement the interface IDestinationConfiguration and register an instance of that class with the .NET Connector when you start your application. These are the necessary steps:

  1. Implement the method IDestinationConfiguration.GetParameters( string destinationName). Inside this method you can retrieve the relevant configuration data in any way you like, for example read the necessary logon parameters from a database or LDAP directory or prompt the user to input them in a dialog window. If you don’t know logon parameters for a system called destinationName, then just return null.

  2. Create an instance of the above implementation and register it with the NCo using the method RfcDestinationManager.RegisterDestinationConfiguration().

  3. You can now make client calls to the relevant destinations and NCo will automatically get access to the logon information for each backend system by calling the GetParameters() method mentioned above.


Using this approach you obviously can write simple configurations for demo scenarios that have the configuration in clear text in the code as well as real world configurations that draw the relevant configuration data from an LDAP server, an encrypted file or some other source of your choice.

Once the .NET Connector has initialized a connection pool for a particular RFC destination, there is no further need to provide the configuration data for any connections belonging to this destination. Still if necessary it is possible to change the configuration data and to inform you application about this by using the event

public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged in the interface IDestinationConfiguration.

The Client Programming Model – Creating the Client Code


One main difference to .NET Connector 2.0 is that there is no need to pay any attention on how to handle the connections. The internal technical details of opening, closing, and managing connections are no longer handled by the application developer. Instead, the complete connection management is done by the .NET Connector. You no longer have to worry about this. Another important difference lies in the way the parameters that are exchanged with the ABAP application are handled.

Take a look at the simple application below to get a first impression of what working with the .NET Connector 3.0 is like. This way you will easily grasp many of the advantages of the new NCo 3.0.

As I mentioned, for creating a demo application it is, of course, still possible to store the configuration data including the password in clear text in a configuration file. In our little demo here, we take the password from a simple implementation of the provider method GetParameters of a class that implements the interface IDestinationConfiguration. But you should not do this in a real world application. It is just a handy way to provide the logon information in demo scenarios.

In order to give you an example that runs on your system, I show you a complete small application. So this is what the class providing our configuration data plus some declarations looks like:

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SAP.Middleware.Connector;
namespace ConsoleApplication1.obj
{
public class MyBackendConfig : IDestinationConfiguration
{
public RfcConfigParameters GetParameters(String destinationName)
{
if (“PRD_000”.Equals(destinationName))
{
RfcConfigParameters parms = new RfcConfigParameters();
parms.Add(RfcConfigParameters.MessageServerHost, "some.ABAP.host");
parms.Add(RfcConfigParameters.LogonGroup, "PUBLIC");
parms.Add(RfcConfigParameters.SystemID, "ABC");
parms.Add(RfcConfigParameters.User, "user");
parms.Add(RfcConfigParameters.Password, "password");
parms.Add(RfcConfigParameters.Client, "100");
parms.Add(RfcConfigParameters.Language, "en");
parms.Add(RfcConfigParameters.PoolSize, "5");
parms.Add(RfcConfigParameters.MaxPoolSize, "10");
parms.Add(RfcConfigParameters.IdleTimeout, "600");
return parms;
}
else
{
return null;
}
}
// The following two are not used in this example:
public bool ChangeEventsSupported()
{
return false;
}
public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;
}
} 

 

As mentioned above, you need a class that implements the relevant interface IDestinationConfiguration and an implementation of the method GetParameters that puts the relevant values in the returning parameter of the method.

Creating the client coding is very simple and straight forward:

 
public static void Main(string[] args)
{
RfcDestinationManager.RegisterDestinationConfiguration(new MyBackendConfig());
//1
RfcDestination prd = RfcDestinationManager.GetDestination("PRD_000");
//2
try
{
RfcRepository repo = prd.Repository;
//3
IRfcFunction companyBapi = repo.CreateFunction("BAPI_COMPANY_GETDETAIL");
//4
companyBapi.SetValue("COMPANYID", "001000");
//5
companyBapi.Invoke(prd);
//6
IRfcStructure detail = companyBapi.GetStructure("COMPANY_DETAIL");
String companyName = detail.GetString("NAME1");
//7
Console.WriteLine(companyName); Console.Read();
}
catch (Exception)
{
// next you should handle all the relevant exceptions, but
// we will not do this here
// some exception handling
}
}

 

Though the example is very simple just some comments on the relevant steps (confer the outcommented digits at the end of the lines):

  1. Register the relevant destination object.

  2. Get the destination corresponding to the ABAP system you want to call.

  3. Next you need a repository for the metadata from the ABAP Dictionary of the corresponding destination system.

  4. Create a function object (that will hold the values of the relevant parameters)

  5. Provide the values for the relevant importing parameters of the function.

  6. Execute the function.

  7. Read the exporting or returning parameters and process them.


If you are familiar with .NET Connector 2.0 two main differences will strike you:

  • You do not open or close any connection yourself. There is no need to do it. And, in general you cannot grab a connection itself. You create a destination and the .NET Connector provides a connection when you need one, keeps it in a pool, closes it after the given idle timeout and takes care of all of the work related to this.

  • There are no proxy classes, but just function classes that serve as containers for the relevant parameters.


If you take care of closing all the brackets in the example, you can copy it to your development environment, reference the relevant .NET Connector libraries and input the configuration data for a real Application Server ABAP that has the BAPI BAPI_COMPANY_GETDETAIL. If the user that you provide in your configuration has the authorization to call this BAPI the program will run on your system and output a result in the console

You wonder why I have not told you which development environment is needed for the .NET Connector 3.0. There is no need to. This release does not support and as a consequence not depend on any particular development environment. To those of you who have worked with the .NET Connector 2.0 this is also new, because this is a major difference.

How to Keep the State


The example I have shown you works with a stateless synchronous RFC which means that the invoke method not only gets a connection from the relevant pool and executes it, but also resets the session on the ABAP system and returns the connection back to the pool.

A stateless call will not do if you need to call two BAPIs and the second BAPI has to run in the same context on the ABAP Application Server as the first one. So you need a way to tell the .NET Connector that you now need a connection that is kept open until you want the .NET Connector to close or return it to the connection pool, in other words you need to inform the .NET Connector that you need a stateful connection.

Still, also in the case of a stateful connection, the technical handling of the connection is left to the .NET Connector. Again there is no need for you to grab a connection yourself. The .NET Connector does all the connection handling and your code looks like this:


RfcSessionManager.BeginContext(destination);

firstBAPI.Invoke(destination);

secondBAPI.Invoke(destination);

RfcSessionManager.EndContext(destination);

 


With BeginContext() you get an RFC connection for your destination from the connection pool and attach it to the currently executing thread for exclusive usage. In other words with this statement you reserve an RFC connection for your thread.

The two Invoke() calls for the first and second BAPI check, whether there is a connection bound to the current thread, and if they find one, they use it for their call. Also the context of the relevant ABAP session is kept until the EndContext() detaches the exclusive RFC connection from the current thread, performs a backend context reset on that connection and returns the connection to the pool.

If you work in the context of a .NET Server with a session management of its own you just need to create your own ISessionProvider object and register it with the RfcSessionManager. But this is something I will not show or explain in any detail here.

The .NET Connector as a Server


If you use the NCo 3.0 as a server the philosophy behind the programming model is the same as in the client use case: The NCo handles many things for you so that you need not worry about many problems, because the SAP connectivity team has solved these problems for you.

In particular this means: First of all, as in the client case, the management of the connections is done completely by the NCo, and again you can store your configuration data outside the application proper wherever you like in any form you like. Secondly, you need no longer trouble yourself with another quite complex task that you had to take care of with the former release of the NCo: By default, the thread management is done by the NCo 3.0.

Again I will show all relevant steps by guiding you through a simple example that presents a basic stateless NCo server. It consists of three new classes:

  • A class that provides the server configuration

  • A handler class that contains the code that should be executed if the relevant function module is called from the ABAP system.

  • The server class.


In addition the client configuration class from the client configuration is also needed.

Providing the Server Configuration


So let us start with the class providing the server configuration:

 
public class MyServerConfig : IServerConfiguration
{
public RfcConfigParameters GetParameters(String serverName)
{
if (“PRD_REG_SERVER”.Equals(serverName))
{
RfcConfigParameters parms = new RfcConfigParameters();
parms.Add(RfcConfigParameters.RepositoryDestination, “PRD_000”);
parms.Add(RfcConfigParameters.GatewayHost, “demoserver”);
parms.Add(RfcConfigParameters.GatewayService, “sapgw00”);
parms.Add(RfcConfigParameters.ProgramID, “DEMO_SERVER”);
parms.Add(RfcConfigParameters.RegistrationCount, “5”);
return parms;
}
else
{
return null;
}
}
// The following code is not used in this example
public bool ChangeEventsSupported()
{
return false;
}
public event RfcServerManager.ConfigurationChangeHandler ConfigurationChanged;
}

 

As in the client example we first need a class to provide the necessary server configuration, technically this is a class that implements the interface IServerConfiguration and as a consequence has a GetParameters method. In this case we define a configuration with the name PRD_REG_SERVER.

For the server in NCo 3.0 with dynamic metadata lookup, you need two connections: one client connection for the DDIC lookup and a server connection to enable the ABAP system to call the .NET Connector server. And as a consequence you need two sets of configuration data:

As for the client connection we reuse the configuration data from the previous client example PRD_000. This means that you have to include the class MyBackendConfig (but without its main method, because we do not need this method here) if you want to run this server example.

As for the server connection you have to connect to the ABAP system by using an intermediate SAP Gateway. This is shown in the figure below:



To those familiar with how to configure an NCo 2.0 server it will be no news how you connect via the intermediate SAP Gateway. Still, I want to spend some words on it for those who don’t know how to do this. You need these three values:

  • Hostname (the host on which the SAP Gateway runs),

  • Gateway service (you can compare this to a port. It has to be sapgwxx where the last double x stands for the system number) and

  • Program ID


in order to identify the server destination uniquely. You must provide the same values for these parameters both when defining the .NET server configuration as shown above in the code example and when defining the RFC destination as a TCP/IP connection in the transaction SM59 as shown below in the next figure:



So much for our explanation of how to get the configuration data of our .NET server program. Next we need a class that provides the handler method for a call from an ABAP system that calls a function module STFC_CONNECTION on our .NCo server.

Creating a Handler Method in the Server


In general to provide one or more handler methods, we have to implement one or more methods that will process incoming RFC calls. In our example we choose one static method for each function module. We assign an annotation to the method as shown below:

[RfcServerFunction(Name = "STFC_CONNECTION", Default = false)]

public static void Function(RfcServerContext context, IRfcFunction function)

The two parameters context and function are provided by the .NET Connector runtime when the method is called.

You could also have one static method as a default for all function modules that don’t have an explicit method assigned to them. Just set the attribute Default in the annotation to true. It is also possible to work with instance methods in the same manner. You should use this if you create a stateful server, but I will not show this in any detail here in this introductory weblog.

So this is what the handler method for the function module STFC_CONNECTION looks like in our example:

 
public class MyServerHandler
{
[RfcServerFunction(Name = "STFC_CONNECTION")]
public static void StfcConnection(RfcServerContext context, IRfcFunction function)
{
Console.WriteLine("Received function call {0} from system {1}.", function.Metadata.Name, context.SystemAttributes.SystemID);
String reqtext = function.GetString("REQUTEXT");
Console.WriteLine("REQUTEXT = {0}\n", reqtext);
function.SetValue("ECHOTEXT", reqtext);
function.SetValue("RESPTEXT", "Hello from NCo 3.0!");
}
}

 

We output the parameter REQUTEXT on the console, return the value of this parameter in the parameter ECHOTEXT and put some variation of "Hello World” in the parameter RESPTEXT. Both parameters: ECHOTEXT and RESPTEXT are returned to the ABAP client.

Create and Run the Server


With the configurations defined and a handler class with the relevant handler method prepared we have now all we need at hand to write the server class:

 
public class MyServerDemo
{
public static void Main(string[] args)
{
RfcDestinationManager.RegisterDestinationConfiguration(new MyBackendConfig());
//1
RfcServerManager.RegisterServerConfiguration(new MyServerConfig());
//2
Type[] handlers = new Type[1] { typeof(MyServerHandler) };
//3
RfcServer server = RfcServerManager.GetServer("PRD_REG_SERVER", handlers);
//4
server.Start();
//5
Console.WriteLine("Server has been started. Press X to exit.\n");
while (true)
{
if (Console.ReadLine().Equals("X"))
break;
}
server.Shutdown(true);
// Shuts down
// immediately aborting ongoing requests.
}
}

 

This is what happens in this method (see the commented-out digits at the ends of the lines):

  1. Register the destination for the DDIC lookups.

  2. Register the server configuration.

  3. Register the handler(s) and create a server instance by passing the server name and an array with the handler(s) to the constructor method.

  4. Start the server.


That’s all. This simple code suffices to set up a server that can not only handle calls of the function module STFC_CONNECTION, but handle up to 5 parallel calls because we have set the RegistrationCount to 5 in the MyServerConfig class.

Just remember: To make the program run, copy the code of all four classes MyBackendConfig (without its main method), MyServerConfig, MyServerHandler, MyServerDemo plus the information on used name spaces to your .NET development environment. You must also adapt the configuration data accordingly so that you can connect to a real SAP Gateway and a real ABAP Application Server.

And Much More


Of course there is much more that you can do with the new .NET Connector 3.0 and much of it is described in the overview document I have already linked above. Just to mention a few more scenarios that are possible with NCo 3.0:

  • When creating a NCo client you can use transactional RFC tRFC/qRFC/bgRFC to make sure that the remote call is executed exactly once.

  • In an analogous manner you can also use tRFC/qRFC/bgRFC in the server use case.

  • You can write a stateful NCo server program interacting with .NET programs that have their own user session handling and with .NET programs that do not have this.


Apart from any details, all these use cases have one feature in common: You need not directly interact with connections, that is, open and release them. Instead all connection handling is done transparently from you by the .NET Connector 3.0. All you have to do is to provide the relevant configuration data. Of course the tRFC/qRFC/bgRFC examples are more complex as they require you to provide the storage of the TID and the payload data and some more code. But this is another story and as many other details beyond the scope of this introductory weblog.

Summary


You have now seen how easily you can write a stateless client and stateless server scenario using the .NET Connector 3.0. You certainly have realized that the code is far more transparent because of the separation of concerns realized in the new .NET Connector architecture:

  • The configuration is left to an administrator and can easily be stored outside the application proper. This makes the application code more straightforward and easier to maintain. Moreover you can adapt or change the destination without touching the application code.

  • As an application developer you do not need to fiddle with the connections, but leave the complete connection management to the .NET Connector.

  • This is true even for stateful scenarios. You just have to tell the NCo for which lines of code you need a stateful connection. And this is all. Again you work only with destinations and leave the handling of the connections proper to the .NET Connector.


As mentioned, other scenarios such as transactional calls are also possible in the server and the client case. They are sketched elsewhere. I hope this weblog has shown you some of the many advantages of the NCo 3.0 and inspires you to try out the new .NET Connector 3.0. I am sure that sooner or later, you will adapt your existing applications for NCo 2.0 to NCo 3.0 and thereby make them more transparent and secure and profit from the better design that comes with the separation of concerns.

 

 

146 Comments
Former Member
0 Kudos
Hi,
Current NCO 3.0 is working with .NET Framework 4? Did you try it?
former_member212758
Discoverer
0 Kudos
I sucessfully replicated the c# example here.

I totally failed to recreate the same example in .net VB.

I in need to connect to R/3 from Excel VBA and there is no example anywhere. Is it even possible?

Thank you

Juraj
former_member194419
Participant
0 Kudos
Hi,

I tried download, but shows the message:
Error Message - ObjectID not existant
thomas_weiss
Active Participant
0 Kudos
Hello Rodrigo,
we have tested the download and it worked. It might be that you tried to download the file just at the time when we  substituted the files yesterday. Let us know if the problem persists.

Thomas
thomas_weiss
Active Participant
0 Kudos
Hello Rodrigo,
we have tested the download and it worked. It might be that you tried to download the file just at the time when we  substituted the files yesterday. Let us know if the problem persists.

Thomas
thomas_weiss
Active Participant
0 Kudos
Hello Huseyin,

yesterday we have uploaded a new version of JCo 3.0 that works with .NET Framework 4.0.

I propose you should use this version.

Regards

Thomas
Former Member
0 Kudos
Hi Thomas,

When I checked checked version of Nco, there are two different versions; one for framework 2.0 and second is framework 4. I want to ask that will you produce a new version for each framework?

Thanks.

Huseyin Akturk
thomas_weiss
Active Participant
0 Kudos
Hello Huseyin,

there is one version for .NET framework 2.0 to 3.5 and another version for .NET framework 4.0, because .NET framework 4.0 contains incompatible changes.

Regards

Thomas
Former Member
0 Kudos
According to the 2010 SAP Guidelines for Best-Built Applications That Integrate with SAP Business Suite ...

".NET Tools SOA-NET-1.

SAP recommends that .NET developers use the SAP Enterprise Services Explorer tool for Microsoft .NET. SAP does not recommend using .NET connector."

Maybe they need to update this guideline too.
Former Member
0 Kudos
Hi Thomas,

in order to process IDoc's on a Server component,Iam I correct to assume we need to call the IDOC_INBOUIND_ASYNCHRONOUS rfc.

Many Thanks
thomas_weiss
Active Participant
0 Kudos
Hello Alan,

it is possible to use the function module IDOC_INBOUND_ASYNCHRONOUS, but it might be easier to process IDOCs if you use the SAP Business Connector. Use this link to find information on the SAP Business Connector: https://service.sap.com/connectors menu entry SAP Business Connector.


Regards

Thomas
Former Member
0 Kudos
Hi Thomas,

currently we are using the SAPIDocReciver and IDocSender class's from the SAP .Net Connector V.2. This has been incorporated into a Custom BizTalk Adapter. Ideally we would be looking to replace the SAP .Net Connetor Ver 2 layer with Ver 3 of the SAP .Net Connector. Use of the SAP Business Connector would require a much greater change.

If we use the new version of the SAP .Net Connector, will we need to check the DDIC to ascertain what paramters are required for calling IDOC_INBOUND_ASYNCHRONOUS rfc.

I am a .Net/BizTalk programmer but do have full SAP support on site

Many Thanks

Alan
Former Member
0 Kudos
Hi Thomas

Did you faced problems when trying to send a lot of Idocs with .Net Connector 3 ?

Thanx
former_member212758
Discoverer
0 Kudos
Is there any possibility to use .net connector in VBA for instance in excel for Remote functional call?

Any example , if possible.

Thank you

Juraj
Former Member
0 Kudos
Hi Thomas,

just to let you know, I have a prototype program processing inbound/outbound Idocs to/from SAP using Nco 3.

Very impressed with new connector, highly adaptable and dynamic.
Former Member
0 Kudos
Hi Sebastien,

I'm getting no problems sending Idocas to SAP, if you need any sample code drop me a line.
Former Member
0 Kudos
Hi Alan
it wa a bug in the NCo decompressor. i have installed version 3.0.1 and it work fine
Thanx

Best regards
Sébastien
former_member212767
Participant
0 Kudos
Hi,

Is .NET Compact Framework supported ?

Kind Regards,
Jari
Former Member
0 Kudos
Hi Tomas,

Congratulations, great job!!!!

I have two questions.

1) There are support on 64 bits plataform?

2) Does The new version support the code/implementation of .NET Connector 2.0?
Former Member
0 Kudos
Dear Thomas,

We are using SAP.NET Connector 3.0 within a web service to execute different BAPI's from a Visual Studio environment. So far we have succesfully registered a Customer Quotation in SAP from our web service. However, when consuming the web service for a second time (creating a new quotation) we have encountered the following error: "Destination configuration already initialized". We have made the following steps:

1. Implementation of the IDestinationConfiguration.
2. The web method for creating the quotation registers the destination configuration.
3. We use the repository in order to invoke and commit the required BAPI.

Are we missing something? Are we on the wrong track when using SAP.NET Connector 3.0 with web services? We appreciate any help you can give us on this subject.
0 Kudos
Alan,

I am trying to setup my first IDoc receiver server and would very appreciate any guidance or examples you could share.  The only thing I have as reference is the old NCO 2.0 CSServerIdocReceiver example but it doesn't work (obviously) in NCO 3.0.  We are also discussing it in the SDN forums at IDOC processing possible  with SAP .NET Connector 3.0:

Thanks
kai_hbner
Explorer
0 Kudos
Hi Thomas,
in NCo 2.0 I was able to detect a broken communication with overriding SAPServerHost->OnServerException. How I can do this in NCo 3.0? RFCServer events(RfcServerApplicationError/RfcServerError) will not fire in that case.

Kind Regards
Kai
eduardo_gironas
Participant
0 Kudos
Hello:
When SAP Nco gets data into its data structures, an execution time is consumed. When I need to show this data into a .NET DataGrid, It is necessary to transfer data from the SAP structures toward the .Net dataGrid and, another execution time is consumed. Is there a way to avoid this overload situation?
Thanks!!
Daniel_KASIMIR
Participant
0 Kudos
Hello,

I want to use the NCo3 in an asp.net application wich works quite fine, even with a sso logon via kereros and gsskrb5.dll.

But the NCo uses always the session and credentials of the first user who logs in. A second user gets the first connection and can fetch data from sap, although this user does not exist in sap.

How to create different sessions for different web users?
I think I have to implement ISessionProvider but in the NCo3 overview document it is not described how to do it and also not in the help file.

Any ideas?

With kind regards

Daniel Kasimirowicz

Former Member
0 Kudos
You need to implement an Asynchronous callback event
former_member212713
Contributor
0 Kudos
Hi Thomas;
Firstly Thanks for sharing.
I'm using C# VS.net 2010 on the Framework 4.0, WinXP and 32Bits.
I downloaded new NCO 3.
I'm writing WinApplication program at .Net with NCO 3.
But My programs don't work.
I'm taking following link error messages.
Are .net 2010  and framework 4 compatible?
If uncompatible can you suggest me diffrent way?
Thanks for interest.

My problems details:
Framework 4.0 Problem at .net Connector 3.0


Former Member
0 Kudos
Can you please share the code that hows you filled up DataGrid with SAP structure??
eduardo_gironas
Participant
0 Kudos

I created a library related to connections with SAP Nco 3,I show a part of this:        <DataGrid AutoGenerateColumns="False" Height="342" HorizontalAlignment="Left" Margin="0,32,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="625" ItemsSource=""  >Hope it helps.

eduardo_gironas
Participant
0 Kudos
Tha SAPFields class is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LibEGA.LibSAP
{
    public class SAPFields
    {
        public SAPFields()
        {
        }

        public string FieldName {get; set;}
        public int Offset {get; set;}
        public int Length {get; set;}
        public char Type {get; set;}
        public string FieldText {get; set;}
    }
}
Former Member
0 Kudos
When you try to look use one of API's to look at a function meta-data the "Documentation" for parameter is not always correct. If a parameter does not a description in SAP it comes back incorrectly - has description of previous parameter. For example look at results of

"repository.GetFunctionMetadata("RFC_CREATE_FUNC_LOC_MASTER")

Sorry not correct forum to report bugs but I do not have access to report bugs.
MarkusTolksdorf
Product and Topic Expert
Product and Topic Expert
0 Kudos
Hello Daniel,

Yes, you need to implement an ISessionProvider. It should ensure to return the appropriate session ID that is associated with the current request. Regarding the backend session: There you need to make sure to use a custom destination that is overriding the credentials per user. Otherwise only the configured user will be used.

Regards,
Markus

Regards,
Markus
Daniel_KASIMIR
Participant
0 Kudos
Hi Markus,

I found no documentation about the ISessionProvider. Do you know any samples or useful hyperlinks?

Vielen Dank

Daniel
Former Member
0 Kudos
Hello,

this function module does not seem to be standard. I tried to reproduce the issue with RFC_CHECK_DESTINATION which has one import parameter (MYDEST, the first one) that has a short text and one without a short text (MYTYPE, the second one). I get the expected result for the second parameter's documentation, namely null. Is there an SAP-delivered function module that can be used to reproduce the issue?

It is of course also possible that the issue was resolved since we released the current version.

Best Regards,
Matthias Fuchs, NCO3 Development
MarkusTolksdorf
Product and Topic Expert
Product and Topic Expert
0 Kudos
Hello Daniel,

up to now, there is only the API documentation, which is included in the downloadable archive.

Regards,
Markus
Former Member
0 Kudos
Hello,

it appears you are using the .Net 2.0 version of NCO3. Please download the 4.0 DLLs and use those. That should fix your issue.

Regards,
Matthias Fuchs, NCO3 Development
Former Member
0 Kudos
Hi Thomas,
is there a way to create a RfcDestination from a saplogon.ini item (except File.Open()) or from a connectionstring?

Thanks!
Former Member
0 Kudos
Hi,

NCO3 does not directly support this feature, but it can certainly be achieved as you pointed out by reading saplogon.ini and then creating a RfcDestination or a RfcCustomDestination based on RfcConfigParameters produced from saplogon.ini.

Regards,
Matthias Fuchs, Development
0 Kudos
Hello Thomas,

I'm implementing NCo 3.0 in a windows application which makes RFC calls in different threads, just for downloading master data from SAP and for creating documents in SAP. With the downloading of master data I have no problems, but with those RFC functions which needs to call the BAPI_TRANSACTION_COMMIT function, so they need the use of the BeginContext method, I receive, after more or less 10 minutes, an exception error out of my code, so I cannot manage it. The exception message is 'Collection was modified; enumeration operation may not execute', in the object SAP.Middleware.Connector.RfcSessionManager.UpdateSessions. The thing is that, if I don't use the BeginContext method, it works fine, at least I have no error, but I suppose this does not guarantee the transaction commit.

I'm using the BAPI invoke and the Commit invoke in the same thread, for example:

public XmlDocument CreateBillingDocuments(string DestinationName, BILLINGDATAINS Documents)
{
...
SAPRfcDestination = RfcDestinationManager.GetDestination(DestinationName);
RfcRepository SAPRfcRepository = SAPRfcDestination.Repository;
IRfcFunction RFCCreateBillingDocuments = SAPRfcRepository.CreateFunction("BAPI_BILLINGDOC_CREATEMULTIPLE");
...
RfcSessionManager.BeginContext(SAPRfcDestination);
RFCCreateBillingDocuments.Invoke(SAPRfcDestination);
IRfcFunction RFCTransactionCommit = SAPRfcRepository.CreateFunction("BAPI_TRANSACTION_COMMIT");
RFCTransactionCommit.SetValue("WAIT", "X");
RFCTransactionCommit.Invoke(SAPRfcDestination);
RfcSessionManager.EndContext(SAPRfcDestination);
...
}

So I suppose I don't need the implementation of the ISessionProvider interface.

I don't know, maybe is the way I set the destination parameters, but it seems something is trying to change the session collection.

Regards.



Former Member
0 Kudos

Hello,<br/><br/>you found a bug in our session clean-up mechanism. We'll fix it right away. A workaround requires implementing your own session provider. I suggest the following:<br/><br/>class WorkaroundSessionProvider : ISessionProvider<br/>    {<br/>        <br/>        private Dictionary<string, Thread> rfcThreads;<br/><br/>        internal WorkaroundSessionProvider()<br/>        {<br/>            rfcThreads = new Dictionary<string, Thread>();<br/>        }<br/><br/>        public String GetCurrentSession()<br/>        {<br/>            string sessionID = Thread.CurrentThread.ManagedThreadId.ToString();<br/>            if (!rfcThreads.ContainsKey(sessionID))<br/>            {<br/>                rfcThreads[sessionID] = Thread.CurrentThread;<br/>            }<br/>            return sessionID;<br/>        }<br/><br/>        public void ContextStarted()<br/>        {<br/>        }<br/><br/>        public void ContextFinished()<br/>        {<br/>        }<br/><br/>        public String CreateSession()<br/>        {<br/>            return string.Empty;<br/>        }<br/><br/>        public void PassivateSession(String sessionID)<br/>        {<br/>        }<br/><br/>        public void ActivateSession(String sessionID)<br/>        {<br/>        }<br/><br/>        public void DestroySession(String sessionID)<br/>        {<br/>            lock (rfcThreads)<br/>            {<br/>                if (rfcThreads.ContainsKey(sessionID))<br/>                {<br/>                    rfcThreads.Remove(sessionID);<br/>                }<br/>                // fire a session DESTROYED event to clean up the session cache<br/>                SessionChanged(new RfcSessionEventArgs(RfcSessionManager.EventType.DESTROYED, sessionID));<br/>            }<br/>        }<br/><br/>        public bool IsAlive(String sessionID)<br/>        {<br/>            // always return true here to prevent currently flawed clean-up routine to trigger a concurrent modification exception<br/>            return true;<br/>        }<br/><br/>        public bool ChangeEventsSupported()<br/>        {<br/>            // return true here to ensure SessionChanged events are handled<br/>            return true;<br/>        }<br/><br/>        public event RfcSessionManager.SessionChangeHandler SessionChanged;<br/>    }<br/><br/>Register an instance, say sp, of that session provider through RfcSessionManager.RegisterSessionProvider(sp) before the first call to RfcSessionManager.BeginContext(). After each RfcSessionManager.EndContext() (or whenever you find it appropriate) do your own clean up by calling sp.DestroySession(sp.GetCurrentSession()).<br/><br/>I tested the proposed workaround and it appears to do the trick. Let me know if you have any issues with it.<br/><br/>I'd like to point out that there would normally indeed be no reason to implement your own session provider. The purpose of this session provider strictly is to bypass the periodic clean-up activities that are not working properly. Note that it puts the responsibility for removing expired sessions into your hands. So I recommend to remove the workaround as soon as you upgrade to a patch level (presumably the next one) that fixes this issue.<br/><br/>Regards,<br/>Matthias Fuchs, NCo3 Development<br/>

0 Kudos
It's so strange, because although apparently the exception is raised in the object SAP.Middleware.Connector.RfcSessionManager.UpdateSessions, I've commented the afterwards management of the xml I receive from the RFC calls, so I've only executed RFC calls, and after 1 hour it haven't crash, so the collection modified must be a xml nodes or tree nodes in different threads.

Regards.

Former Member
0 Kudos
I'm not aware of the details of your coding, but I can tell you that our default session provider works similar to what I suggested as a work around in that it associates sessions with threads. A session is not removed as long as the thread associated with it is alive. The issue only occurs if a session is considered expired because the associated thread is not alive anymore. Then removing that session from an internal collection incurs the concurrent modification exception. That means as long as the thread that started a context is kept alive, the session will not be removed, and hence there will be no problem.

Best Regards.
0 Kudos
Hello Matthias,

I've implemented your workaround and, after one hour executing RFC calls every two seconds, it has raised an IndexOutOfRangeException in the line rfcThreads[sessionID] = Thread.CurrentThread; of the GetCurrentSession() method, but at least I can manage it. I've substituted it by:

try
{
   rfcThreads.Add(sessionID, Thread.CurrentThread);
}
catch
{
}

Thanks so much, I was getting crazy.
Former Member
0 Kudos
Hello Ana,

that's most likely a synchronization issue. I should have synchronized GetCurrentSession. So I suggest you use the following:

public String GetCurrentSession()
{
string sessionID = Thread.CurrentThread.ManagedThreadId.ToString();
lock (rfcThreads)
{
if (!rfcThreads.ContainsKey(sessionID))
{
rfcThreads[sessionID] = Thread.CurrentThread;
}
}
return sessionID;
}

Regards,
Matthias
0 Kudos
Thanks Matthias,

After one hour it hasn't give any problem.

Best regards.
Former Member
0 Kudos
Hi Thomas,
I need to implement web portal with real time data in SAP.
1. Can I use connector directly to access the Data from SAP?
2. If I am going to use portal DB as SQL Server, how I am going to integrate my portal DB with real time data from SAP??

Swapnil


MarkusTolksdorf
Product and Topic Expert
Product and Topic Expert
0 Kudos
Hi Swapril,

1. This is exactly the purpose of the .NET Connector: Accessing data of an SAP system.
2. It heavily depends on what you want to do exactly. It is certainly possible that you do an integration of portal DB with real time data from an SAP system. Your description is pretty general so that I cannot explain what to do.

Regards,
Markus
Former Member
0 Kudos
Hi Markus,

Thanks..

Markus,Due to consideration of request and response time from .net portal to SAP, I am going to use my portal DB separately. But now thing is that How I have to develope a framework which will keep my portal DB in real time with SAP. Means Data fetching/sending to/from SAP with a prticular time slot.

regards,

Swapnil
roland_giger
Explorer
0 Kudos
Hi
i translate the C# Example in a VB.NET Version

Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports SAP.Middleware.Connector
Public Class Form1
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim objDestConfig As New InMemoryDestinationConfiguration()
RfcDestinationManager.RegisterDestinationConfiguration(objDestConfig)
Console.WriteLine("Registered own configuration")
objDestConfig.AddOrEditDestination("TESTW47", 1, "User", "Password", "DE", "000", "100.100.200.200", "00")
Dim destination1 As RfcDestination = RfcDestinationManager.GetDestination("TESTW47")
Try
destination1.Ping()
objDestConfig.RemoveDestination("TESTW47")
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
End Class
Public Class InMemoryDestinationConfiguration : Implements IDestinationConfiguration
Private availableDestinations As Dictionary(Of String, RfcConfigParameters)
Private changeHandler As RfcDestinationManager.ConfigurationChangeHandler
Dim eventtype As Object

Public Event ConfigurationChanged(ByVal destinationName As String, ByVal args As RfcConfigurationEventArgs) Implements IDestinationConfiguration.ConfigurationChanged
Public Sub New()
availableDestinations = New Dictionary(Of String, RfcConfigParameters)
End Sub
Public Function GetParameters(destinationName As String) As RfcConfigParameters Implements IDestinationConfiguration.GetParameters
Dim foundDestination As RfcConfigParameters
availableDestinations.TryGetValue(destinationName, foundDestination)
Return foundDestination
End Function
Public Function ChangeEventsSupported() As Boolean Implements IDestinationConfiguration.ChangeEventsSupported
Return True
End Function
Public Sub AddOrEditDestination(name, poolSize, user, password, language, client, applicationServer, systemNumber)
Dim parameters = New RfcConfigParameters
parameters.Add(RfcConfigParameters.Name, name)
parameters.Add(RfcConfigParameters.MaxPoolSize, Convert.ToString(poolSize))
parameters.Add(RfcConfigParameters.IdleTimeout, Convert.ToString(10))
parameters.Add(RfcConfigParameters.User, user)
parameters.Add(RfcConfigParameters.Password, password)
parameters.Add(RfcConfigParameters.Client, client)
parameters.Add(RfcConfigParameters.Language, language)
parameters.Add(RfcConfigParameters.AppServerHost, applicationServer)
parameters.Add(RfcConfigParameters.SystemNumber, systemNumber)
Dim existingConfiguration As RfcConfigParameters

If availableDestinations.TryGetValue(name, existingConfiguration) Then
availableDestinations(name) = parameters
'C# - RfcConfigurationEventArgs(EventArgs = New RfcConfigurationEventArgs(RfcConfigParameters.EventType.CHANGED, parameters))
'not translated in BV.Net
changeHandler(name, EventArgs.Empty)
Else
availableDestinations(name) = parameters
Console.WriteLine("Added application server destination " + name)
End If
End Sub
Public Sub RemoveDestination(name As String)
If name <> Nothing AndAlso availableDestinations.Remove(name) Then
Console.WriteLine("Successfully removed destination " + name)
Console.WriteLine("Fire deletion event for destination " + name)
RaiseEvent ConfigurationChanged(name, New RfcConfigurationEventArgs(RfcConfigParameters.EventType.DELETED))
End If
End Sub
End Class
Former Member
0 Kudos
Thanks for the detailed article.

I am trying to upload data into Ztables using a BAPI provided by the client, using C#.

If you have any e.g on this, it will be helpful.

Regards,
Former Member
0 Kudos
Thank's a lot for the Example. We're implemmenting something like that but we're having some problems with a remote server access and BAPI_GOODSMVT_CREATE. When we work with a bapi that has small structures it works good, but when we try to work with a standard Bapi that has many structures an error like this apears: Lookup of function BAPI_GOODSMVT_CREATE metadata failed for destination GROMERO.
It seems to be something related with a delay of the communication.
The detail of the exception is:
Se detectó SAP.Middleware.Connector.RfcInvalidStateException
  Message=Lookup of function BAPI_GOODSMVT_CREATE metadata failed for destination GROMERO
  Source=sapnco
  StackTrace:
       en SAP.Middleware.Connector.RfcRepository.LookupFunctionMetadataClassic(String name)
       en SAP.Middleware.Connector.RfcRepository.LookupFunctionMetadata(String name)
       en SAP.Middleware.Connector.RfcRepository.GetFunctionMetadata(String name)
       en SAP.Middleware.Connector.RfcRepository.CreateFunction(String name)
       en WebService1.Service1.Crea_Entrada_Mercancias(String FecDoc, String FecCont, String NumNot, String NumPed, String Centro, String Almacen, String Material, Int32 Cantidad, String Proveedor) en C:\Users\rvargas\Desktop\Nuevo Sistema MP\WebService1\WebService1\Service1.asmx.vb:línea 46
  InnerException: SAP.Middleware.Connector.RfcInvalidStateException
       Message=Lookup of StructureOrTable BAPI2017_GM_ITEM_CREATE metadata failed for destination GROMERO
       Source=sapnco
       StackTrace:
            en SAP.Middleware.Connector.RfcRepository.LookupRecordMetadataClassic(String name, RecordType recordType)
            en SAP.Middleware.Connector.RfcRepository.LookupRecordMetadata(String name, RecordType recordType)
            en SAP.Middleware.Connector.RfcRepository.LookupFunctionMetadataClassic(String name)
       InnerException: SAP.Middleware.Connector.RfcCommunicationException
            Message=destination GROMERO failed when calling DDIF_FIELDINFO_GET -- see log for details
            Source=sapnco
            StackTrace:
                 en SAP.Middleware.Connector.RfcRepository.Execute(RfcFunction function)
                 en SAP.Middleware.Connector.RfcRepository.LookupRecordMetadataClassic(String name, RecordType recordType)
            InnerException: