Невозможно решить проблему со ссылкой на сборку без frameworkAssemblies

Я пытаюсь проверить, что Protocol Buffers будет работать с новыми переносимыми средами выполнения от команды ASP.NET и, в идеале, с большинством других современные среды. Сборка 3.0.0-alpha4 была создана некоторое время назад с использованием профиля 259, поэтому я ожидал, что в некоторых случаях потребуются некоторые изменения, но я решил попробовать. Мне известно о сообщении Орена Новотны о нацеливании на .NET Core, и я должен внесите некоторые изменения в файл Google.Protobuf nuspec , но ошибка, с которой я столкнулся, поставила меня в тупик.

Версия DNX: 1.0.0-rc1-update1

Сценарий, который я сейчас пытаюсь протестировать, представляет собой консольное приложение, ориентированное на dnx451. У меня есть очень простое примерное приложение:

using Google.Protobuf.WellKnownTypes;
using System;

public class Program
{
    public static void Main(string[] args)
    {
        Duration duration = new Duration { Seconds = 100, Nanos = 5555 };
        Console.WriteLine(duration);
    }
}

... и крошечный project.json:

{
  "compilationOptions": { "emitEntryPoint": true },
  "dependencies": { "Google.Protobuf": "3.0.0-alpha4" },

  "frameworks": {
    "dnx451": { }
  }
}

Обратите внимание, что здесь я даже не использую dnxcore* — по иронии судьбы я заставил это работать без проблем.

dnu restore работает нормально; dnx run не работает с:

Ошибка: c:\Users\Jon\Test\Projects\protobuf-coreclr\src\ProtobufTest\Program.cs(9,9): ошибка DNX,Version=v4.5.1 CS0012: тип «Объект» определен в сборке что не упоминается. Вы должны добавить ссылку на сборку «System.Runtime, версия = 4.0.0.0, культура = нейтральная, PublicKeyToken = b03f5f7f11d50a3a».

Следующие изменения приводят к той же ошибке:

  • Явное добавление зависимости к "System.Runtime": "4.0.0" в разделе dependencies для фреймворка
  • Явное добавление зависимости к "System.Runtime": "4.0.0-beta-23109" в разделе dependencies для фреймворка, а также для 4.0.10-beta-*, 4.0.20-beta-* и 4.0.21-beta*.
  • Добавление зависимостей к System.Runtime в пакете NuGet (локально) и перестроение с учетом этого — project.lock.json было обновлено для включения System.Runtime v4.0.0, но возникла та же ошибка.
  • То же самое, включая каталог lib\dotnet в пакет, а также зависимости

Шаги, которые действительно работают (независимо и без dependencies записей), но меня смущают:

  • Изменение вызова Console.WriteLine только на Console.WriteLine("foo") (без других изменений)
  • Изменение типа переменной duration на object вместо Duration
  • Полностью удалить все намеки на протокольные буферы и вместо этого использовать TimeSpan или аналогичный
  • Добавьте следующее в project.json в разделе dnx451:

    "frameworkAssemblies": {
      "System.Runtime": ""
    }
    

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

