Несоответствие версии загрузки сборки: почему она загружается?

#.net #assemblies #dll

#.net #сборки #dll

Вопрос:

У меня есть две сборки: HelloWorld.exe и Hello.dll . EXE-файл является основной сборкой, а dll используется основной сборкой.

Я скомпилировал оба HelloWorld.exe (1.0.0) и Hello.dll (1.0.0). Я поместил сборки в другую папку.

Затем я изменил версию Hello.обновил dll до 2.0.0 и продолжил перезаписывать Hello.dll 1.0.0 с версией 2.0.0. Затем я запускаю HelloWorld.exe и это сработало нормально.

Я ожидал, что это приведет к сбою и немедленной записи, потому что ссылочный Hello.dll, когда я компилировал EXE, была 1.0.0. Теперь DLL 1.0.0 была заменена на 2.0.0, но она все еще работала!

Согласно MSDN:

По умолчанию сборка будет использовать только типы из точно такой же сборки (имя и номер версии), с которой она была собрана и протестирована. То есть, если у вас есть сборка, которая использует тип из версии 1.0.0.2 другой сборки, она не будет (по умолчанию) использовать тот же тип из версии 1.0.0.4 другой сборки. Такое использование как имени, так и версии для идентификации сборок, на которые даны ссылки, помогает избежать проблемы «Ада DLL», связанной с обновлением одного приложения, приводящего к поломке других приложений.

Вопросы:

  1. Почему это сработало?
  2. Как заставить ее НЕ работать?
  3. ДОПОЛНИТЕЛЬНЫЙ ВОПРОС: Что происходит в процессе сборки? Разве версия внешних зависимостей не жестко закодирована для основной зависимости?

Обратите внимание, что Hello.dll не имеет строгого имени.

Вот манифест для HelloWorld.exe:

 // Metadata version: v2.0.50727
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .zV.4..
  .ver 2:0:0:0
}
.assembly extern Hello
{
  .ver 2:0:0:0
}
.assembly HelloWorld
{
...//snipped
}
  

Вот журнал привязки сборки, взятый из Fuslogvw.exe (Просмотр журнала привязки сборки):

 === Pre-bind state information ===
LOG: User = ian.uy
LOG: DisplayName = Hello, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null
 (Fully-specified)
LOG: Appbase = file:///C:/Users/ian.uy/Desktop/HelloWorld/HelloWorld/bin/Debug/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = NULL
Calling assembly : HelloWorld, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: No application configuration file found.
LOG: Using machine configuration file from C:WindowsMicrosoft.NETFrameworkv2.0.50727configmachine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Users/ian.uy/Desktop/HelloWorld/HelloWorld/bin/Debug/Hello.DLL.
LOG: Assembly download was successful. Attempting setup of file: C:Usersian.uyDesktopHelloWorldHelloWorldbinDebugHello.dll
LOG: Entering run-from-source setup phase.
LOG: Assembly Name is: Hello, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
LOG: Binding succeeds. Returns assembly from C:Usersian.uyDesktopHelloWorldHelloWorldbinDebugHello.dll.
LOG: Assembly is loaded in default load context.
  

Ответ №1:

Я столкнулся с тем же самым и попытался заглянуть в журналы msbuild:

 msbuild /v:detailed /t:build
  

Следующие строки выглядели интересными:

Унифицированная зависимость «Newtonsoft.Json, Version = 8.0.0.0, Culture = нейтральный, PublicKeyToken=30ad4fe6b2a6aeed». Использование этой версии вместо оригинальной версии «7.0.0.0» в «C:srcBindingTestLib1binDebugLib1.dll » потому что автоматическое объединение имеет значение «true».

Кроме того, если вы проверите результирующий файл app.config после сборки, вы, вероятно, увидите там автоматическое перенаправление привязки, которого изначально не было в вашем app.config.

Итак, поведение, которое мы наблюдаем, связано с процессами msbuild «автоматической унификации сборки» и «автоматического перенаправления привязки«.

Вот что в документации говорится о AutoUnify параметре:

При значении true результирующий график зависимостей автоматически обрабатывается так, как если бы в параметр AppConfigFile был передан файл app.Config. Этот виртуальный файл App.Config содержит запись bindingRedirect для каждого конфликтующего набора сборок, так что выбирается сборка с самой высокой версией. Следствием этого является то, что никогда не будет предупреждения о конфликтующих сборках, потому что каждый конфликт будет разрешен.

При значении true каждое отдельное переназначение приведет к комментарию с высоким приоритетом, показывающему старую и новую версии и что автоматическое объединение было истинным.

Наконец, если вы хотите увидеть «сбой», вы можете вызвать msbuild со следующими аргументами:

 msbuild /v:d /t:build /p:AutoUnifyAssemblyReferences=false;AutoGenerateBindingRedirects=false
  

Ответ №2:

Почему это сработало?

Потому что вы так указали 😉

Как заставить ее НЕ работать?

  1. Щелкните правой кнопкой мыши на DLL в обозревателе решений
  2. Выберите Свойства
  3. Выберите использовать определенную версию

ДОПОЛНИТЕЛЬНЫЙ ВОПРОС: Что происходит в процессе сборки? Разве версия внешних зависимостей не жестко закодирована для основной зависимости?

Нет, если вы не укажете это. По умолчанию она отключена. Хорошо спроектированные сборки должны быть обратно совместимы, и версия на самом деле не должна иметь значения.

Комментарии:

1. Я думаю , что сборка со строгим именем будет работать так, как вы хотите.

2. Я тоже так думаю. Но что означает «Использовать определенную версию», если на самом деле это не означает «использовать определенную версию»?

3. Use specific version влияет только на компиляцию, а не на загрузку во время выполнения.

Ответ №3:

Среда выполнения проводит различие между обычными сборками и сборками со строгими именами для целей управления версиями. Проверка версии происходит только со сборками со строгими именами.

(источник:https://learn.microsoft.com/en-us/dotnet/framework/app-domains/assembly-versioning )

Следовательно, ответы следующие:

Почему это сработало?

Потому что сборка не имеет строгого имени

Как заставить ее НЕ работать?

Сделайте сборку со строгим именем

Что происходит в процессе сборки?

Зависит

  • Use specific version = false: компилируется с первым файлом, соответствующим ссылке в проекте, принимает любую версию
  • Use specific version = true: компилируется с первым найденным файлом, соответствующим ссылке, включая указанную версию в проекте

Разве версия внешних зависимостей не жестко закодирована для основной зависимости?

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