Перенаправление сборки конфигурации Powershell

У меня есть пользовательская сборка.NET с некоторыми командлетами powershell, которые я использую для общих задач, связанных с доменом. Я только что создал новый командлет, который ссылается на стороннюю библиотеку, которая имеет ссылку на Newtonsoft.Json 4.5.0.0. Однако один из моих других проектов использует последнюю версию json.net (6.0.0.0). Поэтому во время выполнения в PowerShell Fusion выдает ошибку, в которой говорится, что он не может загрузить newtonsoft.json 4.5.0.0.

Я попытался создать powershell.exe.config и поместить туда перенаправление сборки:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json", Culture=neutral,     PublicKeyToken=30ad4fe6b2a6aeed/>
        <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

но это не похоже на работу. Журнал Fusion заявляет, что он ищет в этом новом конфигурационном файле PowerShell, но, похоже, он не перехватывает перенаправление.

Немного озадачен для решения здесь. Любые подсказки, в чем может быть проблема? Такое же перенаправление работает в некоторых из моих бизнес-сервисов, которые в противном случае имели бы ту же проблему (они также используют сторонние lib и json.net 6).

ура

3 ответа

Решение

Не уверен, как это работало больше года назад, однако сегодня на Windows 10 с использованием PowerShell 5.0.10240.16384 единственный способ, которым я смог сделать перенаправление сборки (в моем случае из FSharp.Core 4.3 - 4.4) - вручную разрешать зависимости сборок на основе разрешения сборок вручную в PowerShell. Я пробовал любые другие решения, такие как создание powershell.exe.config файл или пытается загрузить какой-то другой *.config file, но ничего из этого не сработало.

Единственная "ошибка" (в аренде для меня) заключалась в том, что, поскольку у меня нигде нет FSharp.Core 4.3, мне нужно было вручную перенаправить его на 4.4. Я в конечном итоге с помощью

$FSharpCore = [reflection.assembly]::LoadFrom($PSScriptRoot + "\bin\LIBRARY\FSharp.Core.dll") 

$OnAssemblyResolve = [System.ResolveEventHandler] {
  param($sender, $e)

  # from:FSharp.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
  # to:  FSharp.Core, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
  if ($e.Name -eq "FSharp.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a") { return $FSharpCore }

  foreach($a in [System.AppDomain]::CurrentDomain.GetAssemblies())
  {
    if ($a.FullName -eq $e.Name)
    {
      return $a
    }
  }
  return $null
}

[System.AppDomain]::CurrentDomain.add_AssemblyResolve($OnAssemblyResolve)

где я впервые загружаю правильную версию FSharp.Core откуда-то, поскольку версия в GAC старая (я думаю, это может быть и ваш случай)

Вы также можете проверить реальное использование теста в моем проекте.

Основываясь на ответе @davidpodhola, мне нужно было что-то общее, где я хотел, чтобы оно разрешалось в сборку с тем же именем, пока оно загружено, и я бы взял на себя ответственность за загрузку правильной версии заранее.

Таким образом, этот код может быть универсальным, и вы можете добавить его в любой сценарий, выполняющий разрешение подстановочных знаков только для имени сборки. Добавлено сюда, если кому-то еще это нужно:

      $OnAssemblyResolve = [System.ResolveEventHandler] {

    param($sender, $e)

    $searchFor = $null
    if ($e.Name -match "(.*?), .*") {
        $searchFor = $matches[1]
    }
    foreach ($a in [System.AppDomain]::CurrentDomain.GetAssemblies()) {

        $foundItem = $null
        if ($a.FullName -match "(.*?), .*") {
            $foundItem = $matches[1]
        }
    
        if ($foundItem -eq $searchFor) {
            return $a
        }
    }
    return $null
}
[System.AppDomain]::CurrentDomain.add_AssemblyResolve($OnAssemblyResolve)

Я обнаружил, что при попытке разрешить сборки с обработчиком, прикрепленным в PowerShell, с помощью [System.AppDomain]::CurrentDomain.add_AssemblyResolve может вызвать проблемы, особенно в PowerShell ISE.

В качестве альтернативы это можно было бы обработать в сборке корневого модуля (содержащей PSCmdlet классы) путем реализации IModuleAssemblyInitializer интерфейс.

Используя принятый ответ в качестве примера, его можно переписать как:

      public class ModuleInitializer : IModuleAssemblyInitializer
{
    public void OnImport()
    {
        var fSharpCore = Assembly.LoadFrom(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "FSharp.Core.dll"));

        AppDomain.CurrentDomain.AssemblyResolve += (object sender, ResolveEventArgs e) =>
        {
            // from:FSharp.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
            // to:  FSharp.Core, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
            if (e.Name == "FSharp.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a") { return fSharpCore; }

            foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (a.FullName == e.Name)
                {
                    return a;
                }
            }

            return null;
        };
    }
}

В моем случае я использовал этот механизм для поиска сопутствующих .dll.config и перенаправить в соответствии с dependentAssembly/bindingRedirect элементы.

Другие вопросы по тегам