Я ожидаю, что если бы я мог разработать способ заставить работать запись dependencies, я мог бы затем добавить эту зависимость в протокольные буферы, что было бы хорошо, но как зависимость от System.Runtime v4.0.0 в project.lock файл не помогает, я что-то упускаю :(


person Jon Skeet    schedule 07.12.2015    source источник
comment
Я столкнулся с очень похожей ошибкой для простой библиотеки классов. Найдены два способа изменения файла project.json для решения проблемы: nodogmablog.bryanhogan.net/2016/01/   -  person Bryan    schedule 20.01.2016


Ответы (3)


Так что, если вы прищуритесь и посмотрите на project.json, это в основном nuspec с небольшим количеством goop, чтобы описать, какие параметры компиляции и исходники необходимы для сборки проекта. Nuspecs сегодня имеет 2 раздела: frameworkAssemblies для «встроенных» вещей и dependencies для других зависимостей nuget. Здесь он имеет тот же смысл. Когда вы используете что-то из «фреймворка», это необходимо указать в frameworkAssemblies, а не как зависимость пакета nuget.

Теперь о частностях:

При использовании библиотеки на основе PCL или .NET Core в .NET Framework ссылки указывают на сборки (иногда называемые контрактными сборками). Некоторыми примерами этого являются такие вещи, как System.Runtime, System.Threading и т. д. При использовании проектов на основе MSBUILD выполняется задача, которая в основном автоматически добавляет все ссылки System.* в компилятор C#, чтобы избежать этого беспорядка. Эти сборки называются фасадами в .NET Framework. К сожалению, он добавляет ВСЕ из них, даже если они не используются. Зависимость от System.Runtime является триггером для такого поведения (при работе с файлами csproj на основе .NET Framework).

Причина, по которой добавление ссылки на тот же пакет не работает, заключается в том, что в папке .NET Framework (net4*) для этих сборок контрактов (например, System.Runtime) нет библиотек DLL. Если вы посмотрите на эти папки, вы увидите пустой файл _._. Причина этого в том, что когда вы объявляете пакет nuget со ссылкой frameworkAssembly на System.Runtime, системы проекта msbuild не могут его установить (очень сложная ошибка и проблема дизайна).

Это, наверное, сделало вещи более нечеткими...

person davidfowl    schedule 07.12.2015
comment
Итак, просто чтобы уточнить, решение добавить ссылку frameworkAssembly в пакет Google.Protobuf привязано к конкретной целевой платформе? - person Jon Skeet; 07.12.2015
comment
@JonSkeet, вы можете добавить его как "frameworkAssemblies": { "System.Runtime": { "type": "build", "version": "" } }, чтобы он не попал в окончательный файл nuspec, что должно быть более чистым решением. - person Axel Heer; 07.12.2015
comment
@AxelHeer: до сих пор я использовал только "type": "build" для включения исходного кода ... что это значит для сборок фреймворка? И вы предлагаете поместить это в файл nuspec Protobuf или в project.json моего консольного приложения? - person Jon Skeet; 07.12.2015
comment
@JonSkeet добавление фасадов, таких как System.Runtime, в качестве сборок фреймворка в project.json приводит к записям в файле nuspec, если dnu pack используется для создания пакета nuget. Использование "type": "build" приводит к успешной сборке, поскольку она включается для компиляции, но пакет nuget nuspec не ссылается на нее — в любом случае фасад содержит только переадресацию типов. Без каких-либо планов по упаковке вы можете игнорировать мой комментарий... - person Axel Heer; 07.12.2015
comment
Мы собираемся исправить это. Пакетов должно быть достаточно для работы в любом сценарии. - person davidfowl; 09.12.2015
comment
Я добавил ответ, показывающий, что я считаю правильным способом справиться с этим - не могли бы вы подтвердить, что это уместно? Я бы не хотел распространять обходной путь, который как-то ухудшает ситуацию. - person Jon Skeet; 09.12.2015
comment
(Аааа, почему-то не видел ваш предыдущий комментарий.) - person Jon Skeet; 09.12.2015
comment
Запутался в этих зависимостях для других зависимостей nuget. В моем project.json для веб-приложения я вижу - зависимости: { Microsoft.AspNet.Mvc: 6.0.0-rc1-final, Microsoft.CSharp: 4.0.0, - person Bryan; 20.01.2016

Я принял ответ Дэвида Фаулера как причину, почему все это произошло. Теперь с точки зрения того, что я должен сделать с этим, похоже, мне просто нужно добавить элемент frameworkAssemblies в файл nuspec для Google.Protobuf:

<package>
  <metadata>
    ...
    <frameworkAssemblies>
      <frameworkAssembly assemblyName="System.Runtime" targetFramework="net45" />
    </frameworkAssemblies>
  </metadata>
  ...
</package>

Затем эта ссылка frameworkAssembly оказывается в project.lock.json в клиентском проекте, и все в порядке.

Однако, судя по другому комментарию Дэвида («Мы собираемся исправить это»), мне все равно ничего не нужно делать...

person Jon Skeet    schedule 09.12.2015
comment
Информация представляется интересной для разработчиков пакетов NuGet. Вероятно, было бы неплохо добавить больше тегов (nuget, nuspec и т. д.) к вашему вопросу, чтобы информация лучше индексировалась и ее можно было найти. - person Oleg; 09.12.2015
comment
@Олег: Кажется разумным. Сделанный. - person Jon Skeet; 09.12.2015

Мне кажется, что ваша проблема существует только потому, что вы выбрали Консольное приложение вместо «Веб-приложение ASP.NET»/«Шаблоны ASP.NET 5»/«Пустой». Я сделал простое тестовое использование Пустого шаблона, добавил "Google.Protobuf": "3.0.0-alpha4" из NuGet и, наконец, просто изменил Startup.cs, чтобы он использовал Google.Protobuf.WellKnownTypes:

  • добавил using Google.Protobuf.WellKnownTypes;
  • добавлено var duration = new Duration { Seconds = 100, Nanos = 5555 }; внутри Configure
  • изменено await context.Response.WriteAsync("Hallo World!"); на await context.Response.WriteAsync(duration.ToString());

Окончательный код Startup.cs:

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.Extensions.DependencyInjection;
using Google.Protobuf.WellKnownTypes;

namespace ProtobufTest
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app)
        {
            app.UseIISPlatformHandler();

            var duration = new Duration { Seconds = 100, Nanos = 5555 };

            app.Run(async context =>
            {
                await context.Response.WriteAsync(duration.ToString());
            });
        }

        // Entry point for the application.
        public static void Main(string[] args) => WebApplication.Run<Startup>(args);
    }
}

