Как две сборки плагина могут ссылаться на разные версии библиотеки зависимостей?

Представьте, что у меня есть:

  • Игра .NET (Unity), поддерживающая моддинг (не моя игра, поэтому я не имею над ней никакого контроля)
  • Он загружает моды, которые являются сборками .NET, которые могут приносить свои зависимости (библиотеки deps DLL попадают в ту же папку, что и DLL мода).
  • Они используют разные версии библиотеки зависимостей утилит, в идеале опубликованной на NuGet.

Теперь я автор этой сторонней библиотеки утилит.

Мне бы хотелось, чтобы моды могли использовать его, не беспокоясь о конфликтах версий сборки. Например, ModOne загружает Утилиту 1.0.0, а ModTwo загружает Утилиту 2.0.0.

По умолчанию в данном случае побеждает самая последняя версия сборки (или первая загружающаяся, я не уверен) и один из модов сломается.

Мне нужен совет, как решить эту проблему, не создавая кошмара для авторов модов.

Я много искал в Интернете, но не нашел ничего четкого и определенного для моего конкретного случая использования.

Две альтернативы, которые я рассматриваю:

  1. Создайте зависимость сборки со строгим именем. Могут ли авторы модов ссылаться на него через <PackageReference>, затем использовать <Reference>, чтобы указать токен публичного ключа, а затем использовать псевдоним , чтобы принудительно использовать конкретную версию. Это вообще сработает? Я также могу пропустить NuGet и напрямую предоставить загружаемую .dll, если это поможет.
  2. Публикуйте новые версии, но добавляйте новые пространства имен по мере возникновения критических изменений, например. UtilityLib.Feature.V2. Это будет работать до тех пор, пока всегда загружается новейшая версия сборки, но я не знаю, всегда ли это так, похоже, зависит от используемой среды выполнения .NET, и я не знаю, как игра/Unity обрабатывает этот. Насколько мне известно, явное перенаправление привязки сборки невозможно, поскольку мы не контролируем конфигурацию игры.
  3. Опубликуйте зависимость как мод, но по разным причинам я бы хотел избежать этого решения.

Я ищу конкретные рекомендации по этой проблеме, поскольку на аналогичные вопросы, но для немного других случаев использования уже были даны ответы, как правило, в расплывчатой ​​форме. Не будучи очень знакомым с .NET, я в растерянности.

В заключение: возможно ли вообще сделать это в .NET детерминированным способом, не требующим тяжелых обходных путей и сложной настройки для авторов модов, или мне следует сдаться?

Редактировать

Только что попробовал подход № 1 со случайной уже существующей сборкой со строгим именем.

Вот конфигурация в одном моде:

<ItemGroup>
    <PackageReference Include = "Dapper.StrongName" Version = "1.60.1" GeneratePathProperty = "true" Aliases = "DapperV1"/>
    <Reference Include = "Dapper.StrongName, Version=1.60.0.0, Culture=neutral, PublicKeyToken=e3e8412083d25dd3">
        <ReferencePath>$(PkgDapper_StrongName)\Dapper.StrongName.dll</ReferencePath>
        <HintPath>$(PkgDapper_StrongName)\Dapper.StrongName.dll</HintPath>
    </Reference>
</ItemGroup>

Второй мод имеет то же самое с версией 2.0+. Хотя обе сборки присутствуют в выходных каталогах, и эта связь работает правильно в редакторе, во время выполнения для обоих модов загружается только версия 2.0 (регистрация typeof(typeof(DapperV*::Dapper.SqlMapper).Assembly.GetName().FullName)).

Вы ориентируетесь на .NET Framework 4.x или .NET Core (или более позднюю версию, включая .NET 5/6/7/8+)?

Dai 23.06.2024 13:14

@Dai Шаблон игрового мода заставляет нас ориентироваться на .NET 4.7.2. Я думаю, нам не стоит это менять?

Morgan Touverey Quilling 23.06.2024 13:25

.NET Framework 4.x может использовать <bindingRedirect>, .NET Core и более поздние версии имеют альтернативную стратегию. Но в любом случае вас устраивает политика «всегда использовать последнюю версию» при условии, что вы не только строго придерживаетесь SemVer для своих собственных пакетов, но и никогда не вносите никаких изменений, нарушающих ABI (например, удаление каких-либо типов или члены - но эти члены могут стать NOOP - важно не нарушить связывание при загрузке во время выполнения, выполняемое CLR)

Dai 23.06.2024 13:29

Если я не ошибаюсь, я также не могу использовать привязку Redirect, потому что это происходит, когда вы делаете это в конфигурации приложения, когда вы управляете результирующей базой кода, а не когда вы являетесь автором мода внутри проекта?

Morgan Touverey Quilling 23.06.2024 13:37

