dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.57k stars 10.05k forks source link

Using System.Reflection.TypeExtensions.GetProperties in .NET Core 2.0 #20209

Closed Stamo-Gochev closed 4 years ago

Stamo-Gochev commented 6 years ago

I am using a Class Library project, whose target frameworks are net451 and netstandard1.6, which dynamically compiles a class in a way similar to the example here (a "standard" way to do the compilation):

https://github.com/dotnet/roslyn/issues/16211#issuecomment-372998985

but inside a method in MyClass, I want to retrieve all public properties like this (the actual code is the string of this, which is passed to CSharpSyntaxTree.ParseText:

using System;
public class MyClass
{
    public static void Print()
    {
#if NET451
            var props = this.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
#else
            var props = System.Reflection.TypeExtensions.GetProperties(this.GetType(), System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
#endif
             //additional code
    }   
}

Now, the Class Library project is referenced by an ASP.NET Core web app (assume any third party app that I have no control over), which targets netcore1.1 and as System.Reflection.TypeExtensions.GetProperties is available, it is working as expected.

However, targeting netcore2.0 throws an error although it looks like the method should still be available. The alternative (used for net451 above):

var props = this.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);

is actually working for netcore2.0, but not for netcore1.0 or netcore1.1. Also note that this happens only when compiling the class dynamically - using the MyClasss Print method definition works OK in both netcore1.0 and netcore2.0 without the dynamic compilation.

Can you provide more information why this is happening and if it is required for the netcore2.0 project to "provide" the System.Reflection.TypeExtensions package?

danmoseley commented 6 years ago

@weshaggard seems this could be because of your change below?

commit a4364b283016758aff4d8ea162e6e8c550e2da6f Author: Wes Haggard Wes.Haggard@microsoft.com Date: Mon Oct 16 11:46:43 2017 -0700

Update SupportedFrameworks to netcoreapp2.1 for some packages
weshaggard commented 6 years ago

@danmosemsft I don't believe it is related to that commit. It most likely has to do with changes in the shared framework between 1.1 and 2.0

@Stamo-Gochev is is kind of hard to follow exactly what you are doing and what is compiled when. Could you setup a repo with some sample projects that demonstrate the issue you are hitting and I will take a look and help you figure out a solution.

Stamo-Gochev commented 6 years ago

@danmosemsft @weshaggard

I will be working on isolating the issue in a project that I can send as it is not open-sourced, but what I have found so far is that the problem is caused by the System.Reflection.TypeExtensions assembly not being referenced when the compilation is done and thus the GetProperties method is not available.

The references to the assemblies that are necessary for the compilation are obtained by the GetReferences method below - the Class Library project uses the HostingEnvironment variable, which is passed to it from the ASP.NET Core web app:

public class MyLibrary {
    //injected by the ASP.NET Core web app
    private readonly IHostingEnvironment _environment;

    ...
    public void Compile() 
    {
        //assume a correct string definition of the class
        var tree = CSharpSyntaxTree.ParseText(@"
        using System;
        public class MyClass
        {
            public static void Main()
            {
                var instance = new MyClass();
                var props = System.Reflection.TypeExtensions.GetProperties(instance.GetType(), System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
            }   
        }");
        ...
        var compilation = CSharpCompilation.Create(assemblyName,
                        options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary),
                        syntaxTrees: new[] { tree },
                        references: GetReferences()

        ...
    }
    ...

    //get the avaialble references based on the HostingEnvironment variable
    public IEnumerable<MetadataReference> GetReferences()
    {
        var references = new List<MetadataReference>();

        var applicationAssembly = Assembly.Load(new AssemblyName(_environment.ApplicationName));
        var context = DependencyContext.Load(applicationAssembly);      

        if (context != null)
        {
            for (var i = 0; i < context.CompileLibraries.Count; i++)
            {
                var library = context.CompileLibraries[i];
                IEnumerable<string> referencePaths;
                try
                {
                    referencePaths = library.ResolveReferencePaths();
                }
                catch (InvalidOperationException e)
                {
                    continue;
                }

                //add the references
                references.AddRange(referencePaths...);
            }
        }

        return references;
    }
    ...
}

The above code works fine in ASP.NET Core 1.1 project and the full list of loaded assemblies can be found at: https://pastebin.com/LZuTXchM The System.Reflection.TypeExtensions assembly is loaded and thus the compilation is successful.

On the other hande, the same code fails when executed in ASP.NET Core 2.0 project: https://pastebin.com/64hCTBSe The System.Reflection.TypeExtensions assembly is not loaded. In addition, InvalidOperationExceptions are caught for the following entries:

"Cannot find compilation library location for package 'Microsoft.Win32.Registry'"
"Cannot find compilation library location for package 'System.Data.SqlClient'"
"Cannot find compilation library location for package 'System.Runtime.CompilerServices.Unsafe'"
"Cannot find compilation library location for package 'System.Security.AccessControl'"
"Cannot find compilation library location for package 'System.Security.Cryptography.Xml'"
"Cannot find compilation library location for package 'System.Security.Principal.Windows'"
"Cannot find compilation library location for package 'Microsoft.NETCore.App'"

I assume the whole thing is dependent on what is exposed by the HostingEnvironment variable in the above snippet:

var applicationAssembly = Assembly.Load(new AssemblyName(_environment.ApplicationName));
var context = DependencyContext.Load(applicationAssembly);

(context.CompileLibraries)

After the above observations, it looks like the issue is not connected to dotnet/corefx responsibilities at all, so it should be closed, but should it be forwarded to somebody in the ASP.NET Core team? The sole purpose of this is for them to fix a potential breaking change (if it makes sense to be fixed as this might be expected due to deprecated/removed code intentionally) - the "workaround" is to get the "private" System.Private.CoreLib.dll as mentioned in https://github.com/dotnet/roslyn/issues/16211#issuecomment-372998985 which fixes the problem in the ASP.NET Core 2.0 project. This seems like the proper alternative to:

references.Add(MetadataReference.CreateFromFile(typeof(object).Assembly.Location));

but for .NET Core.

weshaggard commented 6 years ago

@davidfowl @anurse any ideas why HostingEnvironment seems to not work here? Is there some difference between 1.1 and 2.0 that needs to be accounted for?

ghost commented 6 years ago

Reassigning as right now, the investigation actionability is in ASP.NET, not CoreFx.

ghost commented 6 years ago

This should be closed and forwarded to ASP.NET but I don't know which repo to do that in. Changing the milestone so it stops showing up a 2.1 ZBB blocker in CoreFx.

danmoseley commented 6 years ago

@davidfowl where should this move to

analogrelay commented 6 years ago

HostingEnvironment is at https://github.com/aspnet/Hosting.

analogrelay commented 4 years ago

Looks like this is a pretty stale issue, referencing .NET Core 2.0 which is end-of-life. Is this issue still occurring on a supported release of .NET Core (currently 2.1 or 3.1)?

ghost commented 4 years ago

This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment. If it is closed, feel free to comment when you are able to provide the additional information and we will re-investigate.

See our Issue Management Policies for more information.