Представьте, что у меня есть:
Теперь я автор этой сторонней библиотеки утилит.
Мне бы хотелось, чтобы моды могли использовать его, не беспокоясь о конфликтах версий сборки. Например, ModOne загружает Утилиту 1.0.0, а ModTwo загружает Утилиту 2.0.0.
По умолчанию в данном случае побеждает самая последняя версия сборки (или первая загружающаяся, я не уверен) и один из модов сломается.
Мне нужен совет, как решить эту проблему, не создавая кошмара для авторов модов.
Я много искал в Интернете, но не нашел ничего четкого и определенного для моего конкретного случая использования.
Две альтернативы, которые я рассматриваю:
<PackageReference>
, затем использовать <Reference>
, чтобы указать токен публичного ключа, а затем использовать псевдоним , чтобы принудительно использовать конкретную версию. Это вообще сработает? Я также могу пропустить NuGet и напрямую предоставить загружаемую .dll, если это поможет.UtilityLib.Feature.V2
. Это будет работать до тех пор, пока всегда загружается новейшая версия сборки, но я не знаю, всегда ли это так, похоже, зависит от используемой среды выполнения .NET, и я не знаю, как игра/Unity обрабатывает этот. Насколько мне известно, явное перенаправление привязки сборки невозможно, поскольку мы не контролируем конфигурацию игры.Я ищу конкретные рекомендации по этой проблеме, поскольку на аналогичные вопросы, но для немного других случаев использования уже были даны ответы, как правило, в расплывчатой форме. Не будучи очень знакомым с .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)
).
@Dai Шаблон игрового мода заставляет нас ориентироваться на .NET 4.7.2. Я думаю, нам не стоит это менять?
.NET Framework 4.x может использовать <bindingRedirect>
, .NET Core и более поздние версии имеют альтернативную стратегию. Но в любом случае вас устраивает политика «всегда использовать последнюю версию» при условии, что вы не только строго придерживаетесь SemVer для своих собственных пакетов, но и никогда не вносите никаких изменений, нарушающих ABI (например, удаление каких-либо типов или члены - но эти члены могут стать NOOP - важно не нарушить связывание при загрузке во время выполнения, выполняемое CLR)
Если я не ошибаюсь, я также не могу использовать привязку Redirect, потому что это происходит, когда вы делаете это в конфигурации приложения, когда вы управляете результирующей базой кода, а не когда вы являетесь автором мода внутри проекта?
Лучшая идея: поскольку это ваш .exe
, вы можете просто установить обработчик для AppDomain.AssemblyResolve и использовать его для перенаправления любых попыток загрузки старых версий вашей библиотеки в текущую последнюю версию (так что не беспокойтесь о app.config
на основе <bindingRedirect>
).
Ах, нет, извините, если это было двусмысленно, это не мой бывший. На самом деле это Cities: Skylines II, так что в моих силах только то, что автор мода может сделать внутри своей собственной DLL.
Тогда какой вред в том, что две разные версии сборки вашей утилиты загружаются в одни и те же процессы? Эти моды/проекты/и т. д. будут использовать вашу сборку как частную зависимость (верно?).
Я действительно этого хочу (вместо того, чтобы управлять версиями моего пространства имен и надеяться, что будет загружена последняя версия). Кажется, невозможно загрузить обе сборки одновременно (см. мое последнее редактирование).
Видели это? stackoverflow.com/questions/4451220/…
как супер хак, добавление версии в название сборки не сработает, т. е. AsemmblyV20, AsemmblyV16 не сработает?
@Dai: да, не помогло :/
@IvanPetrov Ну, подождите секундочку, это великолепно. Я был настолько сосредоточен на других проблемах, что не подумал о том, что имя NuGet и имя сборки — это не одно и то же. Таким образом, все моды могут использовать NuGet «UtilityLib», но использовать другое пространство имен и сборку в зависимости от используемой версии. Имеет смысл?
@MorganTouvereyQuilling ну, не уверен насчет упаковки NuGet, но вы хотите, чтобы имя сборки было другим, а также выходная dll. С этого момента то, как вы его упаковываете, не имеет значения, т. е. это не будет иметь реального эффекта, потому что мы разрешаем библиотеки DLL, и NuGet на данный момент не имеет значения.
Если бы вы могли заставить <bindingRedirect>
, у нас могло бы быть более простое решение, но поскольку вы являетесь библиотекой, которую используют моды, вы не можете позволить себе роскошь установить это.
Единственное, что вы можете сделать, — это иметь действительно разные сборки, различающиеся не по версии, а по имени.
Другими словами, у вас есть схема именования, которая включает версию в имя сборки:
AssemblyV10
AssemblyV12
и т. д. Таким образом, никакие конфликты невозможны, и все собрания будут решаться индивидуально.
Однако я не уверен, позволяет ли NuGet иметь разные версии пакета на основе разных имен сборок, а не только разных версий сборки.
Я собираюсь протестировать полное решение с локальным пакетом и принять ответ, если это сработает, но это кажется многообещающим, спасибо!
Все работает прекрасно, обе «версии» (UtilityV1 v1.0.0 и UtilityV2 v2.0.0) могут даже использовать одно и то же пространство имен «Утилита»! Эмулируется с помощью локального репозитория NuGet. Не уверен, что понял ваше последнее предложение, но я почти уверен, что NuGet примет что угодно, если это не уже опубликованная версия имени пакета.
@MorganTouvereyQuilling Рад, что пока это сработало. На самом деле у вас не так много других вариантов. Я не был уверен, что MS NuGet не применяет одно и то же имя сборки и не допускает только изменения атрибутов версии (это казалось маловероятным, но все же :)) - вы все равно не будете использовать их, я думаю, в качестве дистрибутива. Наконец, возможно, используйте этот подход для большинства выпусков, но не для всех, то есть какой-то гибридный подход, при котором вы не возражаете против конфликтов между определенными версиями, которые будут работать одинаково независимо от того, какая сборка загружена.
@MorganTouvereyQuilling: Если это сработает, отметьте это как ответ.
Извините, совсем забыл принять это. «> Наконец, возможно, используйте этот подход для большинства выпусков, но не для всех» Абсолютно, я намерен соблюдать строгий semver и менять имя сборки только в случае критических изменений в ABI. В конце концов, повторное использование уже загруженной сборки полезно для памяти, если они совместимы, просто мне нужно было плавно обрабатывать критические изменения.
Без проблем. Да, это хорошо сочеталось бы с обеспечением соблюдения семвера, как и должно быть. Ваше здоровье.
Вы ориентируетесь на .NET Framework 4.x или .NET Core (или более позднюю версию, включая .NET 5/6/7/8+)?