Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IsAssignableFrom not working #44222

Closed
SamuelJohnsonMedia opened this issue Nov 3, 2020 · 13 comments
Closed

IsAssignableFrom not working #44222

SamuelJohnsonMedia opened this issue Nov 3, 2020 · 13 comments
Labels
area-AssemblyLoader-coreclr question Answer questions and provide assistance, not an issue with source code or documentation.
Milestone

Comments

@SamuelJohnsonMedia
Copy link

SamuelJohnsonMedia commented Nov 3, 2020

Description

Following the steps to create a plugin architecture as shown in the MS Docs and is failing to compare the implemented interface from the loaded assembly using:

foreach( var type in assembly.GetTypes() )
{
  var found = typeof( ICommand ).IsAssignableFrom( type );
  // found should be true but is not!
}

Whereas this works (as you would expect):

var found = typeof( ICommand ).IsAssignableFrom( typeof( HelloCommand ) )

So does this:

var myFilter = new TypeFilter( (c, o) => c.ToString() == o.ToString() );
var interfaces = type.FindInterfaces( myFilter, typeof( ICommand ) );
if( interfaces.Length > 0 )
{
  // found
}

Please see attached solution for example:
PluginsTest.zip

Configuration

Dotnet: .net 5.0.100-rc.2.20479.15
OS: Windows 10 Pro
Configuration: Any CPU (running on x64 laptop)

Regression?

This doesn't seem to work with .netcore3.1 or 3.0 either in either Visual Studio 2019 or 2019 Preview 6

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added the untriaged New issue has not been triaged by the area owner label Nov 3, 2020
@Dotnet-GitSync-Bot
Copy link
Collaborator

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@am11
Copy link
Member

am11 commented Nov 3, 2020

The issue is that ICommand is loaded as different type. See dotnet/docs#15811.
In your attached project, apply this patch to get the expected result:

--- a/HelloPlugin/HelloPlugin.csproj
+++ b/HelloPlugin/HelloPlugin.csproj

  <ItemGroup>
-    <ProjectReference Include="..\PluginBase\PluginBase.csproj" />
+    <ProjectReference Include="..\PluginBase\PluginBase.csproj" Private="false" ExcludeAssets="runtime" />
  </ItemGroup>

@ghost
Copy link

ghost commented Nov 4, 2020

Tagging subscribers to this area: @vitek-karas, @agocke
See info in area-owners.md if you want to be subscribed.

@SamuelJohnsonMedia
Copy link
Author

SamuelJohnsonMedia commented Nov 4, 2020

@am11 that does not work for me no matter what I try I have tried your suggestion but also the docs suggestion:

  <ItemGroup>
    <ProjectReference Include="..\PluginBase\PluginBase.csproj">
      <Private>false</Private>
      <ExcludeAssets>runtime</ExcludeAssets>
    </ProjectReference>
  </ItemGroup>

@am11
Copy link
Member

am11 commented Nov 4, 2020

It needs clean folder: delete all bin and obj directories (or extract the zip attachment in new place, apply the patch and build+run).

@SamuelJohnsonMedia
Copy link
Author

SamuelJohnsonMedia commented Nov 4, 2020

Yes I have done all that multiple times; I have manually deleted all the bin/obj folders

@am11
Copy link
Member

am11 commented Nov 4, 2020

My steps on Windows 10 are:

  • Download .NET 5 RTM zip file for Windows from https://github.com/dotnet/installer#installers-and-binaries, and extract at C:\Temp\dotnet5\.
  • Download your zip file and apply the aforementioned patch, along with:
    --- Program.cs
    +++Program.cs
            // Load commands from plugins.
            string[] pluginPaths = new string[]
            {
    -            "C:/Projects/Tests/PluginsTest/HelloPlugin/bin/Debug/net5.0/HelloPlugin.dll"
    +           "PluginsTest/HelloPlugin/bin/Debug/net5.0/HelloPlugin.dll"
            };
  • Execute these in sequence:
    # PowerShell
    $ \temp\dotnet5\dotnet build
    $ \temp\dotnet5\dotnet run PluginsTest.csproj