Полученное приложение ASP.NET 5 успешно отображается 100.5555s в веб-браузере.

Демонстрационный проект можно загрузить с здесь.

ОБНОВЛЕНО: я проанализировал проблему с чисто консольным DNX-приложением, которое использует код, и смог найти причину проблемы в методе duration.ToString(), который работает в среде ASP.NET , но не в чистом консольном приложении. Причина проблемы интересна, и я пытаюсь разобраться, но я хотел поделиться своими текущими результатами с другими.

Я мог бы заставить работать следующий код:

using Google.Protobuf.WellKnownTypes;
using System;

namespace ConsoleApp3
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var duration = new Duration { Seconds = 100, Nanos = 5555 };
            Console.WriteLine("{0}.{1:D4}s", duration.Seconds, duration.Nanos);
        }
    }
}

Рабочий проект можно скачать здесь. сильный>

Я дополнительно прокомментировал строку

//[assembly: Guid("b31eb124-49f7-40bd-b39f-38db8f45def3")]

в AssemblyInfo.cs, чтобы не было ненужных ссылок на "Microsoft.CSharp", у которых много других ссылок. project.json содержит в демонстрационном проекте:

{
  ...

  "dependencies": {
    "Google.Protobuf": "3.0.0-alpha4"
  },

  "frameworks": {
    "dnx451": { },
    "dnxcore50": {
      "dependencies": {
        "System.Console": "4.0.0-beta-23516"
      }
    }
  }
}

Кстати, включение "System.Console": "4.0.0-beta-23516" в "dnxcore50" часть "frameworks" требуется, потому что пространство имен Console (для Console.WriteLine) существует в mscorlib из DNX 4.5.1. Если попытаться добавить "System.Console": "4.0.0-beta-23516" на уровне общих зависимостей, то получится ошибка с начинается с текста

Ошибка CS0433 Тип «Консоль» существует как в «System.Console, версия = 4.0.0.0, культура = нейтральная, PublicKeyToken = b03f5f7f11d50a3a», так и в «mscorlib, версия = 4.0.0.0, культура = нейтральная, PublicKeyToken = b77a5c561934e089» ConsoleApp3.DNX 4.5.1

ОБНОВЛЕНО 2: можно заменить строку

Console.WriteLine("{0}.{1:D4}s", duration.Seconds, duration.Nanos);

to

Console.WriteLine((object)duration);

чтобы заставить его работать. Просто использование Console.WriteLine(duration); или var str = duration.ToString(); приводит к ошибке, которую вы описали.

ОБНОВЛЕНО 3: я убедился, что код duration.ToString() вызывает строки, которые используют строки для форматирования. Похоже, что код duration.ToString() делает то же самое, что и ((object)duration).ToString() для типов WellKnownTypes (например, Duration).

Последнее замечание, которое я считаю важным. Описанная проблема существует только для dnx451 (или dnx452 или dnx46). Если бы кто-то убрал строки

"dnx451": { },

из "frameworks" части project.json программа будет скомпилирована только для DNX Core 5.0 ("dnxcore50"). Можно легко убедиться, что у него больше не будет никаких проблем.

ОБНОВЛЕНО 4: Наконец-то я нашел очень простое решение вашей проблемы: нужно просто добавить в проект зависимость "Microsoft.AspNet.Hosting": "1.0.0-rc1-final":

{
  "dependencies": {
    "Google.Protobuf": "3.0.0-alpha4",
    "Microsoft.AspNet.Hosting": "1.0.0-rc1-final"
  }
}

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

Конечный проект без проблем компилируется как для dnx451, так и для dnxcore50. Я интерпретирую результаты следующим образом: «Google.Protobuf» работает как с dnx451, так и с dnxcore50, но автоматическое разрешение зависимостей RC1 по-прежнему содержит ошибки и не может правильно разрешить некоторые необходимые зависимости «Google.Protobuf».