Лучшая идея: поскольку это ваш .exe, вы можете просто установить обработчик для AppDomain.AssemblyResolve и использовать его для перенаправления любых попыток загрузки старых версий вашей библиотеки в текущую последнюю версию (так что не беспокойтесь о app.config на основе <bindingRedirect> ).

Dai 23.06.2024 13:41

Ах, нет, извините, если это было двусмысленно, это не мой бывший. На самом деле это Cities: Skylines II, так что в моих силах только то, что автор мода может сделать внутри своей собственной DLL.

Morgan Touverey Quilling 23.06.2024 13:42

Тогда какой вред в том, что две разные версии сборки вашей утилиты загружаются в одни и те же процессы? Эти моды/проекты/и т. д. будут использовать вашу сборку как частную зависимость (верно?).

Dai 23.06.2024 13:44

Я действительно этого хочу (вместо того, чтобы управлять версиями моего пространства имен и надеяться, что будет загружена последняя версия). Кажется, невозможно загрузить обе сборки одновременно (см. мое последнее редактирование).

Morgan Touverey Quilling 23.06.2024 13:45

Видели это? stackoverflow.com/questions/4451220/…

Dai 23.06.2024 13:46

как супер хак, добавление версии в название сборки не сработает, т. е. AsemmblyV20, AsemmblyV16 не сработает?

Ivan Petrov 23.06.2024 13:47

@Dai: да, не помогло :/

Morgan Touverey Quilling 23.06.2024 13:48

@IvanPetrov Ну, подождите секундочку, это великолепно. Я был настолько сосредоточен на других проблемах, что не подумал о том, что имя NuGet и имя сборки — это не одно и то же. Таким образом, все моды могут использовать NuGet «UtilityLib», но использовать другое пространство имен и сборку в зависимости от используемой версии. Имеет смысл?

Morgan Touverey Quilling 23.06.2024 13:50

@MorganTouvereyQuilling ну, не уверен насчет упаковки NuGet, но вы хотите, чтобы имя сборки было другим, а также выходная dll. С этого момента то, как вы его упаковываете, не имеет значения, т. е. это не будет иметь реального эффекта, потому что мы разрешаем библиотеки DLL, и NuGet на данный момент не имеет значения.

Ivan Petrov 23.06.2024 13:52
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
13
55
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если бы вы могли заставить <bindingRedirect>, у нас могло бы быть более простое решение, но поскольку вы являетесь библиотекой, которую используют моды, вы не можете позволить себе роскошь установить это.

Единственное, что вы можете сделать, — это иметь действительно разные сборки, различающиеся не по версии, а по имени.

Другими словами, у вас есть схема именования, которая включает версию в имя сборки:

AssemblyV10
AssemblyV12 

и т. д. Таким образом, никакие конфликты невозможны, и все собрания будут решаться индивидуально.

Однако я не уверен, позволяет ли NuGet иметь разные версии пакета на основе разных имен сборок, а не только разных версий сборки.

Я собираюсь протестировать полное решение с локальным пакетом и принять ответ, если это сработает, но это кажется многообещающим, спасибо!

Morgan Touverey Quilling 23.06.2024 14:03

Все работает прекрасно, обе «версии» (UtilityV1 v1.0.0 и UtilityV2 v2.0.0) могут даже использовать одно и то же пространство имен «Утилита»! Эмулируется с помощью локального репозитория NuGet. Не уверен, что понял ваше последнее предложение, но я почти уверен, что NuGet примет что угодно, если это не уже опубликованная версия имени пакета.

Morgan Touverey Quilling 23.06.2024 15:08

@MorganTouvereyQuilling Рад, что пока это сработало. На самом деле у вас не так много других вариантов. Я не был уверен, что MS NuGet не применяет одно и то же имя сборки и не допускает только изменения атрибутов версии (это казалось маловероятным, но все же :)) - вы все равно не будете использовать их, я думаю, в качестве дистрибутива. Наконец, возможно, используйте этот подход для большинства выпусков, но не для всех, то есть какой-то гибридный подход, при котором вы не возражаете против конфликтов между определенными версиями, которые будут работать одинаково независимо от того, какая сборка загружена.

Ivan Petrov 23.06.2024 17:26

@MorganTouvereyQuilling: Если это сработает, отметьте это как ответ.

Poul Bak 23.06.2024 17:34

Извините, совсем забыл принять это. «> Наконец, возможно, используйте этот подход для большинства выпусков, но не для всех» Абсолютно, я намерен соблюдать строгий semver и менять имя сборки только в случае критических изменений в ABI. В конце концов, повторное использование уже загруженной сборки полезно для памяти, если они совместимы, просто мне нужно было плавно обрабатывать критические изменения.

Morgan Touverey Quilling 24.06.2024 02:16

Без проблем. Да, это хорошо сочеталось бы с обеспечением соблюдения семвера, как и должно быть. Ваше здоровье.

Ivan Petrov 24.06.2024 05:55

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