2

I'm trying to create a PowerShell-class implementation of an abstract class' function:

class ReadList : Intacct.SDK.Functions.AbstractFunction {

    [string]$ObjectName
    
    ReadList ([string]$ObjectName) {
        $this.ObjectName = $ObjectName
    }

    [void] WriteXml ( [Intacct.SDK.Xml.IaXmlWriter]$xml ) {

        $xml.WriteStartDocument()
        
        $xml.WriteStartElement("get_list")
        $xml.WriteAttributeString("object",$this.ObjectName)    
        $xml.WriteEndElement() # </get_list> 
    
        $xml.WriteEndDocument()
        $xml.Flush()
        $xml.Close()

    }

}

When I attempt to use it, I get a run-time exception:

Error during creation of type "ReadList". Error message: Method 'WriteXml' in type 'ReadList' from assembly 'PowerShell Class Assembly, Version=1.0.0.1, | Culture=neutral, PublicKeyToken=null' does not have an implementation.

I've tried adding the ref tag:

[void] WriteXml ( [Intacct.SDK.Xml.IaXmlWriter][ref]$xml ) {

But I get a different error:

Multiple type constraints are not allowed on a method parameter.

Am I doing something wrong, or is what I'm trying to do not supported?

2

2 Answers 2

2

As far as I can tell this is not supported. I have not been able to find a good workaround, or a resource that succinctly explains it.

Technically the second implementation is correct, but as per the error PowerShell does not support multiple type constraints on a method parameter[1].
Normally this isn't the end of the world. The problem is when inheriting from an abstract class, the class must define all of the abstract methods [2], so a method with signature WriteXml (ref IaXmlWriter <ParameterName>) must be defined. Anything else gives the Method 'WriteXml' ... does not have an implementation.

Even if PowerShell supported multiple type constraints, I don't think this would work as intended because the c# is not the same as PowerShell's [ref] - [ref] was created to support COM Objects and the documentation explicitly states it can't be used to type-cast class members[3].

The only workaround I'm aware of that might work here is writing the ReadList class definition in c#, and adding to PowerShell via Add-Type[4]... not great.

All this is beyond the scope of my knowledge; maybe there is a good solution and someone with more know-how can correct me.


References
[1] SO post that touches on this and [ref] (same as below)

Looking at $Error.Exception.StackTrace, System.Management.Automation.ScriptBlock.Create is raising the exception. Couldn't go further with PowerShell 5.1 but PowerShell Core files include a check for class method parameters here which points to the MultipleTypeConstraintsOnMethodParam exception that gets raised.

Scripting.Classes.BasicParsing.Tests.ps1 checks multiple types will raise a MultipleTypeConstraintsOnMethodParam exception.

[2] Derived classes of the abstract class must implement all abstract methods.
Abstract and Sealed Classes and Class Members

[3] SO post that touches on this and multiple type constraints (same as above)

The PSReference type is not supported with class members
about_Classes

[4] Add a .NET type to a session


Edit
To elaborate on the Add-Type solution

  • Use ReferencedAssemblies to pass in Intacct.SDK.dll and dependent assemblies.
  • Load these assemblies using [Reflection.Assembly]

Use Load() when specifying version or LoadFromPartialName() with just the name, LoadFrom() when specifying the path.

$Assemblies = @(
    'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'
    'System.Xml.ReaderWriter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
) |
foreach {
    [Reflection.Assembly]::Load($_)
}

$Assemblies += @(
    "$pwd/Microsoft.Extensions.Logging.Abstractions.dll"
    "$pwd/Intacct.SDK.dll"
) | foreach {
    [Reflection.Assembly]::LoadFrom($_)
}

Add-Type -TypeDefinition $Source -ReferencedAssemblies $Assemblies
Sign up to request clarification or add additional context in comments.

Comments

0

I came to the same conclusion that I would need to create a C# class definition and import it.

Push-Location ~/Desktop

$Source = @"
using Intacct.SDK.Functions;
using Intacct.SDK.Xml;

public class GetList : Intacct.SDK.Functions.AbstractFunction
{
    public string ObjectName;

    public GetList (string objectName) 
    {
        this.ObjectName = objectName;
    }

    public override void WriteXml( ref Intacct.SDK.Xml.IaXmlWriter xml )
    {
        xml.WriteStartDocument();        
        xml.WriteStartElement("get_list");
        xml.WriteEndDocument();
        xml.Flush();
    }
}
"@
    
# netstandard and ReaderWriter are located in `$PSHOME`; others on Desktop
$Assemblies = @(
    'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'
    'System.Xml.ReaderWriter, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
    "$pwd/Microsoft.Extensions.Logging.Abstractions.dll"
    "$pwd/Intacct.SDK.dll"
)

# using ReferenceAssemblies based on a suggestion from the PowerShell Slack channel
$Type = Add-Type -TypeDefinition $Source -ReferencedAssemblies $Assemblies -PassThru

$GetList = [GetList]::new('something')
$GetList

which generates errors:

Add-Type: getlist.ps1:37:9
Line |
  37 |  $Type = Add-Type -TypeDefinition $Source -ReferencedAssemblies $Assem …
     |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Unable to load one or more of the requested types. Could not load file or assembly 'Intacct.SDK, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. The parameter is incorrect.  (0x80070057 (E_INVALIDARG))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.