Конечно, добавление непосредственно ненужной ссылки "Microsoft.AspNet.Hosting": "1.0.0-rc1-final" можно рассматривать только как обходной путь. Я думаю, что разрешение зависимостей, используемое в ASP.NET 5 и DNX, все еще содержит ошибки. Я отправил сообщение за некоторое время до проблемы, которая все еще открыта. Проблема представляет собой пример, когда разрешение прямой включенной зависимости может привести к другим результатам в виде зависимостей, разрешенных dnu restore. Именно по этой причине я начал сравнивать зависимости рабочего кода, которые я выложил вам изначально, с зависимостями нерабочего проекта. После некоторых тестов я нашел обходной путь и сократил его до единственной зависимости: "Microsoft.AspNet.Hosting": "1.0.0-rc1-final".

person Oleg    schedule 07.12.2015
comment
Но я хочу, чтобы библиотека работала как в консольных приложениях, так и в веб-приложениях. Дело в том, что он должен быть переносимым ... когда я сказал ASP.NET v5, я имел в виду, что во всех отношениях ... ASP.NET, кажется, означает больше, чем веб-приложения в наши дни ... прояснит вопрос. - person Jon Skeet; 07.12.2015
comment
Вы писали об ASP.NET v5. Хотите протестировать его с DNX Core 5.0? Если вам нужно приложение ASP.NET v5 и вы не хотите запускать его внутри консоли, вам следует изменить начало приложения. Project.json имеет "Microsoft.AspNet.Server.Kestrel" как зависимость и web как command. Это позволяет запускать приложение как собственный хостинг в консольном приложении. Нужно использовать все то же приложение ASP.NET, что и раньше. Результаты будут видны не в консоли, а в браузере (на стороне клиента). - person Oleg; 07.12.2015
comment
Я хочу протестировать его с различными средами выполнения, используя новые среды выполнения, созданные командой ASP.NET v5. dnxcore50 уже работает, поэтому его нет в моем вопросе. На данный момент меня не интересует веб-приложение — я пытаюсь заставить консольное приложение работать с целью dnx451 в соответствии с project.json. - person Jon Skeet; 07.12.2015
comment
Вы можете удалить "dnx451": { }, из "frameworks" части project.json. После этого приложение будет работать только с dnxcore50. Вы также можете убедиться, что программа работает. Таким образом, ваш код var duration = new Duration { Seconds = 100, Nanos = 5555 }; и duration.ToString() успешно работает с DNX Core 5.0. Это то, что вы хотели проверить? - person Oleg; 07.12.2015
comment
Я уже сказал (в вопросе), что это работает с dnxcore. Я хочу заставить его работать с dnx451, с консольным приложением. Дело в том, что у меня вполне могут быть пользователи, которые захотят использовать его в этой среде, поэтому мне нужно, чтобы это работало, и я хочу более подробно разобраться в разрешении зависимостей. Я добавил это к вопросу: переход на использование dnxcore или веб-приложения не помогает мне решить этот конкретный сценарий. - person Jon Skeet; 07.12.2015
comment
@JonSkeet: Вероятно, есть недопонимание, потому что вы написали об ASP.NET 5. - person Oleg; 07.12.2015
comment
@Oleg ASP.NET 5 также запускает консольные приложения :) - person khellang; 07.12.2015
comment
Поэтому я уточнил вопрос. Но да, ASP.NET 5 часто используется для обозначения всей среды... вот почему, когда вы делаете файл/новый проект, вы можете получить консольное приложение (пакет) в Интернете, и почему docs.asp.net/en/latest/dnx/console.html — это документация по консольному приложению в Документация по ASP.NET... - person Jon Skeet; 07.12.2015
comment
@JonSkeet: Извините, мне нужно уйти примерно на час. Если ваша проблема не будет решена до времени, я постараюсь вам помочь. Я еще не до конца понимаю твои цели. Я думал, что ваша основная цель - это тестирование Google.Protobuf под ASP.NET 5 и разными версиями dnx. То, что я предложил, вы действительно можете использовать для проверки и тестирования. Форма тестового приложения (использование консольного приложения) тоже интересна, но она уступает основной цели (проверка правильности работы Google.Protobuf под ASP.NET 5). Извините, но мне пора идти. До позже... - person Oleg; 07.12.2015
comment
Дело в том, что под ASP.NET 5 не обязательно подразумевается веб-приложение, учитывая, что ASP.NET 5 включает консольные приложения. Но это нормально. Я бы посоветовал вам удалить этот ответ на данный момент и, возможно, отредактировать и восстановить его позже. - person Jon Skeet; 07.12.2015
comment
@JonSkeet: я все еще думаю, что ASP.NET 5 — это только веб-приложение, а вы имеете в виду DNX. В любом случае я сейчас вернулся и могу воспроизвести проблему с консольным приложением. Я проанализирую это и отправлю вам больше информации. Я должен отметить, что я новичок в ASP.NET 5, и это не то, чем я занимаюсь все время. Тем не менее, другого ответа на ваш вопрос я не увидел и попытался вам помочь. Скоро выложу новую информацию... - person Oleg; 07.12.2015
comment
@Oleg: Возможно, вы захотите повременить с этим - я полагаю, что Дэвид Фаулер расследует и / или пишет ответ, напишите сейчас. - person Jon Skeet; 07.12.2015
comment
@JonSkeet: у меня есть некоторый прогресс в том, чтобы заставить его работать в консольном приложении. Проблема кажется в duration.ToString(). Например, я могу отобразить Console.WriteLine(duration.Seconds);. Если хотите, я могу опубликовать более подробную информацию. - person Oleg; 07.12.2015
comment
@ДжонСкит. Я разместил часть UPDATED в своем ответе с дополнительными подробностями. - person Oleg; 07.12.2015
comment
@JonSkeet: Я нашел очень простой обходной путь: нужно просто добавить "Microsoft.AspNet.Hosting": "1.0.0-rc1-final" в зависимости проекта дополнительно к "Google.Protobuf": "3.0.0-alpha4" (даже для консольного приложения). См. ОБНОВЛЕНО 4 часть моего ответа и демонстрационный проект который очень прост и может быть успешно скомпилирован. - person Oleg; 08.12.2015
comment
@Oleg: Спасибо, хотя я подозреваю, что на самом деле это просто извлечение System.Runtime из другого места ... И я бы не хотел навязывать эту зависимость клиентам, если на самом деле нет другого варианта. - person Jon Skeet; 08.12.2015
comment
@JonSkeet: Добро пожаловать! Я написал в последней части своего ответа, что добавление "Microsoft.AspNet.Hosting": "1.0.0-rc1-final" является единственным обходным путем, что показывает мне, что причина проблемы, которую вы описали, не в коде Google.Protobuf , но в модуле DNX, который автоматически разрешает зависимость. Автоматическое разрешение зависимостей Google.Protobuf работает некорректно. Вы должны опубликовать новую проблему в dnx на GitHub. - person Oleg; 08.12.2015
comment
@Oleg: Верно (извините, только на мобиле понял). Изучу, как выглядит файл project.json. Судя по ответу Дэвида, похоже, что проблема достаточно хорошо понятна, но ее нелегко решить. Придется исследовать несколько других путей. - person Jon Skeet; 08.12.2015
comment
@JonSkeet: Пожалуйста, сообщите мне об окончательном решении. В любом случае DNX не может автоматически разрешать все зависимости Google.Protobuf, но явное включение некоторых зависимостей решает проблему. Вы лучше знаете Google.Protobuf и знаете его зависимости. В любом случае, я повторяю, что если добавление Microsoft.AspNet.Hosting: 1.0.0-rc1-final в качестве прямой зависимости решает проблему, то либо вам нужно добавить некоторые дополнительные метаданные в Google.Protobuf, либо DNX должен исправить ошибку. Двоичные файлы Google.Protobuf работают корректно, нужно просто внедрить необходимые зависимые dll. - person Oleg; 08.12.2015
comment
Только что попробовал ваш обходной путь и, глядя на project.lock.json, он в основном работает, потому что добавляет "frameworkAssemblies": "System.Runtime" в файл. Так что мне, вероятно, просто нужно решить, как изменить файл nuspec для protobuf, чтобы сделать то же самое... - person Jon Skeet; 08.12.2015
comment
@JonSkeet: Ты прав! Изменение "frameworks" части project.json на "frameworks": { "dnx451": { "frameworkAssemblies": { "System.Runtime": "4.0.10.0" } }, "dnxcore50": { "dependencies": { "System.Console": "4.0.0-beta-23516" } } } является лучшим обходным решением. Было бы хорошо, если бы можно было поместить информацию в файл nuspec. - person Oleg; 08.12.2015