LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Calling extension methods from a dotNET / C# assembly (MongoDB-driver)

Solved!
Go to solution

Hello everyone,

I'm currently working on utilizing the MongoDB C# / dotNET driver with LabVIEW (https://docs.mongodb.com/ecosystem/drivers/csharp/). My current version of the driver is 2.4.3.23.
After overcoming some basics (missing DDLs for dorNET Core, etc.) things are starting to take shape.

Unfortunately, due to the Async nature of the library (it seems to me that way), there are a few hoops to jump through when processing results from dotNET Invoke Node / Property Nodes...

I'm calling the "ListCollections" method (https://mongodb.github.io/mongo-csharp-driver/2.3/apidocs/html/M_MongoDB_Driver_IMongoDatabase_ListC...) with an IMongoDatabase refnum.
This works without any errors, but I have troubles handling the result.
According to the documentation it is an "IAsyncCursor<BsonDocument>", which is aimed at being able to be handled asynchronuously (if there is a tutorial out there how to implement that in LabVIEW, I'm very interested).

By looking at this tutorial (https://www.codementor.io/pmbanugo/working-with-mongodb-in-net-2-retrieving-mrlbeanm5) I figured out a way in LabVIEW, but it is one example of complicated code due to programming languages interfaces...

Post.PNG

(Thanks to Richard Carpenter from RBX Systems Ltd for the inspiration via his library)

This does not seem like a suitable idea for the long term, and I came across the extension methods for IAsyncCursors (https://mongodb.github.io/mongo-csharp-driver/2.3/apidocs/html/T_MongoDB_Driver_IAsyncCursorExtensio...), which offer methods such as "ToList". Casting the dotNET ref from IAsyncCursor to IAsyncCursorExtensions unfortunately didn't offer me any of those methods... (see following picture).

Post_2.png

Is there something I'm missing when using the extension methods?
I found a post from the TestStand forums from 2012 (http://forums.ni.com/t5/forums/v3_1/forumtopicpage/board-id/330/thread-id/36029/page/1) that suggest that extension methods are not available.
Is this also the case for LabVIEW?

And regarding the iteration through the IEnumerable pointed to by the cursor: Is there another, more compact or elegant way??

 

Thank you!

Niels Göran

0 Kudos
Message 1 of 18
(5,818 Views)
Solution
Accepted by topic author ngblume


First up regarding the enumeration - no, there is no better way in LabVIEW (besides using SubVIs to hide the mess of course). C# has nice syntactic sugar for performing the enumeration but, in the background, the C# compiler actually converts it back into the same thing as what you are doing.

 

Extension methods in C# are actually just static method calls on static classes with a particular syntax when defining the method - the first input to the static method call is the class instance you want to operate on and defined with "this". The C# language allows a developer to perceive that you have somehow magically added a new method to a class when you use them but this is not the case; they are simply bog standard static method calls in the end and can even be called like any other static method call in LabVIEW (even C# if you wanted to for some reason).

 

So Extension methods are available to call in LabVIEW but you have to understand the secret sauce behind the magic and call it just like any other static method call. Here's a quick example I whipped up that reverses a given string. You can see that I call the static method directly (the class is static so no need for a constructor) and the extension method is marked as [S] for static:

 

3.png

 

In case you are curious, here is the basic code for this assembly, see how it follows the requirements for extension methods I described above:

4.png

 

Now, back to your case. Looking at the documentation for IAsyncCursorExtensions, I see that it is a static class (as we would require for a class housing extension methods):

1.png

 

So far, so good. In order to use the methods on this class we need a static object instance. In LabVIEW, we basically don't need to do anything - we just drop down a property or invoke node for the static class to call its properties / methods. Now lets look at the ToList method:

 

2.png

 

See that this is a static method call. The first parameter is a type designated with a "this" - this is the special definition that makes this an extension method so that the C# syntactic sugar can work however it is just a class instance of the type that this static method operates on.

 

However there is a wrinkle - note that this is a generic method (rather than a method call on a generic class); you can tell from the "<>" brackets format in front of the method name. LabVIEW is not great with generics in general (you will have noticed that strange '1 style syntax in your nodes) and, in particular, LabVIEW cannot call generic methods on a non-generic class. This is because the type is not defined until run-time and LabVIEW cannot create new open or closed generic types (although you can instantiate generic objects from completely closed types, refer http://digital.ni.com/public.nsf/allkb/DC41DCDA972642CF8625787E00732DDD). This is why you don't see any in the list (every method in that static IAsyncCursorExtensions class is generic, so you only see the methods inherited from the type 'object').

 

The solution I would suggest for you would be a wrapper class in C#. This would expose the basic functionality you need and also conveniently hide the generic and enumerating portions from your LabVIEW codebase. You would be able to leverage those same C# syntactic sugar constructs in your wrapper assembly but then expose basic calls (using primitive types).

Message 2 of 18
(5,773 Views)

Dear tyk007,

thank you very much for the extensive answer !!
I was afraid something like that was causing my troubles.
I can follow your answer up to the second to last paragraph...

 


However there is a wrinkle - note that this is a generic method (rather than a method call on a generic class). LabVIEW is not great with generics in general (you will have noticed that strange '1 style syntax in your nodes). LabVIEW cannot call generic methods on a non-generic class since the type is not defined until run-time and LabVIEW cannot create new open or closed generic types (although you can instantiate generic objects from completely closed types, refer http://digital.ni.com/public.nsf/allkb/DC41DCDA972642CF8625787E00732DDD). This is why you don't see any in the list (every method in that static IAsyncCursorExtensions class is generic, so you only see the methods inherited from the type 'object').

 

The solution I would suggest for you would be a wrapper class in C#. This would expose the basic functionality you need and also conveniently hide the generic and enumerating portions from your LabVIEW codebase. You would be able to leverage those same C# syntactic sugar constructs in your wrapper assembly but then expose basic calls (using primitive types).


I noticed the "1"s in the nodes, but didn't really looked at it closely..
When you say that "LabVIEW cannot call generic methods on a non-generic class", does this mean, that I would be able to call generic methods on generic classes (instead of non-generic classes) ??
I was assuming that IAsyncCursor is a generic class (interface)...

 

Regarding the solution with the wrapper class: Could you maybe point me in the direction of a tutorial / starting point on how to handle this?

 

Thank you!!!

Niels Göran

0 Kudos
Message 3 of 18
(5,766 Views)


I noticed the "1"s in the nodes, but didn't really looked at it closely..
When you say that "LabVIEW cannot call generic methods on a non-generic class", does this mean, that I would be able to call generic methods on generic classes (instead of non-generic classes) ??
I was assuming that IAsyncCursor is a generic class (interface)...

 

 

Sorry, my wording is confusing. I should just say LabVIEW cannot call generic methods at all (generic class or no). The angle brackets ("<>") along with a type name in the middle are the giveaway for whether something is generic or not. If a method is generic then the method signature has those brackets attached to the name. if a class or interface is generic, then the class or interface  name has those same angle brackets (eg. "List<T>"). IAsyncCursor is indeed a generic interface by its definition:

5.png

 



 


Regarding the solution with the wrapper class: Could you maybe point me in the direction of a tutorial / starting point on how to handle this?

 


This could be tricky depending on your experience with C#. The best advice I can give you is the general process that I would follow:

  1. List the methods I would need to call (eg. "Read this blah blah", "Update this document blah"), the inputs they need and the expected outputs. Personally I would prefer to use primitive types (eg. int, float, string etc.) on the signature of these methods; they are much easier to deal with in LabVIEW as it can convert most of these into LabVIEW types for you automatically just like my string example.
  2. Create a new class in C# and generate the skeleton method signatures you defined.
  3. Create the necessary constructor for the class that instantiates your connection to the MongoDB server and a Dispose() method and finaliser to close the connection (the finaliser is there in case you forget to call Dispose prior to finishing working with the db). There is the IDisposable pattern which you can follow if you are inclined; again, experience dependent. As private field(s) in your class store the MongoDb object instances (from opening the db connection etc.) that you would need to execute the methods that you will be calling from LabVIEW. I'm not sure on the specifics but it would be the same objects you were creating in LabVIEW.
  4. Inside each method write the code that performs the necessary work using the private MongoDB object fields that you have created in the constructor.

 

0 Kudos
Message 4 of 18
(5,759 Views)

Dear tyk007,

thanks for the answer!

Given that my experience with C# started this morning, I'm expecting to invest several hours to days to make that work..
But I will give it a go and see what becoms of it..

One last question:
I figured out that there seems to be a specific class "MongoDatabase" (http://mongodb.github.io/mongo-csharp-driver/2.4/apidocs/html/T_MongoDB_Driver_MongoDatabase.htm), which unfortunately resides in the "legacy" driver part...
So I'm assuming that using that driver (which apperas to be simply implementing most methods with BSON type as <TDocument>), is a possible but not future-proof approach.
Is that correct?

And if so, why is casting to more specific of an IMongoDatabase to MongoDatabase not possible?

Post_3.png

Thank you!

Niels Göran

0 Kudos
Message 5 of 18
(5,757 Views)

I don't have the specifics (you would need to dig further) but this is the signature of the MongoDatabase class

1.png

 Notice that it does not implement the IMongoDatabase interface (or any other class except object implicitly). So the To More Specific class will not work and LabVIEW rightly points that out.

 

I'd be guessing but I'd say that the C# MongoDB API has undergone refactoring in the past and, unfortunately, some of the names between the legacy namespaces and the current namespaces are similar which leads to confusion. The course of action I'd recommend is to stick with the current namespaces - the advantage of using a C# wrapper is that LabVIEW will be blissfully unaware of all this and will not change if you make any adjustments to the inner workings of your wrapper.

0 Kudos
Message 6 of 18
(5,750 Views)

Dear tyk007,

thank you for the quick response!

I will give creating a C# wrapper a go tomorrow and see how it goes..

Thanks!

Niels Göran

0 Kudos
Message 7 of 18
(5,748 Views)

Dear tyk007,

one more question:
Any recommendations as what IDE to use for the development of the C# wrapper?

 

Thanks!

Niels Göran

0 Kudos
Message 8 of 18
(5,743 Views)

There are several IDEs around that are free:

  • Visual Studio Community is free for independent developers / open source; otherwise VS2015 still has a Desktop Express verstion that is free either way. This will also allow you to experiment outside of your current project.
  • SharpDevelop will be enough for this project; it has limitations but it is completely free and very light-weight (installation etc.) compared to Visual Studio.
  • Jetbrains EAP of Rider is a C# IDE based on their IntelliJ product line. It is currently in beta and therefore free for trialing but longer term this will become a paid product.

My suggestion would be to consider what you would like to experiment with in the future. VS is a large beast but it will also allow you to experiment with newer frameworks etc. and their is a lot more support for IDE usage (Channel 9 for example).

Message 9 of 18
(5,740 Views)

Hello tyk007,

 

I'm working through getting started with C# and LabVIEW DLLs.
The following question keeps bugging me though:
What is the recommend (best accepted) "version" of .NET to use for LabVIEW?
Visual Studio offers me "Class Library (.NET Framework)", "Class Library (.NET Core)" and "Class Library (.NET Standard)". 

So far I got the MongoDriver to work with .NET Core in version 1.0 instead of 1.1, but they should work with netstandard1.5 and net45 (at least the download contains folder named that way..).

 

What is your recommendation as to what version of .NET to work with to ensure best interoperability with LabVIEW?

 

Thanks!

Niels Göran

0 Kudos
Message 10 of 18
(5,673 Views)