cancel
Showing results for 
Search instead for 
Did you mean: 

Crash in NCo 3.0 w/ custom ISessionProvider implementation

Former Member
0 Kudos

I'm implementing a client using NCo 3.0, and need to make a custom session provider as parts of a stateful RFC could come from different threads. As per the documentation, the only real code is in the GetCurrentSession and IsAlive methods. I'm returning false for ChangeEventsSupported.

It works as far as returning the correct session ID when needed but, when the NCo library does its cleanup process later on, it returns false from IsAlive (which is correct, because the session ID being referenced is no longer active) and then the NCo library crashes with the following information:

System.InvalidOperationException

Collection was modified; enumeration operation may not execute.

This exception is happening outside of my code, so I can't catch it. Also, it only happens if I do a stateful RFC (explicitly calling BeginContext/EndContext around my function calls). If I do a synchronous or transactional RFC, nothing bad happens.

Has anyone here implemented a custom session provider? The documentation for doing that is very slim, and the example code that it mentions seems to be non-existent, so I have no idea if I'm somehow doing something incorrectly or if this is a bug in the NCo library. I have a feeling that always returning true from IsAlive will keep it from crashing, but it also seems like that would result in a memory leak.

Accepted Solutions (0)

Answers (3)

Answers (3)

0 Kudos

Hello Maeglin,

Thanks for your quick answer.

So, If I've understood, you provide your own session ID string, but I don't see the relationship between this session ID and the RFCSessionManager object. I mean, if the session ID is not in the list, what do you do (apart of create a new session in your list)?, you register a new ISessionProvider interface with RfcSessionManager.RegisterSessionProvider?, because, as you said, the CreateSession method makes nothing but return an empty string.

And, if you have more than one sessions opened, because you're making different RFC calls in different threads, which sessionID must return the method GetCurrentSession?. I don't see the useful of this method, it should be enough asking if your sessionID is still opened.

Next example is in C# (I hope you can undestand it). In next method I try to create a billing document. Before to call the BeginContext method I should ask for my sessionID calling the method ManageSessions. Here is where I have doubts.

public XmlDocument CreateBillingDocuments(string DestinationName, BILLINGDATAINS Documents)

{

...

SAPRfcDestination = RfcDestinationManager.GetDestination(DestinationName);

RfcRepository SAPRfcRepository = SAPRfcDestination.Repository;

IRfcFunction RFCCreateBillingDocuments = SAPRfcRepository.CreateFunction("BAPI_BILLINGDOC_CREATEMULTIPLE");

...

sessionID = DestinationName + "BAPI_BILLINGDOC_CREATEMULTIPLE";

ManageSessions(sessionID);

RfcSessionManager.BeginContext(SAPRfcDestination);

RFCCreateBillingDocuments.Invoke(SAPRfcDestination);

IRfcFunction RFCTransactionCommit = SAPRfcRepository.CreateFunction("BAPI_TRANSACTION_COMMIT");

RFCTransactionCommit.SetValue("WAIT", "X");

RFCTransactionCommit.Invoke(SAPRfcDestination);

RfcSessionManager.EndContext(SAPRfcDestination);

...

EraseSessionID(); // remove the sessionID from the list and call the DESTROYED event.

}

private void ManageSessions(string sessionID)

{

if (!IsInList(sessionID)) // get if the sessionID is in my list

{

try

{

// register the class which implements ISessionProvider (public class RfcSessionProvider : ISessionProvider)

RfcSessionManager.RegisterSessionProvider(csRfcSessionProvider);

// Call the CreateSession method?

}

catch (RfcBaseException)

{

// Session registered

}

}

else

{

// Make nothing if the sessionID is still alive?

}

}

Best regards.

Former Member
0 Kudos

So, If I've understood...

Not quite.

...you provide your own session ID string, but I don't see the relationship between this session ID and the RFCSessionManager object. I mean, if the session ID is not in the list, what do you do (apart of create a new session in your list)?, you register a new ISessionProvider interface with RfcSessionManager.RegisterSessionProvider?, because, as you said, the CreateSession method makes nothing but return an empty string.

From my last post:

"For synchronous and transactional RFCs (for which you don't need BeginContext/EndContext calls), it returns a string indicating the current thread ID."

I don't know how you'd get the current thread ID in C#, but in C++ the function is GetCurrentThreadId(). I only add a new session ID to my list when I'm calling BeginContext/EndContext and have to manage a stateful RFC session explicitly, generating an ID and adding to the list just before BeginContext, and removing from the list (and firing the SessionChanged event) just after calling EndContext. For synchronous and transactional RFCs, it apparently doesn't matter if you maintain a specific session, because everything happens in one Invoke call (or Commit, in the case of tRFCs).

