I'm writing a COM automation in C# to handle database calls through ADO.NET.
All functions in the component returns a boolean value telling the caller if everything went well.
I've get a problem when an ERROR() is called in NAV. If I got an open db-connection in the component it keeps open even after the NAV ERROR. And if inside a transaction, the transaction keeps alive, locking every other user out of modified tables...
Example:
In NAV I've got a codeunit with an automation variable:
aw 'ADOwrapper'.ADOwrapper
OnRun()
CLEAR(aw);
CREATE(aw);
IF NOT aw.Connect('SomeConnectstring') THEN
ShowErrors;
aw.BeginTrans;
IF NOT aw.ExecuteSQL('INSERT INTO aTable (Id) VALUES (''1'')') THEN
ShowErrors;
IF CONFIRM('Throw error?!',FALSE) THEN
ERROR('Error!');
IF NOT aw.ExecuteSQL('INSERT INTO aTable (Id) VALUES (''2'')') THEN
ShowErrors;
aw.CommitTrans;
aw.Disconnect;
CLEAR(aw);
ShowErrors(); just shows the error and calls ERROR();
If I press Yes in the CONFIRM-box the execution in NAV stops , but the transaction started by the .NET component is still active...
I want the .NET component to be informed, in some way, that there were a problem and that there is no more any references to the object. I guess that it has something to do with reference counts to the wrapper. But I might be completely wrong. :-k
Any tips or thoughts would be very appreciated!
Comments
I've installed VB6 and recreated the the same problem as in NAV.
So I guess that this is some general COM Interop problem. And I might have done some fundamentel mistakes in the .NET code...
Are there any COM Interop experts in here!? [-o<
It is also always a good idea to use local vars, if possible.
www.dasautomatisering.nl
In this case, although Navision should dispose the automation COM object (your wrapper instance) when aw is a local variable, the disposing of the ADO object inside your wrapper doesn't seem to take place.
MCP+I, MCSE NT, Navision MCT (2004,2005)
So right now I've got a couple of options:
- Write a wrapper in unmanaged code
- Redesign completely so that a NAV Error can't be throwed inside a transaction
I think that I'll go for number two here.
I would expose a com - event for CLR exceptions and pass the Exception - Message to Navision.
In Navision it should look like this:
MyControl::ExceptionThrown(Exception: Text[1024])
MESSAGE(Exception);
This way you can avoid calling "IF NOT GetBeer(1,cold)" a zillion times and you can implement the logic for the errors in Navision instead in your .net assemby.
Navision Errors are COM errors and the CLR throws them as COMExceptions.
So you can try to catch exceptions in .Net and fire an event in Navision, for example ClearObject which should look like:
MyControl::ClearObject()
CLEAR(MyControl);
and take a look at COMtrace which is helpful when you mess around with COM
http://www.blunck.se/comtrace/comtrace.html
wakestar
In the Dispose() you can release your allocated objects by hand.
www.dasautomatisering.nl
That might be a nicer way of error handling!
I've read some about creating com events from .NET to NAV, so I think I'll manage to do so, but how could I trap NAV:s error as COMException? Where should I put my try-catch?!
I'd be very grateful if you could post a code sample in .NET for this. [-o<
Btw, does your GetBeer-function realy work?! Could you share it?!
dick11:
I thought of that to, but it doesn't seem that the CCW calls the Dispose()-function.
But the Garbage Collector calls my destructor so I could clean up there, but then it's way too late...
MCP+I, MCSE NT, Navision MCT (2004,2005)
I think I agree with you... I can make the cleanup inside .NET. But, as I wrote earlier, the disposal of the wrapper isn't equal to the disposal of the .NET component. The cleanup in the .NET component is done too late due to the logic of Garbage Collecting in .NET.
Hi
I have no idea how his .net code looks like, but yes... he's responsible for cleaning up the resources used within his .net class.
The last .net component I made was one single .net form which I'm closing when an en error happens. After the this.close() I fire the ClearObject in Navision. - The this.close() does it for me in my case. - I never had any problems with the GC.
Hi,
The problem for me is if I've got an open transaction and NAV throws an ERROR(), the transaction would be open until the GC chooses to do its thing. In my destructor I check for open transactions -> run a rollback and check for open connections -> close. I can't see any other way to do this earlier than through GC, do you?
1.) you create aw in C/SIDE which causes a CCW to be created via .NET COM Interop
2.) you run a function within the wrapper which might return an error
3.) regardless of whether you raise an ERROR in C/SIDE or simply display a message, the ADO connection has to be released before you continue in Navision with the ERROR or you have to call a "ClearADO" function inside the wrapper before raising the error in Navision.
Now the question:
Why don't you close the ADO connections by running a cleanup function in the wrapper from within Navision ?
Maybe I got something wrong, but let's mak sure we are talking about the same thing.
I only see one situation where you run into problems: if an unexpected C/SIDE runtime error occures, all existing ADO objects (and connections) are alive until the garbage collection of .NET destroys the CCW referenced object.
MCP+I, MCSE NT, Navision MCT (2004,2005)
As you have properly trapped the error in Navision and returned to the calling application appropriately this should mean you can fix the condition and then re-execute the logic without haviging to dispose of the object.
The nice thing about this approach is that you build your transactions properly so that they throw the errors accordingly and will work in the standard Navision process flow. It is only a specific codeunit that is built to interface with your .Net component that has to be built to not throw errors.
Epimatic Corp.
http://www.epimatic.com
Yes, we’re talking about the same thing!
The reason I would like the component to handle the cleanup instead of having the NAV developer to trigger it before raising an error is that there will be more developers than me that are going to use the component and they are used to the old ADODB automation. I would like to create the new .NET component with the same behavior as the old one. But I don’t know if it’s possible at all… ](*,)
jlandeen:
Unfortunately it’ll be used by a 3.70b client, and upgrading runtime is not an option.
[SOLVED] Ending .NET component from the NAS
rspruit created a VB6 wrapper for the .NET wrapper :!:
Then he called the .NET components cleanup code from the destructor of the VB6 component, which will be called immediately after NAV clears the automation variable.
Not the most "clean" solution, but I'm convinced that it would do the trick.
I'll give it a try if no one else got a better solution...?
I would use 3 different objects:
1) Calling Object (table,codeunit, form, etc. where thread of execution starts)
2) Transaction Codeunit (calls the Component Wrapper and may encounter error conditions)
3) Component Wrapper Codeunit(a single instance codeunit which has functions that expose the logic of the component to Navision)
Calling Object Code:
I am assuming that there is some "StartTrans", "AbortTrans" and "CleanUp" functions in the Component Wrapper Codeunit that can be used to start/abort a transaction as well as clean up the connection. This would allow you to address your original problem of a left over connection without the need of a VB6 wrapper.
While this may also be a bit of a hack - you should be able to do away with your VB6 wrapper class and only have to maintain .Net code and Navision code. I've found as you add layers of complexity it becomes more difficult to maintain and support.
Epimatic Corp.
http://www.epimatic.com
But I still think that I'll go for the VB6 wrapper solution. I'm not a big fan of SingelInstance Codeunit solutions... And the VB6 wrapper is made in no time.
Thanks anyway!
As far as doing everything in Nav...I still have a love/hate relationship with Navision. I recognize the power of it and the structured development environment can help....but I definately miss the full on power of a true development environment like .Net
Epimatic Corp.
http://www.epimatic.com