agracio / edge-js

Run .NET and Node.js code in-process on Windows, macOS, and Linux

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Trouble running a simple C# library both in .NET 6 and in Quickstart sample:

Porkechebure opened this issue · comments

I'll try to keep it as brief as possible so it won't be a wall of text.
Details:
Node version: v18.15.0
Visual studio: Visual studio 2022 17.3.6
Visual Studio code (to run node): 1.7.33
dotnet --version command output: 6.0.402

C# code I'm trying to run:
https://github.com/Sukasa/MegaHAL-Rewrite

What I'm trying to accomplish:
Run that code from node js as a telegram bot using telegraf node package.

What happens:

Scenario 1:
Since any .NET 6.0 test failed, I tried to copy the code of MegaHAL-Rewrite directly into here:
https://github.com/agracio/edge-js-quick-start (which also required a rebuild, as I discovered, to deploy Edge.js library into the build, more time wasted) and run it.

It actually ran until at some point it throws exception:
Unable to find assembly 'MegaHal.NET, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

I tried reusing the handleException method given in the QuickStart example and I tried to get more details on the exception and this is what I got:

.NET Exception: Unable to find assembly 'MegaHal.NET, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
.NET Exception: at QuickStart.Core.EdgeMegaHal.LoadBrain(Object input) in C:\Users\SomeUser\Downloads\edge-js-quick-start-master\src\QuickStart.Core\EdgeMegaHal.cs:line 49
.NET Exception: System.Runtime.Serialization.SerializationException: Unable to find assembly 'MegaHal.NET, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

I never referenced any MegaHal.NET library in there and never added any reference to external libraries, I transferred all the code inside the QuickStart.Core example.

Scenario 2:
I actually had a .NET 6 project called MegaHal.NET with the same code but MegaHal.NET and QuickStart.Core never knew each other.
What the heck???

Now to the code. This is the quickstart index.js modified

const path = require('path');
var version = process.argv[2];
var namespace = 'QuickStart.' + version.charAt(0).toUpperCase() + version.substr(1);
if(version === 'core') version = 'coreapp';

const baseNetAppPath = path.join(__dirname, '/src/'+ namespace +'/bin/Debug/net'+ version +'3.1');
console.log(baseNetAppPath)
process.env.EDGE_USE_CORECLR = 1;
if(version !== 'standard')
    process.env.EDGE_APP_ROOT = baseNetAppPath;

var edge = require('edge-js');

var baseDll = path.join(baseNetAppPath, namespace + '.dll');

var megahalTypes = namespace + '.EdgeMegaHal';


var InitializeMegaHal = edge.func({
    assemblyFile: baseDll,
    typeName: megahalTypes,
    methodName: 'InitializeMegaHal'
});


var LoadBrain = edge.func({
    assemblyFile: baseDll,
    typeName: megahalTypes,
    methodName: 'LoadBrain'
});


var GenerateReply = edge.func({
    assemblyFile: baseDll,
    typeName: megahalTypes,
    methodName: 'GenerateReply'
});


InitializeMegaHal('', function(error, result) {
    if (error) throw error;
    console.log(megahalTypes + '.InitializeMegaHal');
    console.log(result);
});

LoadBrain('truevaiano.brn', function(error, result) {
    if (error) throw error;
    console.log(megahalTypes + '.LoadBrain');
    console.log(result);
});


GenerateReply('', function(error, result) {
    if (error) throw error;
    console.log(megahalTypes + '.GenerateReply');
    console.log(result);
});

The main class in C# is exactly this;:
https://github.com/Sukasa/MegaHAL-Rewrite/blob/master/MarkovBot.cs

Where you can find the 3 methods.
I made the proxy class as asked:


using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace QuickStart.Core
{
    public class EdgeMegaHal
    {
        public static MegaHalCore megahal;
        string modelsDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Models");
        public async Task<object> InitializeMegaHal(dynamic input)
        {
            try
            {
                megahal = new MegaHalCore();
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public async Task<object> LoadBrain(dynamic input)
        {
            try
            {
                if (string.IsNullOrEmpty(input))
                {
                    return $"Error 1: input is {input} and path looks like {Path.Combine(modelsDirectory, input)}";
                }
                else
                {
                    megahal.LoadBrain(Path.Combine(modelsDirectory, input));
                    return true;
                }
            }
            catch (Exception ex)
            {
                return $"Error 2: Error is {ex.Message}";
            }
        }

        public async Task<object> GenerateReply(dynamic input)
        {
            try
            {
                megahal.Learn(input);
                var response = megahal.GenerateReply(input);
                megahal.Learn(response);
                return response;
            }
            catch (Exception ex)
            {
                return $"Error 3: Error is {ex.Message}";
            }

        }
    }
}

this is the visual studio code output

C:\Users\SomeUser\Downloads\edge-js-quick-start-master\src\QuickStart.Core\bin\Debug\netcoreapp3.1
QuickStart.Core.EdgeMegaHal.InitializeMegaHal
true
QuickStart.Core.EdgeMegaHal.LoadBrain
Error 2: Error is Unable to find assembly 'MegaHal.NET, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
QuickStart.Core.EdgeMegaHal.GenerateReply
I'm speechless.

As you can see the first and third method run fine, but the second method doesn't.
The second method tries to load up a file:

        public void LoadBrain(string Filename)
        {
            FileStream FS = File.Open(Filename, FileMode.Open);
            BinaryFormatter BF = new BinaryFormatter();
            brain = (Brain)BF.Deserialize(FS);
            FS.Close();
        }

Anyone has any idea?
I totally abandoned the idea of running the .NET 6.0 version of this as there are major issues all around, starting from here:
#175

You can follow the discussion and it's a nightmare.

Any idea?
I'm thinking of giving up and just do a .NET Web Api to call without so much troubles

On a side note the whole trouble SEEMS (not sure though) related to binaryformatter being an a-hole and trying to make assumptions on DLL names by itself.

Here's some more info:
https://stackoverflow.com/questions/14990980/system-runtime-serialization-serializationexception-unable-to-find-assembly-mya

I tried to use JsonSerializer instead but it has been a nightmare as this application produces enormous json files, so changing from binaryformatter to other serialization libraries it's not that immediate.

I also tried a CustomizedBinder as suggested but no luck.
:(


    sealed class CustomizedBinder : SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            Type returntype = null;
            string sharedAssemblyName = "QuickStart.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
            assemblyName = Assembly.GetExecutingAssembly().FullName;
            typeName = typeName.Replace(sharedAssemblyName, assemblyName);
            returntype =
                    Type.GetType(String.Format("{0}, {1}",
                    typeName, assemblyName));

            return returntype;
        }

        public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
        {
            base.BindToName(serializedType, out assemblyName, out typeName);
            assemblyName = "QuickStart.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
        }
    }

In the end, it seems the problem is that the binary file I'm trying to read has been serialized from another version of the DLL and edge is actually doing it's work.
Well, Imma close this.