As for how I know which session ID to return from GetCurrentSession, that's maintained by what's calling into my SAP interface code. The string that contains the current SAP session ID for stateful RFCs is set before BeginContext, EndContext, CreateFunction or Invoke are called in the context of a specific stateful RFC session, and is cleared afterwards. For synchronous and transactional RFCs, I don't set it at all and GetCurrentSession falls back to returning a session ID based on the current thread. As I said before, I'm only using one ISessionProvider implementation, which is set up at the very beginning and cleaned up at the very end of the time that my application is running.

In the case of your example, your create and commit BAPI calls are all happening in the same thread, so you don't need your own ISessionProvider implementation. If the potential exists for them to be called in different threads in your actual application, though, then that's where you need one.

0 Kudos

Maybe I don't need to implement the interface, but if I use the BeginContext method the application crash after more or less ten minutes (always the same time), raising the error 'Collection was modified; enumeration operation may not execute' in the object SAP.Middleware.Connector.RfcSessionManager.UpdateSessions. So, something is changing the session collection.

The only way for it works is just to avoid the use of this method (BeginContext), but I suppose this doesn't guarantee the commit of the transaction.

Anyway, thanks for your support.

Regards.

Former Member
0 Kudos

If you're getting that error and don't have your own ISessionProvider implementation, then that's very strange. It's using SAP's default session provider in that case, and you'd think they would have worked the bugs out of that by now.

If you're getting the error and DO have your own ISessionProvider implementation, then yeah... try not registering your own and using the default instead.

Good luck!

0 Kudos

No, I've not implemented the ISessionProvider interface.

I though it was due to the way I launch the threads. Instead of:

ServiceThread = new Thread(ThreadLaunchService);

ServiceThread.Start(ActiveService);

I tried using a pool:

ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadLaunchService), ActiveService);

It has taken more time, but finally it crashed.

Regards.

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.

Sorry, and thank you for your support.

Regards.

0 Kudos

for you to know, see comment 'Application crash using BeginContext'

/people/thomas.weiss/blog/2011/01/14/a-spotlight-on-the-new-net-connector-30

It seems it was a real bug.

Regards.

Former Member
0 Kudos

Thanks for the link. I had suspected that it was a bug, and that confirms it. I'll keep the link to that workaround in case I need it later.

At the moment, I'm doing the same sort of thing, but not using the thread ID for BeginContext/EndContext and the RFCs in between. I guess that's the only case in which they keep a record in their collection that has to be cleaned up later.

Former Member
0 Kudos

Hello,

I'm working on my MSc Thesis Project and I got stuck on this. I have to manage session but I'm not sure how to do that in practice.

I was wondering if anyone has code sample regarding this problem (C# or anything else)?

Thanks in advance.

Former Member
0 Kudos

Code sample? Not really. It's difficult to break my ISessionProvider implementation out of context and still have it make sense, and if you're using a different language than I am (C++/CLI) then translation might be difficult. I can describe what I did, though.

I maintain my own list of active session IDs, and implemented the following methods in ISessionProvider:

GetCurrentSession returns the current stateful RFC session ID, if one is active, which is maintained by other parts of the code ensuring that it is set when necessary. For synchronous and transactional RFCs (for which you don't need BeginContext/EndContext calls), it returns a string indicating the current thread ID.

IsAlive returns true or false depending on whether or not a session ID is in the list, though now that I've added support for the SessionChanged event it doesn't seem to be called anymore.

ChangeEventsSupported returns true, to indicate SessionChanged event support.

CreateSession returns an empty string. It isn't used in a client, but it needs to return something to avoid compiler errors.

All other methods are empty, but still there... again, to avoid compiler errors. The event I just implemented in the standard way, with a delegate and a way to fire it from outside the session provider object. I fire the event whenever I remove a session ID from the list that I maintain (the only defined event type is "destroyed", so it makes sense that that's the only time you would fire it).

To install the session provider, I create an instance of the session provider class and register it (RfcSessionManager::RegisterSessionProvider) when I initialize my SAP interface. When I shut things down, I unregister it (RfcSessionManager::UnregisterSessionProvider).

Former Member
0 Kudos

It seems the crash problem has been resolved, by adding support for the SessionChanged event to my ISessionProvider implementation. I ran through a stateful RFC, waited about an hour, and no crash occurred. That was more than enough time for it to crash when I was testing last week.

I still consider this to be a bug in the .NET connector library, the documentation, or both, but at least I have a workaround that works well for me. SAP should either document the need to support SessionChanged, or fix the bug that causes the crash if that support isn't there.

0 Kudos

Hello Maeglin,

Could you explain how did you resolve this issue with some code example?. I'm getting the same error executing RFC calls in different threads, but I cannot see the way to implement the ISessionProvider interface, I'm using RfcSessionManager.BeginContext and EndContext. Also can you explain which support did you add to the SessionChanged event?

Thanks and regards.