Подтвердить что ты не робот

Как разрешить адреналин NuGet

Я создаю библиотеку с некоторым функционалом с именем CompanyName.SDK, который должен быть интегрирован в проект компании CompanyName.SomeSolution

CompanyName.SDK.dll должен быть развернут через пакет NuGet. Пакет CompanyName.SDK имеет зависимость от сторонних пакетов NuGet. Например, допустим Unity. Текущая зависимость находится на v3.5.1405-prerelease Unity.

CompanyName.SomeSolution.Project1 зависит от Unity v2.1.505.2. CompanyName.SomeSolution.Project2 зависит от Unity v3.0.1304.1.

Интеграция CompanyName.SDK в это решение добавляет зависимость от Unity v3.5.1405-prerelease. Пусть возьмем, что CompanyName.SomeSolution имеет один runnable выходной проект CompanyName.SomeSolution.Application, который зависит от двух выше и от CompanyName.SDK

И здесь начинаются проблемы. Все сборки Unity имеют одинаковые имена во всех пакетах без спецификатора спецификации. И в целевой папке это будет только одна версия Unity сборок: v3.5.1405-prerelease через bindingRedirect в app.config.

Как код в Project1, Project2 и SDK использует точно необходимые версии зависимых пакетов, которые они были закодированы, скомпилированы и протестированы с помощью <

ПРИМЕЧАНИЕ 1: Unity - просто пример, реальная ситуация в 10 раз хуже с модулями 3rdparty, зависящими от других модулей 3rdparty, которые в свою очередь имеют 3-4 версии одновременно.

ПРИМЕЧАНИЕ 2. Я не могу обновить все пакеты до их последних версий, потому что есть пакеты, у которых есть зависимость не последней версии других пакетов.

ПРИМЕЧАНИЕ 3. Предположим, что у зависимых пакетов есть изменения в изменении между версиями. Это реальная проблема, почему я задаю этот вопрос.

ПРИМЕЧАНИЕ 4: Я знаю о вопросе о конфликтах между разными версиями одной и той же зависимой сборки, но ответы там не решают корень проблемы - они просто скрывают ее.

ПРИМЕЧАНИЕ 5. Где, черт возьми, это обещанное решение "DLL Hell"? Он просто появляется с другой позиции.

ПРИМЕЧАНИЕ 6. Если вы считаете, что использование GAC каким-то образом является вариантом, напишите пошаговое руководство или дайте мне ссылку.

4b9b3361

Ответ 1

Пакет

Unity не является хорошим примером, потому что вы должны использовать его только в одном месте под названием Root of Composition. И Composition Root должен быть как можно ближе к точке входа приложения. В вашем примере это CompanyName.SomeSolution.Application

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

  • более удобный код
  • лучшая тестируемость
  • избавиться от нежелательной зависимости (каждому клиенту CompanyName.SDK действительно нужна зависимость Unity?)

Итак, возьмем для примера мнимую библиотеку .NET Logging:

CompanyName.SDK.dll зависит от .NET Logging 3.0
CompanyName.SomeSolution.Project1 зависит от .NET Logging 2.0
CompanyName.SomeSolution.Project2 зависит от .NET Logging 1.0

Существуют разрывы между версиями .NET Logging.

Вы можете создать свою собственную стороннюю зависимость, введя интерфейс ILogger:

public interface ILogger
{
    void LogWarning();
    void LogError();
    void LogInfo();
} 

CompanyName.SomeSolution.Project1 и CompanyName.SomeSolution.Project2 должен использовать интерфейс ILogger. Они зависят от сторонней зависимости ILogger интерфейса. Теперь вы храните эту библиотеку .NET Logging за одним местом и легко выполняете обновление, потому что вам нужно сделать это в одном месте. Кроме того, нарушение изменений между версиями больше не является проблемой, поскольку используется одна версия библиотеки .NET Logging.

Фактическая реализация интерфейса ILogger должна быть в другой сборке, и это должно быть только место, где вы ссылаетесь на библиотеку .NET Logging. В CompanyName.SomeSolution.Application, где вы создаете свое приложение, вы должны теперь привязать абстракцию ILogger к конкретной реализации.

Мы используем этот подход, и мы также используем NuGet для распространения наших абстракций и наших реализаций. К сожалению, проблемы с версиями могут появляться вместе с вашими собственными пакетами. Чтобы избежать этих проблем, примените Semantic Versioning в пакетах, которые вы развертываете через NuGet для вашей компании. Если что-то изменится в вашей базе кода, которая распространяется через NuGet, вы должны изменить во всех пакетах, которые распространяются через NuGet. Например, у нас есть локальный сервер NuGet:

  • DomainModel
  • Services.Implementation.SomeFancyMessagingLibrary (который ссылается на DomainModel и SomeFancyMessagingLibrary)
  • и многое другое...

Версия между этими пакетами синхронизируется, если версия изменена в DomainModel, та же версия находится в Services.Implementation.SomeFancyMessagingLibrary. Если нашим приложениям требуется обновление наших внутренних пакетов, все зависимости обновляются до одной и той же версии.

Ответ 2

Вы можете работать на уровне сборки после компиляции, чтобы решить эту проблему с...

Вариант 1

Вы можете попробовать объединить сборки с помощью ILMerge

.

ilmerge/target:winexe/out:SelfContainedProgram.exe Program.exe ClassLibrary1.dll ClassLibrary2.dll

Результатом будет сборка, представляющая собой сумму вашего проекта и его необходимых зависимостей. Это имеет некоторые недостатки, такие как потеря поддержки моно и потеря идентификаторов сборки (имя, версия, культура и т.д.), Поэтому лучше всего, когда все сборки, которые нужно объединить, созданы вами.

Итак, вот...

Вариант 2

Вместо этого вы можете встраивать зависимости как ресурсы в свои проекты, как описано в этой статье. Вот соответствующая часть:

Во время выполнения CLR не сможет найти зависимую DLL сборки, что является проблемой. Чтобы это исправить, когда ваше приложение инициализирует, зарегистрирует метод обратного вызова в AppDomains Событие ResolveAssembly. Код должен выглядеть примерно так:

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {

   String resourceName = "AssemblyLoadingAndReflection." +

      new AssemblyName(args.Name).Name + ".dll";

   using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) {

      Byte[] assemblyData = new Byte[stream.Length];

      stream.Read(assemblyData, 0, assemblyData.Length);

      return Assembly.Load(assemblyData);

   }

};

Теперь первый раз поток вызывает метод, который ссылается на тип в зависимый файл DLL, событие AssemblyResolve будет вызвано и Код обратного вызова, показанный выше, найдет нужный встроенный ресурс DLL и загрузить его, вызвав перегрузку метода Load Assemblys, который принимает в качестве аргумента байт [].

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

Обновление

Посмотрите здесь. Вы также можете попробовать использовать эти теги <probing> в каждом проекте app.config, чтобы определить пользовательскую подпапку для поиска при поиске сборок в CLR.