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

Bump for 2.2 and 3.0 #47

Closed
JustinGrote opened this issue Oct 17, 2019 · 10 comments
Closed

Bump for 2.2 and 3.0 #47

JustinGrote opened this issue Oct 17, 2019 · 10 comments
Assignees

Comments

@JustinGrote
Copy link

Currently locked to 2.0 (without using a binding redirection), can we get packages for 2.2 and 3.0?

@andrewlock
Copy link
Owner

andrewlock commented Oct 17, 2019

That shouldn't be necessary - the 2.0 is a lower bound. If you're running on .NET Core 2.2 or 3.0 then the packages should be automatically pulled up to the correct version.

If you're using it in a library, then you may need to add an explicit reference to the correct version of Microsoft.Extensions.Configuration (2.1.x/2.2.x/3.0.0) but that should work without issue?

(without using a binding redirection)

Unless you're running on .NET Framework (which only supports ASP.NET Core 2.1) then there is no binding redirection 🙂 In .NET Core, binding redirects are essentially automatic.

Does that help? If not, feel free to share the project file that is causing you issues 🙂

@JustinGrote
Copy link
Author

JustinGrote commented Oct 18, 2019 via email

@andrewlock
Copy link
Owner

Ah, interesting. And irritating. Most libraries only increment the assembly version on a major version bump, exactly to avoid these issues 🙄

However, I've just updated the samples, and they seem to show there's no issues. As I mentioned already, .NET Core doesn't have binding redirects, so there's no issues there.

The only place you could have issues is if you're using .NET Framework. but .NET Framework is only supported on .NET Core 2.1. I've updated the sample/WebDemoProject2_0 to use ASP.NET Core 2.1 running on net461 and there seems to be no issues?

I also a .NET Core 3.0 sample that shows there's no issues there either?

I am going to bump the dependencies on these projects at some point when I find time, but I will likely bump them to 2.1, as then they'll support the 2.1 LTS at a minimum, but will work anything 2.1+ as far as I can tell.

@JustinGrote
Copy link
Author

JustinGrote commented Oct 18, 2019 via email

@JustinGrote
Copy link
Author

OK, so to clarify, the issue is for building a .NET standard 2.0 app when running it on .NET framework. (In my case, I'm making a powershell configuration module), because .NET framework requires strong naming.

Given the following dependencies:
'Microsoft.Extensions.Configuration.CommandLine' = '2.'
'Microsoft.Extensions.Configuration.Json' = '2.
'
'Microsoft.Extensions.Configuration.FileExtensions' = '2.'
'Microsoft.Extensions.Configuration.EnvironmentVariables' = '2.
'
'NetEscapades.Configuration.Yaml' = '1.6'

The built module loads just fine in Powershell Core (.NET Core 2.1) but fails in Windows Powershell (.NET Framework) due to the assembly versions not matching (NetEscapades is looking for 2.0.0.0 but 2.2 is what is available)

If I explicitly specify 2.0.0.0, then it works on both platforms, however it comes with a lot of extra unnecessary DLLs because there were a lot of APIs added since 2.0.0.0 to .NET standard, and when built with 2.2 or 3.0 it comes out much smaller (plus the extra features after all)

So to solve this, I can either try to:

  1. Get a binding redirect to work with Windows Powershell in a shippable manner
  2. Ask you to make separate builds that reference 2.2 or 3.0 respectively :)

@andrewlock
Copy link
Owner

Ah interesting, I don't know much about PowerShell so way out of my comfort zone there! Just want to clarify a few more things, as should be able to reproduce this outside of the PowerShell context I think.

OK, so to clarify, the issue is for building a .NET standard 2.0 app when running it on .NET framework

There's no such thing as a .NET Standard 2.0 app. There's a .NET Standard library, or there's a .NET Framework app that references .NET Standard libraries. I'm not entirely sure how it works with PowerShell though. Do you create libraries, or console apps when you create a module?

If I explicitly specify 2.0.0.0, then it works on both platforms, however it comes with a lot of extra unnecessary DLLs because there were a lot of APIs added since 2.0.0.0 to .NET standard.

I'm not sure I understand this statement - if you use version 2.x of the libraries, they all target .NET Standard 2.0. There are no "APIs added since 2.0.0.0 to .NET standard" - APIs have been added to .NET Standard 2.1, but you're not targeting that here?

Is the module you're working on open source, I think it would really help for me to understand 🙂

Ask you to make separate builds that reference 2.2 or 3.0 respectively :)

The problem is, there's no real way for me to do this... The libraries all target .NET Standard 2.0, so there's no way to conditionally reference other packages. That's the intention with the current package references I've used - if you are targeting a different version of the Microsoft.Extensions packages, NuGet will use the correct package references.

