microsoft / node-api-dotnet

Advanced interoperability between .NET and JavaScript in the same process.
MIT License
524 stars 58 forks source link

Calling back from .NET with caller nodejs environment and context #189

Closed Enzojz closed 4 months ago

Enzojz commented 10 months ago

Hello,

I am currently facing a scenario, I am trying to create a template based word docx generator module written in C# and F#, it works like docx-templates but will be written in .NET and aot which should be much faster.

However, when generating docx, the values to fill in the template are evaluated as javascript and there will be some contextual calls (such as injected customized functions from nodejs side that reads the DB etc..). I see in the example here that we can call nodejs from .net, but it's under an environment created by c#, that means I will not able to use the context from the nodejs which calls the .net module.

Is there any possibility in the future to have such interoperation?

jasongin commented 10 months ago

It's already supported; in fact the very first code snippet at the top of the readme is JS code calling .NET. You can use this project to load and call .NET APIs in a pre-built assembly, or to create a Node.js addon from your .NET code with or without AOT. See also these example projects:

Enzojz commented 10 months ago

It's already supported; in fact the very first code snippet at the top of the readme is JS code calling .NET. You can use this project to load and call .NET APIs in a pre-built assembly, or to create a Node.js addon from your .NET code with or without AOT. See also these example projects:

No, this is what I mean.

I have nodejs code A, and c# library B called from A, there's no problem. The problem is I wish to call back from B to A, with context given by A, for example I have a function is A called get_image(uuid), which enquiries into my db with the uuid to get the path of an image. Now I wanted in lib B in the halfway of calculate (in my case after AST of a docx file) to call this function from A, I found no way to do this.

jasongin commented 10 months ago

There are a couple ways you can do that. But I can understand why you missed it since they are not mentioned in the readme or the simple example projects.

  1. Pass a function (delegate) object from JS to C#. Declare a delegate in C# that defines the parameters and return type of the JS function you want to call. It can use JS value types (JSValue GetImage(JSValue uuid)) or .NET types that can be automatically marshalled to/from JS (string GetImage(string uuid)). Then pass the JS function object from JS to a .NET method that takes a delegate parameter. There are some tests of variations of this scenario in TestCases/delegates.js and TestCases/Delegates.cs.

  2. Implement a C# interface with a JS class. Declare an interface in C# that has your string GetImage(string uuid) method, and tag it with [JSExport]. The build will generate a .d.ts with a TypeScript version of the interface. Then create a TS class that implements the interface, and pass an instance of that class to a C# method that has a parameter that is the interface type. The interface method can be called from C# code. (This can also be done with plain JS instead of TS.) The semantic-kernel example implements the .NET ILogger interface this way.

Note the .NET Guid type is not yet marshalled automatically to/from JS so you'll have to use a string for now.

jasongin commented 10 months ago

I'll work on some additions to documentation about implementing .NET delegates or interfaces in JS.

526677804 commented 5 months ago

Hello, I would like to ask where the document mentioned above is placed? I also have similar needs and need to refer to it. Thank you very much.

jasongin commented 4 months ago

I have some new documentation in PR now. The relevant topics are:

After the PR is merged, the docs will be published to GitHub pages where it will be much easier to browse.