outputs:

Loading commands from: C:\Temp\PluginsTest\HelloPlugin\bin\Debug\net5.0\HelloPlugin.dll
-- .\PluginsTest.csproj --
No such command is known.

which is different than what it produces without the HelloPlugin.csproj patch:

Loading commands from: C:\Temp\PluginsTest\HelloPlugin\bin\Debug\net5.0\HelloPlugin.dll
System.ApplicationException: Can't find any type which implements ICommand in HelloPlugin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null from C:\Temp\PluginsTest\HelloPlugin\bin\Debug\net5.0\HelloPlugin.dll.
Available types: HelloPlugin.HelloCommand
   at PluginsTest.Program.CreateCommands(Assembly assembly)+MoveNext() in C:\Temp\PluginsTest\Program.cs:line 107
   at System.Collections.Generic.List`1.InsertRange(Int32 index, IEnumerable`1 collection)
   at System.Collections.Generic.List`1.AddRange(IEnumerable`1 collection)
   at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.ToList()
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at PluginsTest.Program.Main(String[] args) in C:\Temp\PluginsTest\Program.cs:line 29

@SamuelJohnsonMedia
Copy link
Author

That looks like it hasnt worked; it should have output the commands available in the PlugIn and not No such command is known

@am11
Copy link
Member

am11 commented Nov 4, 2020

So we can't blame IsAssignableFrom API for this?

      foreach( Type type in assembly.GetTypes() )
      {
        if( typeof( ICommand ).IsAssignableFrom( type ) )
        {
          var result = Activator.CreateInstance( type ) as ICommand;
          if( result != null )
          {
            count++;
            yield return result;
          }
        }
      }

      if( count == 0 )
      {
        string availableTypes = string.Join( ",", assembly.GetTypes().Select( t => t.FullName ) );
        throw new ApplicationException(
            $"Can't find any type which implements ICommand in {assembly} from {assembly.Location}.\n" +
            $"Available types: {availableTypes}" );
      }

IsAssignableFrom seems to be working as expected.

@SamuelJohnsonMedia
Copy link
Author

I delete the whole project and re-downloaded, applied your patch and then ran it through VS 2019 Preview and it worked:

Loading commands from: C:\Projects\Tests\PluginsTest\HelloPlugin\bin\Debug\net5.0\HelloPlugin.dll
Commands:
hello    - Displays hello message.

Feels a bit flakey, but thanks ... now to try and get this working in my actual project

@am11
Copy link
Member

am11 commented Nov 4, 2020

The code is written in a way that it looks for command in first argument, we need to publish it first, as opposed to running with dotnet-run (which passes first argument as project path):

$ \temp\dotnet5\dotnet publish
$ \temp\dotnet5\dotnet bin\Debug\net5.0\PluginsTest.dll hello
Loading commands from: C:\Temp\PluginsTest\HelloPlugin\bin\Debug\net5.0\HelloPlugin.dll
-- hello --
Hello !!!

@vitek-karas
Copy link
Member

dotnet run doesn't take the project as a parameter on its own, instead it's an option, so dotnet run Project.csproj will treat Project.csproj as the first command line parameter for the app. If you want to specify the project to run that's done via dotnet -p Project.csproj in which case there will be no command line parameters passed to app. And so dotnet run -p Project.csproj hello will pass hello as the only command line parameter to the app.

@vitek-karas vitek-karas added question Answer questions and provide assistance, not an issue with source code or documentation. and removed untriaged New issue has not been triaged by the area owner labels Nov 4, 2020
@vitek-karas vitek-karas added this to the Future milestone Nov 4, 2020
@SamuelJohnsonMedia
Copy link
Author

I think this can be closed now; thanks for everyones input and help.

@ghost ghost locked as resolved and limited conversation to collaborators Dec 6, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-AssemblyLoader-coreclr question Answer questions and provide assistance, not an issue with source code or documentation.
Projects
None yet
Development

No branches or pull requests

5 participants