Обработка условных ссылок на пакеты в коде и модульных тестах - PullRequest
0 голосов
/ 24 октября 2019

У меня есть проект библиотеки, который расширяет некоторые функции на EntityFrameworkCore. Я ищу поддержку как 2.*, так и 3.*. Мой проект настроен так:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;netcoreapp3.0</TargetFrameworks>
[...]
  </PropertyGroup>
  <ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.0' ">
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0" />
  </ItemGroup>
  <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.6" />
  </ItemGroup>
[...]
  </PropertyGroup>
</Project>

В коде я использую функцию EntityTypeExtensions.FindProperty(...). Сигнатура этой функции изменяется между 2.2.6 и 3.0.0.

Код проекта (неправильно?) Использует подпись для 2.2.6. Это правильно компилируется (что не должно иметь место?) В обеих целевых инфраструктурах.

У меня есть проект модульного теста, который имеет несколько целей и имеет условные ссылки, очень похожие на исходный проект:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>netcoreapp3.0;netcoreapp2.0</TargetFrameworks>
[...]
  </PropertyGroup>
[...]
  <ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp2.0' ">
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.6" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="2.2.6" />
  </ItemGroup>
  <ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.0' ">
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.0.0" />
  </ItemGroup>
[...]
</Project>

Все модульные тесты (неправильно?) Проходят в обеих целевых инфраструктурах.

Обратите внимание, что, несмотря на то, что сборка и тесты проходят, библиотека используется в проекте netcore3 (который ссылается на efcore 3.0.0). напрямую) выдает следующее исключение. Что кажется вполне разумным, я просто не понимаю, почему это позволило мне добраться до этой точки.

System.MissingMethodException: Method not found: 'Microsoft.EntityFrameworkCore.Metadata.IProperty Microsoft.EntityFrameworkCore.EntityTypeExtensions.FindProperty(Microsoft.EntityFrameworkCore.Metadata.IEntityType, System.Reflection.PropertyInfo)'.

Вопросы:

  • Есть ликак обойти это, так что это воспринимается как ошибка / предупреждение / что-то, по крайней мере, во время сборки?
  • Есть ли решение для этого, чтобы использовать директивы препроцессора вокруг вызова к .FindProperty(...) и на основе платформысделать правильный вызов метода? Разве нет способа сделать это, основываясь на версии efcore вместо зависимости?
  • Есть ли способ провести модульное тестирование правильно с различными пакетами? В настоящий момент я ожидал, что модульные тесты не пройдут в одной из версий, поскольку метод не существует.

  • Исходный репозиторий и, в частности, вызов FindProperty может бытьнайдено здесь .
  • Пример проекта netcore3, который приводит к MissingMethodException при вызове библиотеки, можно найти здесь .
  • Трассировка стекаисключение можно найти здесь .

Ответы [ 2 ]

1 голос
/ 27 октября 2019

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

Шаги для проверки: Загрузите Panner.Order версию 1.1.0 с nuget.org (вы опубликовали 1.1.1 с тех пор, как задали эти вопросы). , которая имеет ту же, но другую, проблему). Если у вас установлен NuGet Package Explorer , откройте с ним nupkg, разверните папку lib/ и дважды щелкните каждый из файлов .dll. В качестве альтернативы вы можете извлечь nupkg в виде zip-файла, а затем использовать ILSpy или ILDasm или все, что вы хотите для проверки сборок. Обратите внимание, что обе сборки netstanard2.0 и netcoreapp3.0 имеют одинаковые ссылки на сборки. В частности, ссылка на Microsoft.EntityFrameworkCore.dll относится к версии 2.2.6.0, хотя мы ожидаем, что версия netcoreapp3.0 будет использовать версию 3.0.0.0. Поэтому я пришел к выводу, что ваша сборка netstandard2.0 была неправильно скопирована в папку netcoreapp3.0 вашего пакета. Ваш пакет 1.1.1 имеет противоположную проблему. Папки netstandard2.0 и netcoreapp3.0 содержат сборку netcoreapp3.0, поэтому ваш пакет не работает с проектами, которые пытаются использовать сборку netstandard2.0.

