Loading .NET assembly dependencies not working
Asked Answered
C

1

6

I have been searching for a solution for a couple of days. None of the answers work in my case which is to reference (load?) assemblies .NET into app domain from PowerShell session.

I load references first (that are required to be referenced by the aforementioned DLL to be working [Reflection.Assembly]::LoadFile() or [Reflection.Assembly]::LoadFrom()) then I load my .NET DLL by calling Add-Type.

Unfortunately that is not working so I cannot create a few instances from that DLL. I am getting the same errors when I use DLL without references attached in a normal C# project but as soon as I reference the other assemblies and recompile it works without errors (I can confirm it is because of referenced assemblies as I checked that in LinqPad as well).

PowerShell:

[System.Reflection.Assembly]::LoadFile((Get-Item -Path ".\System.Data.SQLite.dll" ).FullName)
Add-Type -Path (Get-Item -Path ".\Connector.dll" ).FullName -ReferencedAssemblies (Get-Item -Path ".\System.Data.SQLite.dll" ).FullName -PassThru | Out-Null
$certMGT = New-Object Connector

third line of that PowerShell script throws:

New-Object : Exception calling ".ctor" with "0" argument(s): "Failed to find or load the registered .Net Framework Data Provider."
At C:\Repos\Connector\bin\Installer.ps1:306 char:20
+         $certMGT = New-Object Connector
+                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

PSMessageDetails      : 
Exception             : System.Management.Automation.MethodInvocationException: Exception calling ".ctor" with "0" argument(s): "Failed to find or load the registered .Net Framework
                         Data Provider." ---> System.Configuration.ConfigurationErrorsException: Failed to find or load the registered .Net Framework Data Provider.
                           at System.Data.Common.DbProviderFactories.GetFactory(DataRow providerRow)
                           at System.Data.EntityClient.EntityConnection.GetFactory(String providerString)
                           at System.Data.EntityClient.EntityConnection.ChangeConnectionString(String newConnectionString)
                           at System.Data.Objects.ObjectContext..ctor(String connectionString, String defaultContainerName)
                           at Connector.Entity.ConnectorDBEntities..ctor(String connectionString)
                           at Connector.DBManager..ctor()
                           at Connector.DAL.ConfigurationDAL..ctor()
                           at Connector.ConnectorConfig..ctor()
                           at Connector.ConnectorCertMGT..ctor()
                           --- End of inner exception stack trace ---
                           at System.Management.Automation.DotNetAdapter.AuxiliaryConstructorInvoke(MethodInformation methodInformation, Object[] arguments, Object[] originalArgumen
                        ts)
                           at System.Management.Automation.DotNetAdapter.ConstructorInvokeDotNet(Type type, ConstructorInfo[] constructors, Object[] arguments)
                           at Microsoft.PowerShell.Commands.NewObjectCommand.CallConstructor(Type type, ConstructorInfo[] constructors, Object[] args)
TargetObject          : 
CategoryInfo          : InvalidOperation: (:) [New-Object], MethodInvocationException
FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand
ErrorDetails          : 
InvocationInfo        : System.Management.Automation.InvocationInfo

LinqPad query (C# Program; references Connector.dll) - this works fine

void Main()
{
    Assembly.LoadFile(@"C:\Repos\Connector\bin\System.Data.SQLite.dll");
    Connector connector = new Connector();//this also throws exactly the same error if I do not LoadFile as in above line
}
Cabriole answered 20/11, 2015 at 8:42 Comment(14)
So, what's the problem? Can't you just load the dependencies/references before calling Add-Type?Inhume
Yes, I do load them ( Add-Type ) before and it seems to be loaded to a different scope so that the main DLL do not resolve the assemblies properly. I have the same code in LINQPad and it works.Cabriole
Have you tried using Assembly.LoadFrom?Larrabee
Now I'm confused. Could you post a code sample ?Inhume
Well... that's what I wanted to ask you for. You've said you're using LoadFile to load dependent assemblies. AFAIR, LoadFrom shall be used in such cases. Paste your code, pleaseLarrabee
LoadFrom does not work as well. There must me something with the domain.Cabriole
Can we see full stack trace of exception $Error[0]|fl -Force? Does Connector your own class? Can we see its source?Macronucleus
Connector class is not my class. I cannot change it. Stacktrace attached.Cabriole
I bet this must be something with resolving assemblies.Cabriole
If it is possible to debug, it would be good to know what exact argument is passed to this call: System.Data.Common.DbProviderFactories.GetFactory(DataRow providerRow).Macronucleus
Why you don't use Add-Type for System.Data.SQLite.dll ?Tribble
Of course I tried that. I have a suspicion that PowerShell domain has some problems in locating or choosing native dlls (\x86\SQLite.Interop.dll \x64\SQLite.Interop.dll). When I retrive [System.Type]::GetType("System.Data.SQLite.SQLiteFactory, System.Data.SQLite") I got the error: System.IO.FileLoadException: Could not load file or assembly 'System.Data.SQLite.SQLiteFactory\, System.Data.SQLite' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)Cabriole
The problem probably lies in your "Connector" class code. It's the one who calls into other classes that ultimately causes that error. What are you passing in Connector.Entity.ConnectorDBEntities..ctor(String connectionString)Borlase
System.Data.SQLite is an assembly that does quite unusual stuff. Which version are you using? Did you try the mixed-mode one?Preceding
L
0

Step 1 Compile Connector.dll - presumably you already have that done

$SQLitePath = convert-path '.\res\sqlite-netFx46-binary-bundle-x64-2015-1.0.114.0\System.Data.SQLite.dll'
add-type -path $SQLitePath

$null = new-item '.\Connector.cs' -value @'
using System;
using System.Data.SQLite;

namespace Powershell.Examples{
public class Connector : System.IDisposable {
   SQLiteConnection con;

   public Connector(){}
   
   public void connect(string Path){
      con = new SQLiteConnection();
      con.ConnectionString = "Data Source=" + Path;
      con.Open();
   }
   
   public void close(){
      if(con.State == System.Data.ConnectionState.Open)
         con.Close();
   }

   bool _disposed = false;
   public void Dispose(){
      Dispose(disposing: true);
      GC.SuppressFinalize(this);
   }
   protected virtual void Dispose(bool disposing){
      if(_disposed) return;
      if(disposing){
         // free managed objects here
         con.Dispose();
      }
      // freee unmanaged objects here
      
      _disposed = true;
   }
   ~Connector(){ Dispose(disposing:false); }
}}
'@
$pams = @{
   TypeDefinition = get-content '.\Connector.cs' -raw
   OutputAssembly = '.\Connector.dll'
   OutputType     = 'Library'
   ReferencedAssemblies = @($SQLitePath,'System.Data')
}
$null = add-type @pams -PassThru

Step 2 Use it in Powershell

PS C:\Working\Folder> $SQLitePath = convert-path '.\res\sqlite-netFx46-binary-bundle-x64-2015-1.0.114.0\System.Data.SQLite.dll'
PS C:\Working\Folder> add-type -Path $SQLitePath
PS C:\Working\Folder> add-type -Path (convert-path '.\Connector.dll') -ReferencedAssemblies $SQLitePath

PS C:\Working\Folder> $obj = [Powershell.Examples.Connector]::new()
PS C:\Working\Folder> $obj.connect((Convert-Path .)+'\MyData.db')
PS C:\Working\Folder> $obj.close()
PS C:\Working\Folder> $obj.Dispose()
PS C:\Working\Folder> rv obj
Lucullus answered 19/1, 2023 at 3:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.