Sorry to drag this out, I'm not trying to be obstinate, I just don't quite understand the problem yet I think (and if I do, there's no easy fix 🙁)

@JustinGrote
Copy link
Author

No problem! I appreciate the discussion.

Apologies on the loose terminology. Let me clarify a few things:

  1. Yes, I'm targeting .NET standard 2.0
  2. I misspoke on the APIs, what I meant to say is that Microsoft.Extensions.Configuration 2.2+ adjusted their dependencies to align with .NET standard 2.0 much better, so the result is a lot less extra DLLs that didn't strictly match before, hence the attractiveness, though not a big deal.
  3. You can target .NET standard 2.0 but have Microsoft.Extensions.Configuration 2.2+ as a requirement instead of 2.0 as a build. When you build it, the assemblyversion of M.E.C. that your DLL marks to rely on will become 2.2 instead of 2.0.0.0, so it can import against that dependency in .NET framework without a binding redirect. I think it's dumb Microsoft revved the assemblyversion on such a relatively common library, but maybe that's just their plan with ASP.NET core, even though the extensions were designed such that they don't require ASP.NET core.

So, all that being said, I came up with a workaround that hooks into the ResolveEventHandler a script that effectively overlays the "new" assemblies on the top, just how a binding redirect does. When your DLL asks for 2.0.0.0, it actually returns the 2.2 DLL and loads it. Seems to work fine.

function Import-Assembly {
<#
.SYNOPSIS
Adds Binding Redirects for Certain Assemblies to make them more flexibly compatible with Windows Powershell
#>
    [CmdletBinding()]
    param(
        #Path to the dependencies that you wish to add a binding redirect for
        [Parameter(Mandatory)][IO.FileInfo[]]$Path
    )
    if ($PSEdition -ne 'Desktop') {
        write-warning "Import-Assembly is only required on Windows Powershell and not Powershell Core. Skipping..."
        return
    }

    $pathAssemblies = $path.foreach{
        [reflection.assemblyname]::GetAssemblyName($PSItem)
    }
    $loadedAssemblies = [AppDomain]::CurrentDomain.GetAssemblies()

    $onAssemblyResolveEventHandler = [ResolveEventHandler] {
        param($sender, $assemblyToResolve)

        $assemblyToResolveName = $assemblyToResolve.Name.split(',')[0]

        $bindingRedirectMatch = $pathAssemblies.where{
            $PSItem.Name -eq $assemblyToResolveName
        }

        if ($bindingRedirectMatch) {
            write-host -fore Magenta "Import-Assembly: Creating a 'binding redirect' to $BindingRedirectMatch"
            return [reflection.assembly]::LoadFrom($bindingRedirectMatch.CodeBase)
        }

        $loadedAssembliesMatch = $loadedAssemblies.where{$PSItem.FullName -eq $assemblyToResolve.FullName}
        if ($loadedAssembliesMatch) {
            write-host -fore Magenta "Returning Already Loaded Assembly for $assemblyToResolveName"
            return $loadedAssembliesMatch
        }

        #If no matches return null
        write-host -fore Magenta "Returning Null for $assemblyToResolveName"
        return $null
    }
    [AppDomain]::CurrentDomain.add_AssemblyResolve($onAssemblyResolveEventHandler)

    Add-Type -Path $Path

    [System.AppDomain]::CurrentDomain.remove_AssemblyResolve($onAssemblyResolveEventHandler)
}

$ImportAssemblies = Get-Item "$PSScriptRoot/lib/*.dll"
if ($PSEdition -eq 'Desktop') {
    Import-Assembly -Path $ImportAssemblies
} else {
    Add-Type -Path $ImportAssemblies
}

But I agree pursuing this workaround route is better than having you build multiple packages for each Microsoft.Extensions.Configuration revision, since most other .NET apps can just use a binding redirect, however that's basically impossible to do in a Powershell Module since Powershell gets loaded first, and you don't want to be dropping a .config file into someone's global powershell exe file :)

@andrewlock
Copy link
Owner

Well congrats on figuring that out - what a nightmare! I have to admit, I've never even considered these libraries being used by PowerShell..

In all honesty, I'm not sure what the best option is with all this. It's a bit of a mess 🙁

@JustinGrote
Copy link
Author

This is good enough for now, no need to create work on your side.

Powershell is just a .NET application like any other, so any othe application targeting the .NET standard runtime would run into this same problem, the difference is that at compile time, dotnet will automatically add binding redirects to the config file, unfortunately I have to effectively dynamically generate those at runtime.

This works though, and opens up a much wider range of module use in Powershell. With Powershell 7 we will have .NET 3.1 and be able to try to use LoaderContext :)

@JustinGrote
Copy link
Author

Closing for now with the "workaround" on .NET framework to be "just use binding redirects"

Here is the project BTW:
https://github.com/justingrote/powerconfig

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants