Edge.js Communication With C# DLL : .NET Core Support and Method Signature - edge.js

I am using a Node Client written using Edge.js to Invoke the Methods contained in a C# Dll.
I am facing 2 Issues:
(A). NET CORE VERSION > 2.0 SUPPORT :
I want to invoke methods written in the C# Dll that targets the latest .NET Core Framework i.e.Net Core 6.
When I try to invoke to methods contained in the C# Dll that targets the .NET Core Framework 6 from Node Client using Edge.js Api I get the below error :
return edge.initializeClrFunc(options); ^ Error: Could not load type 'TestLibrary6.Startup' from assembly 'TestLibrary6, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. at Object.exports.func (D:\LEARNING\JAVASCRIPT\EdgeJs\DotNetCore 2\NodeApplication\node_modules\edge-js\lib\edge.js:177:17) at Object.<anonymous> (D:\LEARNING\JAVASCRIPT\EdgeJs\DotNetCore 2\NodeApplication\main1.js:4:25) at Module._compile (node:internal/modules/cjs/loader:1101:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10) at Module.load (node:internal/modules/cjs/loader:981:32) at Function.Module._load (node:internal/modules/cjs/loader:822:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12) at node:internal/main/run_main_module:17:47 { Message: "Could not load type 'TestLibrary6.Startup' from assembly 'TestLibrary6, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.", TypeName: 'TestLibrary6.Startup', Data: {}, InnerException: null, TargetSite: {}, StackTrace: ' at System.Reflection.RuntimeAssembly.GetType(RuntimeAssembly assembly, String name, Boolean throwOnError, Boolean ignoreCase, ObjectHandleOnStack type)\r\n' + ' at System.Reflection.RuntimeAssembly.GetType(String name, Boolean throwOnError, Boolean ignoreCase)\r\n' + ' at ClrFuncReflectionWrap.Create(Assembly assembly, String typeName, String methodName)\r\n' + ' at ClrFunc.Initialize(FunctionCallbackInfo<v8::Value>* info)', HelpLink: null, Source: 'mscorlib', HResult: -2146233054,
Below is the Code that I use in the Node Client that uses the Edge.js Client to Invoke the methods on the Dll :
The version of the Edge.js is : "edge-js": "^16.6.0"
const edge = require("edge-js");
process.env.EDGE_USE_CORECLR = 1;
const simpleFunc = edge.func({
assemblyFile: "assets/net6.0/TestLibrary6.dll",
typeName: "TestLibrary6.Startup",
methodName: "Invoke",
});
simpleFunc("Test", function (error, result) {
if (error) throw error;
console.log("Simple Result: ", result);
});
`
When I use the same Edge.js Api to invoke the methods on a C# Dll that targets the .NET Core Framework 2,it works successfully without giving any error.
Is there a suppport of .NET Core Version > 2 in Edge.js ?
As most the clients are moving to newer versions of .NET Core it would be benefecial if we can have support for .NET Core Versions that are greater than 2.0
(B). By convention for Edge.js to work successfully with .NET Core Dll all the Public Methods must have a syntax as shown below :
` public async Task<object> Invoke(object input)
{
}
`
If we call a Method contained in the C# Dll that has a syntax different from the above syntax it does not work with Edge.js i.e. Edge.js api "edge.func()" will throw an error if we try to inovke method contained in the C# Dll other that the above signature.
For eg. If I try to invoke the below method contained in the C# Dll from the Node Client using Edge.js api:
`public string GetName(){}`
It throws an error :
` return edge.initializeClrFunc(options); `
```JavaScript
Error: Unable to access the CLR method to wrap through reflection. Make sure it is a public instance method.
Type: TestLibrary.Startup, Method: GetName, Assembly: TestLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
at Object.exports.func (D:\LEARNING\JAVASCRIPT\EdgeJs\DotNetCore 2\NodeApplication\node_modules\edge-js\lib\edge.js:177:17)
at Object.<anonymous> (D:\LEARNING\JAVASCRIPT\EdgeJs\DotNetCore 2\NodeApplication\main.js:4:24)
at Module._compile (node:internal/modules/cjs/loader:1101:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
at node:internal/main/run_main_module:17:47 {
`
``
Message: 'Unable to access the CLR method to wrap through reflection. Make sure it is a public instance method.\r\n' +
'Type: TestLibrary.Startup, Method: GetName, Assembly: TestLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null',
Data: {},
InnerException: null,
TargetSite: {},
If we want to invoke exisiting dll methods that has a signature that is different from the one used by convention in edge.js i.e. public async Task Invoke(object input){},
it is will not be possible as by convention if requires the C# methods to have this signature and as a work-arround I would need to write a wrapper C# method for already existing method and call that method in the Node.js Client.
This is time-consuming operation
Is there any work-arround this issue ? That is to call a C# method contained in the DLL from the Node Client using Edge.js Api that has the signature different from "async Task Invoke(object input)"
Any suggestions/guidance to resolve the above 2 Issues would be deeply appreciated as I am stuck on this.

Related

AspNet Core and Autofac Dependency Resolution Exception

I'm trying to use Autofac in a multi-tenant application and I need to inject DefaultIdentity as following:
c.Register(c =>
{
var opt = new DbContextOptionsBuilder<ApplicationDbContext>();
opt.UseSqlServer(scm.GetConnectionString("ReservedArea_" + t.DatabaseSuffix));
return new ApplicationDbContext(opt.Options);
}).AsSelf()
.InstancePerLifetimeScope();
....
tenantServices.AddDefaultIdentity<ApplicationUser>(options =>
{
options.SignIn.RequireConfirmedAccount = true;
options.User.RequireUniqueEmail = false;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddSignInManager<AppSignInManager<ApplicationUser>>()
.AddUserManager<AppUserManager<ApplicationUser>>();
c.Populate(tenantServices);
Where "c" is my containerBuilder and tenantServices my list of services to inject.
When I authenticate all works fine but after that, when application redirects to another page, I receive following error:
DependencyResolutionException: None of the constructors found with
'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type
'RA.WEB.Classes.Account.AppSignInManager1[RA.WEB.Data.ApplicationUser]' can be invoked with the available services and parameters: Cannot resolve parameter 'RA.WEB.Data.ApplicationDbContext dbContext' of constructor 'Void .ctor(Microsoft.AspNetCore.Identity.UserManager1[RA.WEB.Data.ApplicationUser],
Microsoft.AspNetCore.Http.IHttpContextAccessor,
Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory1[RA.WEB.Data.ApplicationUser], Microsoft.Extensions.Options.IOptions1[Microsoft.AspNetCore.Identity.IdentityOptions],
Microsoft.Extensions.Logging.ILogger1[Microsoft.AspNetCore.Identity.SignInManager1[RA.WEB.Data.ApplicationUser]],
RA.WEB.Data.ApplicationDbContext,
Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider,
Microsoft.AspNetCore.Identity.IUserConfirmation`1[RA.WEB.Data.ApplicationUser])'.
Autofac.Core.Activators.Reflection.ReflectionActivator.GetAllBindings(ConstructorBinder[]
availableConstructors, IComponentContext context,
IEnumerable parameters)
DependencyResolutionException: An exception was thrown while
activating
Microsoft.AspNetCore.Identity.SecurityStampValidator1[[RA.WEB.Data.ApplicationUser, RA.WEB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] -> RA.WEB.Classes.Account.AppSignInManager1[[RA.WEB.Data.ApplicationUser,
RA.WEB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].
What am I doing wrong?
Thanks in advance for your help

Problem with 32-bit COM component from 64-bit application. (TYPE_E_CANTLOADLIBRARY)

I have a created a dummy application for testing purposes which is a 64-bit console application, which creates an instance of a 32-bit COM application. It is a third party application called Laserforms.
static void Main(string[] args)
{
LFM32.Application application;
try
{
application = new LFM32.Application();
Console.WriteLine("Success!");
application.Quit();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
Console.Read();
}
}
Now, this works perfectly on my machine, but when I deploy it to another machine, which has Laserform installed as well I am getting the below error message.
{System.InvalidCastException: Unable to cast COM object of type 'System.__ComObject' to interface type 'LFM32.Application'.
This operation failed because the QueryInterface call on the COM component for the interface with IID '{6688DD46-3DA3-4FEC-8A1F-54E1453DCC77}'
failed due to the following error: Error loading type library/DLL. (Exception from HRESULT: 0x80029C4A (TYPE_E_CANTLOADLIBRARY)).}
I have go one through the registry of both and can't see any differences, what else could I be doing wrong?
UPDATE:
So I ran TlbImp on the .tlb file to create a DLL I could reference in my .NET application. This created a couple of extra DLLs and some warnings around Iunknown and some invalid types. I then ran Regasm (from Framework and Framework64) on this and the other DLLs it created but I am still seeing the exact same error.

System.Security.Permissions missing when invoking JsonConvert.DeserializeObject in .NET Core 3.0

I have generated the Csharp client for WebAPICore using the OpenAPI generator.
I am getting the local response as 200 with ResponseStatus:OK
But while type casting the response as shown below:
try {
return JsonConvert.DeserializeObject(response.Content, type, serializerSettings);
} catch (Exception e) {
throw new ApiException(500, e.Message);
}
AT return JsonConvert.DeserializeObject(response.Content, type, serializerSettings);. It is having the following issue:
Could not load file or assembly 'System.Security.Permissions, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified.
I have tried installing the NuGet Package with version: 4.0.3.0 and added the reference to Project, but still facing the same.

ASP.NET Core 3.1 services.AddSession(); causes crash

When I add
services.AddSession();
to my startup in my core 3.1 I get
Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Session.ISessionStore Lifetime: Transient ImplementationType: Microsoft.AspNetCore.Session.DistributedSessionStore': Unable to resolve service for type 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' while attempting to activate 'Microsoft.AspNetCore.Session.DistributedSessionStore'.) on stack: at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(IEnumerable`1 serviceDescriptors, ServiceProviderOptions options)
at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)
at Microsoft.Extensions.DependencyInjection.DefaultServiceProviderFactory.CreateServiceProvider(IServiceCollection containerBuilder)
at Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter`1.CreateServiceProvider(Object containerBuilder)
at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
at Microsoft.Extensions.Hosting.HostBuilder.Build()
At no time did I as for DistributedSessionStore, I just added the line AddSession and did not add any nuget packages, where is it getting this data from?
When I go to the definition on services.AddSession() it takes me to
Assembly Microsoft.AspNetCore.Session
Quite surprised as to what this error is all about.
I think that the only way to safely test for session is to use some code like this:
if(HttpContext.Features.Get<ISessionFeature>()?.Session != null && HttpContext.Session.IsAvailable)
{
...
}

Assembly.LoadFrom(assemblypath) in WCF Servicefactory

I am using a WCF ServicehostFactory for my wcf service in conjunction with unity for service and repository/DAL instantiation using interfaces.
I want to load an assembly into my appdomain (before the service is instantiated), whose types are registered in my web.config for Unity DI.
<assembly name="Customer.DAL.AdventureWorks"/>
<assembly name="Customer.DAL.Blogger"/>
<assembly name="Customer.Interfaces"/>
<assembly name="Customer.DAL"/>
<namespace name="Customer.Interfaces.Repository"/>
<namespace name="Customer.DAL.AdventureWorks"/>
<namespace name="Customer.DAL.Blogger"/>
<container>
<register type="Customer.Interfaces.DAL.IDal, Customer.Interfaces" mapTo="Customer.DAL.API, Customer.DAL">
<lifetime type="transient"/>
</register>
<register type="Customer.Interfaces.Repository.IRepositoryDepartment, Customer.Interfaces" mapTo="Customer.DAL.AdventureWorks.RepositoryDepartment, Customer.DAL.AdventureWorks">
<lifetime type="transient"/>
</register>
<register type="Customer.Interfaces.Repository.IRepositoryBlogs, Customer.Interfaces" mapTo="Customer.DAL.Blogger.RepositoryBlogs, Customer.DAL.Blogger">
<lifetime type="transient"/>
</register>
</container>
and I am using the following code to load the assembly:
Assembly.LoadFile("C:\xxxxx\Customer.DAL.dll"); // loaded from somewhere on disk.
I have verified the existence of the loaded assembly against the current appdomain and the assembly is loaded successfully.
Immediately after that, I try to register the service type using unity, but I get the following exception:
The type name or alias Customer.DAL.API, Customer.DAL could not be resolved. Please check your configuration file and verify this type name.
So why can the type "Customer.DAL.API" not be resolved?
EDIT: I run FusionLog in parallel and it traces out the failed assembly binding.
Why does it actually search for the assembly, cause the this assembly has already been loaded by me using reflection as mentioned above?!
EDIT-II:
#Tuzo:
I read all the articles you posted.
And tried the following:
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
AssemblyName asname = AssemblyName.GetAssemblyName(#"C:\xxxx\KM_Solution\Customer.DAL\bin\Debug\Customer.DAL.dll");
return Assembly.Load(asname);
}
But I still get the exception. I checked AppDomain.CurrentDomain.GetAssemblies() and I can find my assembly, but it still throws the exception that Unity cannot resolve the type...
I would handle the AssemblyResolve event and load the assembly if you need to:
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
IUnityContainer container = new UnityContainer()
.LoadConfiguration();
// Example of resolving without hard reference
var customerDal = container.Resolve(
Type.GetType("Customer.Interfaces.Repository.IRepositoryDepartment, Customer.Interfaces"));
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string assemblyName = #"C:\xxxxx\" + args.Name + ".dll";
if (File.Exists(assemblyName))
{
return Assembly.LoadFile(assemblyName);
}
return null;
}
In terms of why LoadFile doesn't work, the reason is the Load Context. There are 3 contexts: Load, LoadFrom, and Neither. When you use LoadFile or Load(byte[]) then the assembly is loaded into the Neither context. In the neither context nothing can bind to the assembly without using AssemblyResolve.
See:
Understanding The CLR Binder
Best Practices for Assembly Loading
Suzanne Cook's Blog: Choosing a Binding Context and LoadFile vs. LoadFrom