Однако я понятия не имею, почему этослучается. Когда я клонирую ваш репозиторий и запускаю dotnet pack и проверяю сгенерированный nupkg, я вижу, что сборки netstandard2.0 и netcoreapp3.0 имеют разные ссылки, поэтому я уверен, что сгенерированный локально пакет должен работать. Вам нужно выяснить, почему публикуемые вами пакеты не генерируются правильно.

Чтобы быстро ответить на ваши вопросы:

Есть ли способ обойти это, чтобы он воспринимался как ошибка/ предупреждение / что-то, по крайней мере, во время сборки?

Будет, так как проблема была не с проектом, а с пакетом. Если вы нацелены на несколько проектов и вызовете API, который не существует хотя бы в одном из TFM, вы получите ошибку компиляции.

Является ли решение этой проблемы использованием директив препроцессора вокругвызов .FindProperty (...) и на основе этой структуры сделать правильный вызов метода? Разве нет способа сделать это на основе версии efcore вместо зависимости?

Когда вы вызываете API, которые отличаются в разных TFM, да, вы можете использовать #if для измененияваш код для проекта TFM, как описано в документах ASP.NET Core при переходе на 3.0 .

Я собираюсь игнорировать «на основе версии efcore», потому что ячеловек, ориентированный на детали, и я не хочу писать тысячу слов для чего-то, что в конечном итоге не имеет значения. Ключ в том, что в этом сценарии вам не нужно. Вы использовали условия в ссылках вашего пакета, чтобы ввести разные версии efcore для проекта TFM, поэтому каждый раз, когда ваш проект компилируется, он использует другую версию efcore, но только одну версию для каждой цели компиляции. Поэтому вам не нужно выбирать во время выполнения различные версии efcore.

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

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

Если вы действительно хотите протестировать пакет, а не свой код, вы можете использовать файл nuget.config для добавления локальной папки в качестве источника пакета, тогда ваш тестовый проект с множественным таргетингом ссылается на пакет, а не на проект. Возможно, вы также захотите использовать файл nuget.config для установки globalPackagesFolder на то, что находится в .gitignore, потому что NuGet считает пакеты неизменными, и если отладочная версия вашего пакета попадает в папку глобальных пакетов вашего профиля пользователя,каждый проект, который вы используете на этом компьютере (который использует папку глобальных пакетов вашего профиля пользователя), будет использовать эту отладочную версию, что затруднит вам обновление. Для клиентов, которые хотят тестировать пакеты, а не проекты, я настоятельно рекомендую использовать предварительные метки SemVer2 и создавать уникальную версию пакета для каждой отдельной сборки, чтобы снизить риск тестирования версии, отличной от вашей.

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

1 голос
/ 25 октября 2019

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

В коде я использую функцию EntityTypeExtensions.FindProperty (. ..). Сигнатура этой функции изменяется между 2.2.6 и 3.0.0.

Согласно вашему описанию, я предполагаю, что вы можете использовать код, подобный EntityTypeExtensions.FindProperty(entityType, propertyInfo);, в вашем первоначальном проекте.

Для Microsoft.EntityFrameworkCore 2.2 :

FindProperty (это Microsoft.EntityFrameworkCore.Metadata.IEntityType entityType, System.Reflection.PropertyInfo propertyInfo);second parameter=>PropertyInfo

Для Microsoft.EntityFrameworkCore 3.0 :

FindProperty (это Microsoft.EntityFrameworkCore.Metadata.IEntityType entityType, System.Reflection.MemberInfo memberInfo);second parameter=>MemberInfo

Однако, пожалуйста, отметьте PropertyInfo Class , вы найдете:

Inheritance: Object->MemberInfo->PropertyInfo

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

Так что для этой проблемы вы могли бы использовать сигнатуру для 3.0.0 (MemberInfo) в коде вместо 2.2.6 (PropertyInfo), чтобы сделатьтест. Я думаю, что сборка будет падать, как вы ожидали. И, как предлагает еретик в комментарии, для многоцелевого проекта, использование #if - хороший выбор.

Надеюсь, что все вышеперечисленное поможет вам, и если я что-то неправильно пойму, пожалуйста, поправьте меня:)

...