Русский
Русский
English
Статистика
Реклама

Net 5

Поддержка процессоров Apple M1 в .NET

21.11.2020 22:12:40 | Автор: admin

17 ноябряAppleофициально представила устройства на базе своего нового ARM-процессораAppleM1. Естественно, это событие не могло быть не замечено со стороны компанииMicrosoft, которая с 2014 года начала активную экспансию .NET на новые платформы. Давайте посмотрим, что нас ждет в связи с этим в ближайшее время.

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

Spoiler

Да, на новых маках будет .NET

Visual Studio Code

Команда разработчиков Visual Studio Code уже объявила о том, что работает над поддержкой новых процессоров. На странице загрузок Insider Preview для macOS уже появились опция для загрузки экспериментальной сборки с поддержкой ARM. Следить за работой команды можно на официальном аккаунте в GitHub.

Visual Studio for Mac

Если команда VS Code уже подготовила тестовые сборки с поддержкой Apple M1, то их коллеги из команды Visual Studio for Mac оказались не так расторопны:

Впрочем Visual Studio for Mac гораздо более крупный и сложный проект, поэтому портирование его на новый процессор может занять несколько больше времени. Сейчас эта версия IDE может работать при поддержке Rosetta 2.

На данный момент у владельцев новых ноутбуков Apple наблюдаются некоторые проблемы при отладке проектов на Xamarin.Forms для iOS. Соответствующий баг уже заведен в репозитории проекта Xamarin.iOS & Xamarin.Mac.

Rider

В JetBrains уже объявили, что они работают над переносом JetBrains Runtime (и всех продуктов, работающих на JVM, в том числе и Rider) на Apple Silicon. На данный момент IDE от JetBrains работают на чипах Apple Silicon через Rosetta 2. Правда не все функции работают в этом режиме стабильно. Так, например, многие жалуются на то, что отладка в Rider сейчас не работает.

Docker

Docker стал практически must have инструментом для современного разработчика. У Майкрософт есть обширный набор образов для .NET, но к сожалению, воспользоваться вы ими на ноутбуке с новым процессором от Apple пока не сможете.

Будем надеяться, что в ближайшее время поддержка M1 будет реализована в Docker.

.NET

А теперь перейдем к самому главному получит ли новый чип поддержку .NET?

Те, кто подсмотрели спойлер в начале статьи уже знают ответ на этот вопрос. Команда разработки .NET активно работает над поддержкой Apple M1. Для этого даже был создан отдельный проект в трекинге платформы. Стоит отметить тот факт, что текущая версия платформы (а именно, недавно ушедший в релиз .NET 5) будет работать поверх Rosetta. А вот в .NET 6 уже будет нативная поддержка нового чипа. Согласно планам Microsoft, произойдет это не раньше, чем через год:

Из того, что уже выполнено, я бы отдельно отметил такие задачи:

Также запланирована поддержка нового процессора в ASP.NET Core.

Но несмотря на то, что официальной поддержки новых процессоров придется ждать почти год, уже доступна к загрузке альфа-версия .NET 6.0. На момент написания статьи, это версия 6.0.0-alpha.1.0562.6.

Mono

Проект Mono, который обычно был догоняющим (так как команде приходилось реализовывать те возможности, которые уже были в .NET) на этот раз оказался слегка впереди. Пулреквест с поддержка нового процессора сделала сама Apple, которая ранее обещала помощь в поддержке M1 для проектов с открытым исходным кодом.

Проекты, которые вскоре должны получить поддержку Apple M1Проекты, которые вскоре должны получить поддержку Apple M1

Самое большое изменение, которое было сделано для поддержки процессора M1 связанно с тем, как работает JIT, а именно, с изменение состояния потоков. Это было реализовано с помощью новых макросов в mono/mini.h. Они были встроены в систему из соображений производительности.

Rosetta 2

В этой публикации не один раз упоминалась технология Rosetta 2. Для тех, кто не знает, что это, приведем пояснение, которое размещено на странице портала Apple Developer:

Rosetta - это процесс трансляции, который позволяет пользователям запускать приложения, содержащие инструкции x86_64, на микросхеме Apple. Rosetta призвана упростить переход на микросхему Apple, давая разработчикам время на создание универсального двоичного кода приложений. Если исполняемый файл содержит только инструкции Intel, macOS автоматически запускает Rosetta и начинает процесс трансляции. По окончании трансляции система запускает подготовленный исполняемый файл вместо оригинала. Однако процесс перевода требует времени, поэтому транслированные приложения иногда запускаются или работают медленнее.

Итоги

Новый процессор (а соотвественно устройства, которые будут на основаны на нем) без сомнений получит нативную поддержку в .NET, впрочем эта задача не является приоритетной в текущем роадмапе, поэтому ждать ее придется не раньше, чем в релиз уйдет шестая версия платформы. До того момента можно будет работать c .NET, используя возможности Rosetta 2. Что касается инструментария для разработчиков, то я могу предположить, что в ближайшие пол года основные проблемы будут решены (возможно даже с участием Apple) и уже к апрелю можно будет потихоньку присматриваться к компьютерам на базе Apple M1 в качестве рабочего инструмента.

Подробнее..

Вышел .NET 5. И что?

04.12.2020 10:17:32 | Автор: admin

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


image


Перфоманс


Изменения в перфомансе это самая заметная часть релиза. О них много писали на сайте Microsoft и рассказывали на .NET Conf. Давайте посмотрим внимательнее и определимся, где, насколько и за счет чего выросла производительность.


image


Измерения перфоманса с этого скрина основаны на бенчмарках от TechEmpower. Здесь Microsoft сравнивают результаты замеров производительности .NET 3.1 с ожидаемыми значениями для .NET 5. Стоит заметить, что это именно ожидаемые значения реальных результатов от TechEmpower пока не было. Однако уже эти результаты выглядят впечатляюще ускорение на 38 процентов для Plaintext, на 42 для Json и на 20 процентов в категории Fortunes (пояснения по разным типам бенчмарков вы можете найти в вики TechEmpower).


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


Также замеры производительности делал автор библиотеки Fusion Александр Якунин. В двух статьях на Medium он сначала измерил, насколько ускорилась работа демок его библиотеки (от 20 до 105 процентов), а затем проверил пятый .NET на нескольких синтетических бенчмарках и также получил некоторое ускорение.


За счет чего это происходит? В первую очередь, благодаря изменениям в GC, которые привели к уменьшению количества аллокаций и ускорению сборки мусора. Кстати, все эти изменения осуществляются одновременно с переписыванием GC на C#, а значит его код становится куда доступнее, понятнее и безопаснее. Подробности можно почитать в статье от Microsoft.


Другие изменения коснулись JIT компиляции, асинхронности и многих методов базовой библиотеки. Это касается методов, относящихся к работе со строками, регулярными выражениями и, что особенно важно, с коллекциями и сериализацией. Во все той же статье приводится много примеров таких изменений, с соответствующими пулл-реквестами и бенчмарками.


Изменения в JSON сериализации выглядят особенно впечатляюще. Только посмотрите на результаты бенчмарка по сериализации больших массивов!


private MemoryStream _stream = new MemoryStream();private DateTime[] _array = Enumerable.Range(0, 1000).Select(_ => DateTime.UtcNow).ToArray();[Benchmark]public Task LargeArray(){    _stream.Position = 0;    return JsonSerializer.SerializeAsync(_stream, _array);}

image


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

Языковое


Вместе с .NET 5 вышла и девятая версия C#. Официальную страничку с изменениями вы можете найти на сайте Microsoft, но давайте попробуем разобраться что именно здесь стоит внимания?


Самое заметное изменение это record-типы, позволяющие избавиться от бойлерплейта при написании DTO. Записи это иммутабельные ссылочные типы с простым и коротким объявлением. В них по умолчанию определены методы Equals, HashCode, Copy, Clone, PrintMembers и ToString разумеется, все они используют значения полей типа для выполнения операций. То есть Equals корректно сравнивает две записи по значениям, а не по ссылке.


Также записи поддерживают синтаксис копирования с изменением значений полей через with, например:


var person = new Person { FirstName = "Mads", LastName = "Nielsen" };var otherPerson = person with { LastName = "Torgersen" };

Больше о применении записей вы можете почитать в блоге Владимира Хорикова и в публикации от Konrad Kokosa.


Другое важное изменение это обновленный pattern matching. Теперь паттерны могут настраиваться по типам и операторам сравнения, причем несколько разных сравнений вы можете объединять через логические операторы. Например:


DeliveryTruck t when t.GrossWeightClass switch {    < 3000 => 10.00m - 2.00m,    >= 3000 and <= 5000 => 10.00m,    > 5000 => 10.00m + 5.00m}

Остальные изменения не столь заметны они касаются верхнеуровневых программ (возможность писать короткие программы без базового класса и функции main), упрощенного синтаксиса для new (без указания типа), init-only сеттеров и прочего.


Последние изменения в новом C#, которые хотелось бы упомянуть это расширенная поддержка для source generators. Этот функционал позволяет генерировать код при компиляции проекта и зачастую очень удобен для авторов библиотек. В новом релизе работу с генераторами кода сделали чуть проще, расширив функционал partial классов (с них сняли некоторые из старых ограничений) и добавив инициализаторы модулей (методы, которые вызываются до первого доступа к любому полю/методу модуля).
Хороший пример работы с генераторами кода вы можете найти в этой статье на Medium.


Отдельно стоит рассказать про F# 5. В новой версии языка добавилась масса классных возможностей для скриптов (например, директива для подключения nuget пакетов), slicing для более удобной работы с данными, а также улучшилась работа с quotation expressions и computation expressions. А еще сильно улучшился интероп с C# и перфоманс. Словом, новый релиз выглядит интересно и делает F# потенциально полезнее в продакшене. Полное описание всех изменений можно найти на сайте Microsoft.


Коротко язык оброс разнообразным сахаром. Самое заметное изменение в C# 9 это записи, но едва ли оно само по себе стоит обновления. С другой стороны, если вы будете обновлять версию дотнета по каким-то другим причинам, то у вас появится новый функционал, делающий язык чуть более мощным. Мелочь, а приятно.

Прочее


Помимо глобальных улучшений платформы Microsoft также неплохо поработали над отдельными библиотеками.


Например, в новой версии дотнета сильно улучшилась поддержка десктопа. WinForms и WPF наконец-то получили все преимущества .NET Core классный перфоманс, упаковка всего приложения в один .exe, работа без установленного на машине дотнета и и нет, не кроссплатформенность. Все это работает только под Windows.


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


С кроссплатформенностью все осталось по-прежнему. Релиз MAUI перенесли на .NET 6, а других решений для кроссплатформенного десктопа Microsoft пока не предлагает. Так что продолжаем использользовать Авалонию (или можно обратить свое внимание на Uno Platform перспективно выглядящий фреймворк для разработки на все и сразу).


Еще больше улучшений появилось для фулстек веб-разработки с использованием Blazor. Главное из них это улучшения производительности Microsoft обещают, что WebAssembly версия Blazor ускорится аж в три раза. Одной из основных причин такого роста производительности стал пререндер на стороне сервера. А еще добавили ленивую подгрузку зависимостей, изоляцию CSS/JS в рамках файла, новые контролы и многое другое. В общем, если вы собирались попробовать Blazor, но все никак не доходили руки сейчас он выглядит куда более production-ready технологией.


image


Из оставшихся изменений заметнее всего смотрятся те, которые призваны помочь микросервисам и работе с облачными сервисами в целом. Здесь и уменьшение размеров Docker-контейнеров, и улучшенная поддержка gRPC, и поддержка OpenAPI по умолчанию, и многое другое. Кстати, Azure полностью поддерживает .NET 5 со дня релиза.


А еще Microsoft выкатили Project Tye, который как раз является инструментом для удобного управления микросервисами. На текущий момент трудно сказать, стоит ли использовать Tye в продакшене, но обратить внимание и поиграться с ним в свободное время определенно нужно.


Коротко Microsoft улучшили поддержку отдельных инструментов и добавили приятных фич для облака. Если вы используете WinForms, WPF или Blazor обновиться определенно стоит.

Миграция


Давайте разберемся, насколько сложно будет мигрировать ваше приложение с .NET Core 3 на .NET 5?


Гайд по миграции от Microsoft говорит нам, что основные сложности в миграции коснутся приложений на Blazor, там произошло много ломающих изменений. Для большинства же остальных приложений понадобится просто обновить версию дотнета в application.json, файл проекта и версии зависимостей. А еще адрес докер-контейнера с .NET 5 немного отличается от соответствующего адреса для .NET 3.1:


docker pull mcr.microsoft.com/dotnet/core/aspnet:3.1docker pull mcr.microsoft.com/dotnet/aspnet:5.0

Другие отзывы о миграции, которые я смог найти например, вот этот гайд от Alexandre Malavasi и твит о миграции Fusion от Александра Якунина тоже говорят, что миграция проходит без каких-то специфических трудностей. Я также не столкнулся с особыми проблемами, пока мигрировал свои проекты и демки на новый дотнет. В любом случае, перед миграцией на новую версию советую внимательно изучить список ломающих изменений. А еще стоит посмотреть доклад про миграцию с .NET Conf.


Итого


Подведем итоги. Стоит ли мигрировать на новый дотнет и ради чего?


Кажется, самое важное, ради чего стоит заморачиваться это перфоманс. Ускорили практически все и достаточно заметно. Как на низком уровне через улучшения в GC и JIT так и на уровне отдельных частей фреймворка. Так что, если вы хотите выиграть в производительности, стоит как минимум попробовать обновить версию фреймворка и замерить, насколько ускорились основные сценарии.


Другие важные причины для миграции это улучшения для Blazor и WPF/WinForms. Если вы используете любой из этих фреймворков, стоит попробовать перейти на .NET 5, все же изменения достаточно заметные и полезные. Однако стоит учитывать, что для Blazor миграция выйдет достаточно непростой.


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


И на этом все! Удачной миграции на новый дотнет. Оставайтесь на связи, мы вернемся с более подробными материалами про современные возможности дотнета.

Подробнее..

Что нового в Windows Forms runtime в .NET 5.0

17.12.2020 10:10:32 | Автор: admin
С тех пор как Windows Forms был Open Soursed в конце 2018 года и в целом интерфейс был перенесен на .NET Core, и команда, и наши внешние участники были заняты исправлением старых ошибок и добавлением новых функций. В этом посте мы поговорим о новых возможностях среды выполнения Windows Forms в .NET 5.0. Заглядывайте под кат!



Улучшения и дополнения элементов управления Windows


Пожалуй, самое крутое в Windows Forms сегодня это живое и заинтересованное сообщество на GitHub. Многие из новых функций и улучшений были либо предложены, либо даже полностью реализованы членами нашего сообщества. В рамках разработки .NET 5.0 мы приняли и смерджили более 900 запросов, причем более чем 70% этих запросов исходило от нашего сообщества. Огромный привет всем его участникам, которые помогли нам улучшить Windows Forms runtime.

Вот несколько примеров того, что мы получили от сообщества.

Новый элемент управления TaskDialog




Task dialog это диалоговое окно, которое может использоваться для отображения информации и получения простого ввода от пользователя, но имеет больше функций, чем окно сообщения. Как и окно сообщения, оно форматируется операционной системой в соответствии с заданными вами параметрами.

Улучшения ListView


Элемент управления ListView хорошо знаком разработчикам Windows Forms, однако в нем отсутствовал API для легкого доступа к некоторым функциям, добавленным в Windows Vista, таким как сворачиваемые группы, групповые задачи, субтитры и нижние колонтитулы.

В .NET 5.0 мы преодолели разрыв API, и теперь Windows Forms ListView намного ближе к паритету с нативным элементом управления Win32.

Новый API включает: * свертывание/развертывание группы ListView * нижние колонтитулы группы ListView * субтитры групп ListView * задачи группы ListView * изображение заголовка группы ListView



Улучшения FileDialog


FileDialog получил новый API: FileDialog.ClientGuid. Диалоговое окно файла Windows позволяет вызывающему приложению связать идентификатор GUID с постоянным состоянием диалогового окна. Состояние диалогового окна может включать такие факторы, как последняя посещенная папка, а также положение и размер диалогового окна. Обычно это состояние сохраняется в зависимости от имени исполняемого файла. При указании GUID приложение может иметь разные сохраняемые состояния для разных версий диалогового окна в одном приложении (например, диалогового окна импорта и открытого диалогового окна).

Улучшения производительности


Windows Forms всегда была известна как управляемая оболочка для Win32 API. Таким образом, Windows Forms всегда сильно зависела от уровня взаимодействия с неуправляемыми компонентами Windows. Главным приоритетом с первых дней существования .NET Core была оптимизация нашего уровня взаимодействия, создание непреобразуемых структур, явный выбор более эффективных W-функций и использование unsafe кода там, где это возможно. Все эти изменения это то, что мы называем изменениями арахисового масла, в том смысле, что каждое из них является крошечным и вряд ли заметным, но за время жизни приложения эти изменения в сумме приводят к существенному увеличению производительности.

В .NET 5.0 мы подняли планку выше и оптимизировали несколько путей. Исторически Windows Forms полагалась на GDI+ (и некоторые GDI) для операций отрисовки. Хотя GDI+ проще в использовании, чем GDI, поскольку он абстрагирует контекст устройства (структуру с информацией о конкретном устройстве отображения, таком как монитор или принтер) через объект Graphics, он также работает медленно из-за дополнительных накладных расходов. В ряде ситуаций, когда мы имеем дело со сплошными цветами и кистями, мы решили использовать GDI.

Мы также расширили ряд API-интерфейсов, связанных с рендерингом (например, PaintEventArgs), с помощью интерфейса IDeviceContext, который, хотя и может быть недоступен напрямую разработчикам Windows Forms, позволяет нам обойти объект GDI+ Graphics и, таким образом, уменьшить выделение и увеличить скорость. Эти оптимизации показали значительное сокращение потребления памяти в путях перерисовки, в некоторых случаях экономия x10 при распределении памяти.



Для более подробного технического погружения вы можете посмотреть сегмент обзора API или сегмент .NET Community Standup, где Джереми Кун рассказывает об этих оптимизациях.

Вы можете взять тестовый проект здесь и самостоятельно проверить результаты, как и один из наших пользователей Джереми Синклер:



И последнее, но не менее важное: мы расширили API TextRenderer, чтобы принимать перегрузки ReadOnlySpan char, потому что рисование и измерение текста довольно распространенное действие. Это позволит значительно более эффективно отрисовывать текст, когда можно избежать выделения новых строк (нарезка другого ввода, построение массива символов на основе стека и т.д.).

Улучшения и исправления специальных возможностей


В течение последних нескольких лет команда обновляла Windows Forms SDK 20-летней давности, чтобы он соответствовал сегодняшним требованиям доступности и соответствию.

В .NET 5.0 мы внесли много улучшений, включая, помимо прочего, следующие:




Мы также исправили несколько проблем, которые влияли на UX при использовании некоторых инструментов доступности. Например, мы переработали реализации специальных возможностей таким образом, чтобы доступ к AccessibleObject больше не приводил к преждевременному созданию элемента управления Handle, что, в свою очередь, обеспечивает более предсказуемое поведение элементов управления и позволяет избежать неожиданных сюрпризов в пользовательском интерфейсе.

Мы также улучшили и исправили поведение в нескольких элементах управления (таких как PropertyGrid и MonthCalendar), которые могли помешать инструментам специальных возможностей правильно перемещаться по пользовательскому интерфейсу или, в тяжелых случаях, вызывать сбой приложений.

Поддержка Visual Basic


Visual Basic вместе с его Application Framework поддерживается в .NET 5 и Visual Studio 16.8! Visual Studio 16.8 включает конструктор Windows Forms Designer, поэтому Visual Basic готов для миграции существующих приложений или создания новых приложений.

Для получения дополнительной информации обратитесь к публикации Visual Basic WinForms Apps в .NET 5 и Visual Studio 16.8.

Критические изменения


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


Список известных проблем см. в документе Известные проблемы .NET 5.0.
Подробнее..

Провайдер логирования для Telegram (.NET 5 .NET Core)

27.01.2021 18:04:51 | Автор: admin

Не секрет, что Telegram является на данный момент одним из самых популярных мессенджеров. Особенно в среде ИТ-специалистов. Он удобен, в нем нет встроенной рекламы и работает весьма стабильно. Довольно большую часть времени я общаюсь как по работе, так и по личным вопросам именно в этом мессенджере. Поэтому в один прекрасный день я подумал о том, что было бы удобно, чтобы в этом же мессенджере я мог получать уведомления о работе некоторых своих сервисов. На тот момент я как раз активно работал над интеграцией проекта //devdigest и Telegram, поэтому используя тот же родной Telegram Bot SDK довольно быстро реализовал логгер.

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

Вот в таком виде приходят логи в телеграмм из одного из проектов, которыми я занимаюсьВот в таком виде приходят логи в телеграмм из одного из проектов, которыми я занимаюсь

Подготовка

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

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

Дале нужно получить идентификатор канала. Для публичных каналов все просто - в качестве идентификатора мы можем использовать имя канала. Для приватных каналов все чуть-чуть сложнее.

Получение идентификатора приватного канала.

Чтобы получить идентификатор приватного канала придется воспользоваться помощью еще одного бота @JsonDumpBot. Вам нужно будет любое сообщение из этого канала переслать в этот бот. В ответ вы получите сообщение примерно такого вида:

{  "update_id": 111001100,  "message": {    "message_id": 123456,    "from": {      "id": 12345678,      "is_bot": false,      "first_name": "FirstName",      "username": "username",      "language_code": "en"    },    "chat": {      "id": 123456,      "first_name": "FirstName",      "username": "username",      "type": "private"    },    "date": 1111111111,    "forward_from_chat": {      "id": -1123456789101,      "title": "torf.tv logs",      "type": "channel"    },    "forward_from_message_id": 1,    "forward_date": 1111111111,    "text": "test"  }}

Идентификатор канала находится в блоке forward_from_chat -> id

Теперь, имея идентификатор чата и токен бота мы можем перейти к настройке логгера.

Настройка логгера

Для конфигурации логгера используется класс TelegramLoggerOptions, который содержит следующие поля:

  • AccessToken токен бота;

  • ChatId идентификатор канала (приватного, или публичного), или чата, куда бот будет отправлять сообщения;

  • LogLevel минимальный уровень сообщений, которые будут отправляться в канала. Обычно я в канал отправляю сообщения начиная с уровня Warning, или Error;

  • Source удобочитаемое название сервиса. Полезно, если в один канал приходят сообщения из нескольких сервисов;

Существует несколько вариантов конфигурации логгера непосредственно через код, или через файл кофигурации.

Настройка логгера в коде

Для начала нужно создать и инициализировать экземпляр класса TelegramLoggerOptions.

var options = new TelegramLoggerOptions{    AccessToken = "1234567890:AAAaaAAaa_AaAAaa-AAaAAAaAAaAaAaAAAA",    ChatId = "-0000000000000",    LogLevel = LogLevel.Information,    Source = "Human Readable Project Name"};

Зачем передать этот объект в метод-расширение AddTelegram():

builder  .ClearProviders()  .AddTelegram(options)  .AddConsole();

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

Настройка логгера через файл конфигурации appconfig.json

Также блоггер можно настраивать через файл конфигурации приложения, как это показано ниже:

{  "Logging": {    "LogLevel": {      "Default": "Information",      "Microsoft": "Warning",      "Microsoft.Hosting.Lifetime": "Information"    },    "Telegram": {      "LogLevel": "Warning",      "AccessToken": "1234567890:AAAaaAAaa_AaAAaa-AAaAAAaAAaAaAaAAAA",      "ChatId": "@channel_name",      "Source": "Human Readable Project Name"    }  },  "AllowedHosts": "*"}

Далее, в метод-расширение AddTelegram() необходимо передать экземпляр IConfiguration,

public static IHostBuilder CreateHostBuilder(string[] args) =>    Host.CreateDefaultBuilder(args)        .ConfigureLogging((context, builder) =>        {            if (context.Configuration != null)                builder                    .AddTelegram(context.Configuration)                    .AddConsole();        })        .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<startup>(); });

Пример находится здесь

Установка

Установить логгер можно из NuGet, или же интегрировать код прямо к себе в проект. Библиотека распространяется под лицензией MIT.

Подробнее..

Работа с большими решениями .NET 5 в Visual Studio 2019 16.8

10.02.2021 10:11:22 | Автор: admin

С выпуском .NET 5 миграция решений из .NET Framework увеличилась. В частности, мы начали наблюдать перемещение очень крупных решений. Чтобы обеспечить максимальное удобство, мы работали над оптимизацией Visual Studio для обработки решений, содержащих большое количество проектов .NET 5 и .NET Core. Многие из этих оптимизаций были включены в версию 16.8, и в этом посте рассматриваются внесенные нами улучшения.

Запуск компилятора C# и VB вне процесса

Roslyn, компилятор C# и Visual Basic, парсит и анализирует все решение для поддержки служб, таких как IntelliSense, Go to Definition и диагностика ошибок. В результате Roslyn имеет тенденцию потреблять ресурсы, которые увеличиваются пропорционально размеру открытого решения, что может стать весьма значительным для больших решений. Команда Roslyn уже работала над минимизацией этого воздействия, активно кэшируя информацию на диске, которая не требуется немедленно, но даже при таком кэшировании нельзя избежать необходимости хранить данные в памяти.

Чтобы уменьшить влияние на более крупные решения Visual Studio, команда Roslyn приложила значительные усилия, чтобы вывести компилятор Roslyn из процесса Visual Studio в его собственный процесс. Запуск Roslyn в собственном процессе освобождает ресурсы в самой Visual Studio и дает компилятору Roslyn больше места для выполнения своей работы. Для больших решений это может сэкономить до трети памяти, потребляемой Visual Studio при открытии большого решения.

Оптимизация узла зависимостей

Каждый проект .NET 5 и .NET Core имеет узел в обозревателе решений с именем Dependencies, который отображает все, от чего зависит проект: другие проекты, сборки, пакеты NuGet и т.д. В дополнение к отображению непосредственных зависимостей проекта, узел также показывает транзитивные зависимости проекта, т.е. все, от чего зависит каждая зависимость, и так далее. Для проекта любого разумного размера этот список транзитивных зависимостей может стать довольно большим.

К сожалению, первоначальная реализация узла Dependencies не была очень эффективной в том смысле, что она хранила информацию о переходных зависимостях в памяти. В нем содержалось гораздо больше информации, чем было необходимо, и большая часть данных была избыточной. Мы переписали код, чтобы сохранить только ту информацию, которая абсолютно необходима, и начали использовать существующую информацию о зависимостях, которая уже хранится в NuGet. Эта перезапись сэкономила до 1015% памяти, потребляемой Visual Studio при открытии большого решения.

Уменьшение дублирования информации в MSBuild

После Roslyn одним из других основных потребителей ресурсов в процессе Visual Studio является MSBuild. Это связано с тем, что в качестве механизма сборки большая часть среды IDE основана на объектной модели MSBuild. Хотя мы сделали сами файлы проектов намного меньше в .NET 5 и .NET Core, существует значительное количество вспомогательных файлов проектов, которые импортируются в проекты через SDK. Оценка всех этих файлов необходима для понимания и построения проекта и может потреблять до трети памяти, нужной Visual Studio при открытии большого решения.

Хотя файлы проекта генерируют много данных, большая часть этих данных повторяется, и мы начали дедупликацию этих данных в памяти. Строки - один из наиболее распространенных побочных продуктов системы проектов, и они хранят такую информацию, как имена файлов, параметры и пути. В частности, пути могут быть довольно длинными и потреблять много памяти, если их слишком много или они дублируются слишком много раз. Обеспечено хранение только одной копии строки, что позволяет сэкономить до 510% памяти, потребляемой Visual Studio при открытии большого решения.

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

Одним из важных аспектов проектирования системы проектов .NET 5 и .NET Core является асинхронность. Часто системе проекта требуется выполнять работу в ответ на действие пользователя (например, добавление новой ссылки в проект). Вместо того, чтобы блокировать Visual Studio, пока она завершает свою работу, система проектов позволяет пользователю продолжать работу в среде IDE и выполнять работу в фоновом режиме.

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

Улучшения загрузки большого решения

Проделав всю эту работу, мы значительно оптимизировали работу с большими решениями .NET 5 и .NET Core. Начиная с версии 16.8, во многих наших тестах мы наблюдали увеличение в 2,5 раза размера решения, которое мы можем открыть, прежде чем столкнуться с проблемами ресурсов. Мы также наблюдаем снижение до 25% сбоев, сообщаемых из-за исчерпания ресурсов.

Перечисленные выше улучшения - это только начало изменений, которые мы вносим для улучшения работы с большими решениями в Visual Studio. Производительность отдельных решений по-прежнему может варьироваться в зависимости от размера решения, типа проектов, загруженных расширений и т.д., И есть области, которые мы ищем для улучшения. Мы рекомендуем всем пользователям, у которых возникают проблемы с медленной загрузкой или сбоями при загрузке решений, обращаться к нам по адресу vssolutionload@microsoft.com, чтобы мы могли продолжать улучшать загрузку решений всех типов!

Подробнее..

Парсер командной строки на .NET5

17.02.2021 10:06:07 | Автор: admin

Сегодня мы собираемся показать вам, как начать парсинг аргументов командной строки. Кстати, это один пост из серии статей о .NET 5. У нас есть еще много интересного.

Приложения командной строки, также известные как консольные приложения, - это программы, созданные для использования из оболочки, например cmd или bash. Они существуют с 1960-х годов, задолго до появления Windows, MacOS или любого другого графического пользовательского интерфейса (GUI).

Обычно, когда вы начинаете изучать язык программирования, самый простой и распространенный стартовый пример -- это приложение Hello world. Подобные примеры в основном выводят на консоль только текст Hello world, используя свои встроенные API. Компьютерное ПО может делать много разных вещей. Иногда у вас будет ввод, который каким-то образом преобразуется в вывод. В нашем примере Hello world нет никакого ввода.

Возьмем C#/.Net. Каждый раз, когда вы создаете новое консольное приложение, вы начинаете с файла Program.cs со статическим методом Main, который является точкой входа в ваше приложение:

...static void Main(string[] args){    Console.WriteLine("Hello World!");}...

Очень важной частью этого кода является определение аргумента string[] args. Это определение параметра, которое содержит все аргументы, которые передаются нашему исполняемому файлу во время инициализации нашего процесса. В отличие от C и C++, имя программы не рассматривается как первый аргумент командной строки в массиве args. Если вам нужно это значение, вы можете вызвать Environment.GetCommandLineArgs().

Если вы привыкли к приложениям командной строки, передача аргументов другим приложениям - очень распространенная задача. Да, вы можете вручную проанализировать эти значения, но если у вас есть несколько параметров, это может быть очень подверженным ошибкам кодом (который в любом случае в основном является шаблонным). Это похоже на проблему, которую кто-то уже мог исправить, не так ли? Поэтому, конечно, мы можем найти библиотеку NuGet, которая поможет нам проанализировать эти аргументы. В этой статье я сосредоточусь на CommandLineParser.

CommandLineParser

CommandLineParser - это библиотека с открытым исходным кодом, созданная Эриком Ньютоном и членами сообщества .NET. Она существует с 2005 года и её скачали более 26 миллионов раз! CommandLineParser предлагает приложениям CLR простой и лаконичный API для управления аргументами командной строки и связанными задачами, такими как определение переключателей, параметров и команд.

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

Вместо того, чтобы создавать еще один пример только для демонстрации этой библиотеки, я буду использовать консольное приложение WinML .NET5, которым я поделился в своем предыдущем посте. Вот исходный код. Начнем с этого и добавим NuGet-пакет CommandLineParser:

Давайте создадим новый класс с именем CommandLineOptions:

using CommandLine;namespace ImageClassifier{    public class CommandLineOptions    {        [Value(index: 0, Required = true, HelpText = "Путь к файлу изображения для анализа.")]        public string Path { get; set; }        [Option(shortName: 'c', longName: "confidence", Required = false, HelpText = "Minimum confidence.", Default = 0.9f)]        public float Confidence { get; set; }    }}

Это почти все, что нам нужно для использования этой библиотеки. ValueAttribute и OptionAttribute предоставляются пакетом. Я использую именованные параметры, чтобы было ясно, для чего нужен каждый аргумент. Вернемся к нашему методу Program.cs Main, добавим оператор using, чтобы иметь возможность легко использовать классы пакета в этом файле:

using CommandLine;

Давайте изменим тип возвращаемого значения нашего метода Main на Task. Это означает, что любое возвращаемое нами значение int будет возвращено вызывающей стороне нашего процесса, что обычно указывает на успех/неудачу. В этом примере мы просто вернем 0 в случае успеха и любое другое значение, кроме 0, в случае ошибки:

static async Task Main(string[] args){    return await Parser.Default.ParseArguments<CommandLineOptions>(args)        .MapResult(async (CommandLineOptions opts) =>        {            try            {                // У нас есть полученные аргументы, поэтому давайте просто передадим их                return await AnalyzeFileAsync(opts.Path, opts.Confidence);            }            catch            {                Console.WriteLine("Error!");                return -3; // Unhandled error            }        },        errs => Task.FromResult(-1)); // Invalid arguments}

Здесь вы можете увидеть все изменения по сравнению с предыдущей версией кода.

С этими изменениями приложение корректно анализирует наши аргументы. Для нас даже есть страница помощи, созданная автоматически!

Допустим, вы хотите проанализировать изображение, но хотите получить результат, даже если вы не слишком уверены в нем, скажем, с доверием 30%. Теперь это легко сделать с помощью аргумента -c (--confidence). С этим изображением:

Вы можете получить этот результат, используя --confidence:

> .ImageClassifier.exe C:\Users\alzollin\Downloads\NotALion.jpg --confidence 0.3Image 'C:\Users\alzollin\Downloads\NotALion.jpg' is classified as 'Persian cat'(p=58%).

Заключение

Пакет NuGet CommandLineParser - очень мощный помощник, который упрощает эту часто повторяющуюся задачу до простого декларативного подхода. Кроме того, он даже еще более кастомизируемый, чем я продемонстрировал здесь. Вы можете найти его документацию на их странице GitHub вики.

Подробнее..

Перевод Кодогенерацию с использованием Roslyn можно использовать и без перехода на .Net 5

26.02.2021 00:21:59 | Автор: admin


Недавно, когда я просматривал новые возможности, которые будут включены в .Net 5, я натолкнулся на одну весьма интересную генераторы исходного кода. Этот функционал меня особенно заинтересовал, так как я использую аналогичный подход в течение последних 5 лет, и то, что предлагает Microsoft это просто более глубокая интеграция этого подхода в процесс сборки проектов.


Примечание: Оригинал был написан в момент, когда релиз .Net 5 только-только собирался выйти, но актуальности этот текст, на мой взгляд, не потерял, поскольку переход на новую версию платформы занимает какое-то время, да и принципы работы с Roslyn никак не поменялись.


Далее я поделюсь своим опытом использования Roslyn при генерации кода, и надеюсь, что это поможет вам лучше понять, что именно предлагает Microsoft в .Net 5 и в каких случаях это можно использовать.


Для начала, давайте рассмотрим типичный сценарий генерации исходного кода. У вас есть некий внешний источник информации например такой как база данных или JSON описание какого-нибудь REST сервиса или другая .Net сборка (через рефлексию) или что-нибудь еще, и с помощью этой информации вы можете сгенерировать различные типы исходного кода, такие как DTO, классы моделей базы данных или прокси для REST сервисов.


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


По совпадению, я недавно опубликовал проект с открытым исходным кодом, в котором есть пример такой ситуации. В проекте более 100 классов, которые представляют узлы синтаксического дерева SQL, и мне нужно было создать посетителей (visitors реализации интерфейса IVisitor в советующем шаблоне проектирования), которые будут обходить и изменять объекты дерева (больше информации о проекте вы можете найти в моей предыдущей статье "Дерево синтаксиса и альтернатива LINQ при взаимодействии с базами данных SQL").


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


На первый взгляд, у этой проблемы нет решения, но на самом деле, чтобы её решить, я могу использовать компилятор Roslyn и заранее пре-компилировать классы модели, получив таким образом информацию, аналогичную той, которую я мог бы получить через рефлексию.


Давайте создадим простое консольное приложение и добавим в него пакет Microsoft.CodeAnalysis.CSharp.


Примечание: теоретически это можно сделать и через t4 (без консольного приложения), но я предпочитаю не бороться с добавлением в него ссылок на dll и странным синтаксисом, при отсутствии нормального редактора.


Для начала, нам нужно прочитать все .cs файлы, содержащие классы модели, и извлечь из них синтаксические деревья:


var files = Directory.EnumerateFiles(    Path.Combine(projectFolder, "Syntax"),     "*.cs",     SearchOption.AllDirectories);files = files.Concat(Directory.EnumerateFiles(projectFolder, "IExpr*.cs"));var trees = files    .Select(f => CSharpSyntaxTree.ParseText(File.ReadAllText(f)))    .ToList();

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


var cSharpCompilation = CSharpCompilation.Create("Syntax", trees);foreach (var tree in trees){    var semantic = cSharpCompilation.GetSemanticModel(tree);    ...

Используя семантические данные, мы можем получить объект типа INamedTypeSymbol:


foreach (var classDeclarationSyntax in tree    .GetRoot()    .DescendantNodesAndSelf()    .OfType<ClassDeclarationSyntax>()){    var classSymbol = semantic.GetDeclaredSymbol(classDeclarationSyntax);

который может предоставить информацию о конструкторах и свойствах классов:


//Propertiesvar properties = GetProperties(classSymbol);List<ISymbol> GetProperties(INamedTypeSymbol symbol){    List<ISymbol> result = new List<ISymbol>();    while (symbol != null)    {        result.AddRange(symbol.GetMembers()            .Where(m => m.Kind == SymbolKind.Property));        symbol = symbol.BaseType;    }    return result;}//Constructorsforeach (var constructor in classSymbol.Constructors){    ...}

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


foreach (var parameter in constructor.Parameters){    ...    INamedTypeSymbol pType = (INamedTypeSymbol)parameter.Type;

Теперь необходимо проанализировать каждый тип параметра и выяснить следующее:


  1. Является ли этот тип списком?
  2. Является ли тип Nullable (в проекте используются "Nullable reference types")?
  3. Наследуется ли от этот тип от базового типа (в нашем случае интерфейса), для которого мы и создаем "Посетителей" (Visitors).

Семантическая модель дает ответы на эти вопросы:


var ta = AnalyzeSymbol(ref pType);....(bool IsNullable, bool IsList, bool Expr) AnalyzeSymbol(    ref INamedTypeSymbol typeSymbol){    bool isList = false;    var nullable = typeSymbol.NullableAnnotation == NullableAnnotation.Annotated;    if (nullable && typeSymbol.Name == "Nullable")    {        typeSymbol = (INamedTypeSymbol)typeSymbol.TypeArguments.Single();    }    if (typeSymbol.IsGenericType)    {        if (typeSymbol.Name.Contains("List"))        {            isList = true;        }        if (typeSymbol.Name == "Nullable")        {            nullable = true;        }        typeSymbol = (INamedTypeSymbol)typeSymbol.TypeArguments.Single();    }    return (nullable, isList, IsExpr(typeSymbol));}

Примечание: метод AnalyzeSymbol извлекает фактический тип из коллекций и значений Nullables::


List<T> => T (list := true) T? => T (nullable := true) List<T>? => T (list := true, nullable := true)

Проверка базового типа в семантической модели Roslyn является более сложной задачей, чем такая же при использовании рефлексии, но это также возможно:


bool IsExpr(INamedTypeSymbol symbol){    while (symbol != null)    {        if (symbol.Interfaces.Any(NameIsExpr))        {            return true;        }        symbol = symbol.BaseType;    }    return false;    bool NameIsExpr(INamedTypeSymbol iSym)    {        if (iSym.Name == "IExpr")        {            return true;        }        return IsExpr(iSym);    }}

Теперь мы можем поместить всю эту информацию в простой контейнер:


public class NodeModel{    ...    public string TypeName { get; }    public bool IsSingleton { get; }    public IReadOnlyList<SubNodeModel> SubNodes { get; }    public IReadOnlyList<SubNodeModel> Properties { get; }}public class SubNodeModel{    ...    public string PropertyName { get; }    public string ConstructorArgumentName { get; }    public string PropertyType { get; }    public bool IsList { get; }    public bool IsNullable { get; }}

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


В моем проекте я запускаю генерацию кода как консольную утилиту, но в .Net 5 вы сможете встроить эту генерацию в класс реализующий интерфейс ISourceGenerator и помеченный специальным атрибутом Generator. Экземпляр этого класса будет автоматически создаваться и запускаться во время сборки проекта для добавления недостающих частей кода. Это, конечно, удобнее, чем отдельная утилита, но идея аналогична.


Примечание: Я здесь не буду описывать саму кодогенерацию в .Net 5 так как в интернете есть много информации об этом, например ссылка 1 или ссылка 2


В завершение, я хочу сказать, что вы не должны воспринимать эту новую возможность .Net 5 как невероятное нововведение, которое коренным образом изменит подход к генерации динамического кода, используемый в таких библиотеках, как AutoMapper, Dapper и т. д. (слышал и такие мнения) Не изменит! Дело в том, что описанная выше генерация кода работает в статическом контексте, где все заранее известно, но, например, AutoMapper не знает заранее, с какими классами он будет работать, и ему все равно придется динамически генерировать IL код "на лету". Однако бывают ситуации, когда такая генерация кода может быть весьма полезна (одну из них ситуаций я описал в этой статье). Поэтому стоит, как минимум, знать об этой возможности и понимать ее принципы и ограничения.


Ссылка на исходный код на github

Подробнее..
Категории: C , Net , Codegeneration , Net 5

Уменьшить размер консольного .NET 5.0 приложения

23.03.2021 10:07:12 | Автор: admin

Target Framework Moniker

Давайте знакомиться. В .NET 5.0 для использования Windows Forms или WPF нам недостаточно просто указать net5.0:

<PropertyGroup>  <TargetFramework>net5.0</TargetFramework>  <UseWindowsForms>true</UseWindowsForms></PropertyGroup>

При попытке использования Windows Forms или WPF мы получаем ошибку

C:\Program Files\dotnet\sdk\5.0.201\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.DefaultItems.targets(369,5): error NETSDK1136: The target platform must be set to Windows (usually by including '-windows' in the TargetFramework property) when using Windows Forms or WPF, or referencing projects or packages that do so.

Решение, как подсказывает ошибка состоит в указании Target Framework Moniker

<PropertyGroup>  <TargetFramework>net5.0-windows</TargetFramework>  <UseWindowsForms>true</UseWindowsForms></PropertyGroup>

Как это работает

При сборки автоматически импортируются файлы из Microsoft.NET.Sdk\targets.
Далее в dotnet\sdk\5.0\Sdks\Microsoft.NET.Sdk.WindowsDesktop\targets\Microsoft.NET.Sdk.WindowsDesktop.props содержиться код:

    <FrameworkReference Include="Microsoft.WindowsDesktop.App" IsImplicitlyDefined="true"                        Condition="('$(UseWPF)' == 'true') And ('$(UseWindowsForms)' == 'true')"/>    <FrameworkReference Include="Microsoft.WindowsDesktop.App.WPF" IsImplicitlyDefined="true"                        Condition="('$(UseWPF)' == 'true') And ('$(UseWindowsForms)' != 'true')"/>    <FrameworkReference Include="Microsoft.WindowsDesktop.App.WindowsForms" IsImplicitlyDefined="true"                        Condition="('$(UseWPF)' != 'true') And ('$(UseWindowsForms)' == 'true')"/>

Где проблема

Дело в том, что FrameworkReference это транзитивная зависимость: Документ дизайна .NET , Документация NuGet

Это значит, что если у нас есть где-то сборка, которая использует тип из Windows Forms или из WPF мы должны будем все зависимые сборки перевести на 'net5.0-windows'.

Это грозит нам тем, что мы добавляем в резузльтатирующий файл потенциально ненужный хлам.

Если весь код использует Windows Forms или WPF проблемы нет, а если мы в итоге создаём консольное приложение то получаем дополнительные 60МБ при создании самодостаточного файла.

Пример

Библиотека

using System.Windows.Forms;namespace Library{    public class Demo    {        void ShowForm()        {            var f = new Form();            f.Show();        }    }}

Консольное приложение

using System;class Program{    public static void Main()    {        Console.WriteLine("Hello World!");    }}

Обратим внимание, что мы не используем класс Library.Demo.

Соберём самодостаточное приложение с помощью dotnet publish:

dotnet publish ConsoleApp.csproj --self-contained -c Release -r win-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true /p:IncludeAllContentForSelfExtract=true

Результат 81,8МБ !

Благодаря IncludeAllContentForSelfExtract при запуске приложение в %TEMP%\.net мы можем посмотреть из чего оно состоит.

Как же так ?
Мы не использовали Library.Demo, мы указали PublishTrimmed, а Windows Forms к нам пришёл.

Решение

Как видим dotnet publish при обычных настройках не справился со своей работой, поэтому поможем ему !

Шаг 1

В библиотеке укажем вручную зависимость фреймворка и попросим не создавать транзитивную зависимость:

<Project Sdk="Microsoft.NET.Sdk">  <PropertyGroup>    <TargetFramework>net5.0</TargetFramework>    <!-- Укажем зависимости явно -->    <DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences>  </PropertyGroup>  <ItemGroup>    <!-- .NET Runtime -->    <!-- В данном случае неважно будет PrivateAssets="all" или нет, всегда добавляется при сборке -->    <FrameworkReference Include="Microsoft.NETCore.App" />        <!-- Windows Desktop -->    <!-- PrivateAssets="all" - зависимость не идёт дальше -->    <FrameworkReference Include="Microsoft.WindowsDesktop.App" PrivateAssets="all"  />        <!-- Можно указать и более конкретно:      Microsoft.WindowsDesktop.App.WPF      Microsoft.WindowsDesktop.App.WindowsForms -->  </ItemGroup></Project>

Документация для DisableImplicitFrameworkReference

Ключевая часть PrivateAssets="all". которая не даёт зависимости распространяться дальше.

Шаг 2

Меняем .net5.0-windows на .net5.0 в нашем консольном приложении

Результат

Собираем той же командой:

dotnet publish ConsoleApp.csproj --self-contained -c Release -r win-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true /p:IncludeAllContentForSelfExtract=true

Получаем файл размером всего 18.8МБ со следующим содержимым

Заключение

Стоит ли делать так в библиотеках ?
Однозначно да !
С одной стороны это позволяет использовать типы из Windows Forms или WPF, с другой стороны у сборщика получается выкинуть всё неиспользованное и выдать меньший размер файла.

Подробнее..
Категории: C , Net , Csharp , Net 5

Добавляем CRUD в ASP.NET Core проект за 10 минут с помощью EasyData

19.04.2021 06:22:01 | Автор: admin

image


Одной из первых задач для большинства бизнес-приложений на ASP.NET Core является реализация операций CRUD (Create, Read, Update, Delete) для основных объектов, с которыми работает ваше решение.


Каждый разработчик, которому нужно решить эту задачу, знает, что создание CRUD-страниц и форм очень скучный и трудоемкий процесс.
Если делать это вручную, то получится очень медленно и наверняка с кучей недоработок (пропущенные поля, забытые валидаторы и т.д.).
Можно воспользоваться инструментом scaffolding'а, доступным в Visual Studio. Но даже в этом случае это будет совсем не быстрый процесс, поскольку его нужно запускать для каждого класса модели. В итоге вы получаете множество .cs/.cshtml файлов, которые нужно поддерживать и атуализировать по мере изменений в классах модели или просто когда нужно что-то исправить в поведении или внешнем виде CRUD страниц. Если количество сущностей в вашей БД превышает десяток, то весьма велики шансы того, что файлы для реализации CRUD операций занимают больше 50% всей кодовой базы вашего проекта. Более того это решение все равно не обеспечивает некоторых важных, а порой и необходимых функций, таких как разбитие на страницы в режиме просмотра (pagination) или банальные поиск/фильтрация.


Решение: использовать библиотеку с открытым кодом EasyData, о которой и пойдет речь в данной статье.


Что такое EasyData?


EasyData и была создана для решения большинства (если не всех) проблем описанных выше. Это библиотека с открытым кодом, распространяется по MIT лицензии, исходники доступны на GitHub. Главной особенностью является использование декларативного подхода.
Весь процесс можно разделить на два основных этапа:


  • Вы описываете, с какими данными (сущностями и атрибутами) вы хотите работать и как ваша программа должна работать с этими данными (типы, ограничения, отношения между сущностями и т.д.).

  • На основе этой информации библиотека EasyData разворачивает Web API для CRUD-операций и пользовательский интерфейс на основе чистого (ванильного) JavaScript, что позволяет вашим пользователям выполнять все те операции.

Самое замачательное здесь то, что в случае использования Entity Framework Core для первого шага (описания данных) вам нужен только ваш DbContext! Вы просто скармливаете его библиотеке, и EasyData автоматически извлекает оттуда всю необходимую информацию для разворачивания CRUD API и пользовательского интерфейса.


Весь процесс занимает всего несколько минут и около 10 строк кода:


EasyData quick demo


Подключаем EasyData в свой проект


Прежде всего, чтобы попробовать EasyData в работе, вы можете открыть и запустить один из примеров проектов, доступных на GitHub.


Для установки EasyData в собственный проект надо выполнить следующие 3 простых шага:


1. Устанавливаем NuGet пакеты EasyData


  • EasyData.AspNetCore
  • EasyData.EntityFrameworkCore.Relational

2. Добавляем EasyData middleware в Startup.Configure:


using EasyData.Services;.    .    .    .    .    app.UseEndpoints(endpoints => {        endpoints.MapEasyData(options => {            options.UseDbContext<AppDbContext>();        });        endpoints.MapRazorPages();    });

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


3. Настраиваем страницу CRUD операций


Если вы используете Razor Pages, добавьте новую страницу (например, EasyData.chstml). Если это MVC, вам понадобятся новый контроллер и соответствующий ему view.


Наша новая страница должна будет ловить все адреса, которые начинаются с определенного префикса (/easydata/ по умолчанию, но это можно настроить). Для этого мы используем специальный catch-all параметр в определении маршрута: "/easydata/{**entity}".


Кроме того, мы также добавляем .css и .js файлы EasyData (easydata.min.css и easydata.min.js), которые обеспечивают отрисовку интерфейса управления данными и обработку всех CRUD-операций на стороне клиента.


@page "/easydata/{**entity}"@{    ViewData["Title"] = "EasyData";}<link rel="stylesheet" href="http://personeltest.ru/aways/cdn.korzh.com/ed/1.2.4/easydata.min.css" /><div id="EasyDataContainer"></div>@section Scripts {    <script src="http://personeltest.ru/aways/cdn.korzh.com/ed/1.2.4/easydata.min.js" type="text/javascript"></script>    <script>        window.addEventListener('load', function () {            new easydata.crud.EasyDataViewDispatcher().run()        });    </script>}

Вот и все. Теперь вы можете запустить свой проект, открыть URL-адрес /easydata и наслаждаться функциями CRUD.


Вот как это выглядит в итоге:



Страница просмотра значений для некоторой сущности (в данном случае, Orders)



Диалог редактирования одной записи



Lookup диалог, который был открыт из диалога редактирования записи


Как это работает


Коротко о том, как работает вся эта магия.


Как мы уже упоминали ранее, EasyData решает 3 основных задачи:


  • Собирает метаданные из нашей базы данных.
  • Устанавливает API для основных CRUD операций.
  • Визуализирует интерфейс (опять же, на основе метаданных) и обрабатывает все взаимодействие пользователя с этим интерфейсом.

Давайте изучим все эти части более подробно.


Метаданные


Метаданные это данные о ваших данных: какие сущности (таблицы) хранятся в вашей базе данных, как они связаны, какие имеют атрибуты (поля) и другая полезная информация.


EasyData собирает метаданные (каким-либо способом об этом см. ниже) и сохраняет их в объекте класса MetaData. Этот объект содержит список сущностей (таблиц), атрибуты (поля) для каждой сущности, связи между сущностями и некоторую дополнительную информацию, используемую в API и при визуализации и обработке пользовательского интерфейса.


Для заполнения объекта MetaData нам нужно указать некоторый загрузчик метаданных. В нашем примере мы сделали это с помощью вызова UseDbContext для загрузки метаданных из объекта DbConext. На данный момент (в версии 1.2), это пока единственный доступный загрузчик метаданных. В будущих версиях можно будет загружать метаданные непосредственно с БД или, возможно, каким-нибудь другим способом.


EasyData middleware


EasyData middleware отвечает за обработку REST API для всех CRUD (и не только) операций, инициированных веб страницей.


Чтобы добавить middleware в очередь обработки вашего ASP.NET Core приложения используйте функцию MapEasyData в процессе настроки точек вызова (endpoints) UseEndpoints:


  app.UseEndpoints(endpoints =>    {       endpoints.MapEasyData(options => {            options.UseDbContext<AppDbContext>();        });    }

Этот вызов желательно поставить перед любым вызовом MapControllerRoute или MapRazorPages.
По умолчанию EasyData API будет откликаться по адресу /api/easydata, но вы легко можете изменить его на другой:


   endpoints.MapEasyData(options => {        options.Endpoint = "/api/my-crud";        .    .    .    .    });

Единственное, что нужно настроить для EasyData middleware, это сказать ему, откуда брать метаданные. Как уже упоминалось выше, сейчас доступен только один вариант, а именно получение метаданных с DbContext. Вот почему мы добавляем вызов UseDbContext <AppDbContext>() в приведенном выше примере. Кроме получения метаданных, UseDbContext также обеспечивает наш middlware всеми средствами для выполнения CRUD-операций (через сам объект DbContext).


Корневая страница интерфейса EasyData


В качестве такой страницы выступает Razor page или же MVC view. Эта страница должна обрабатывать все URL адреса, которые начинается с определенного префикса. По умолчанию это /easydata/ и поэтому все пути, типа /easydata/student или /easydata/invoice, должны быть обработаны этой страницей.


NB: /easydata/ префикс по умолчанию. Вы можете установить и другой путь к этой странице, но в этом случае его нужно будет указать в параметрах нашего объекта RootDispatcherView


Наша корневая страница может содержать любые элементы HTML на ваш выбор. Но для нормальной работы CRUD интерфейса, она должна включать следующие 4 элемента:


  • <link> элемент со ссылкой на CSS файл EasyData (easydata.min.cs)


  • Контейнер (пустой элемент div), где будет рисоваться наш CRUD интерфейс. По умолчанию он должен иметь идентификатор EasyDataContainer, но это также можно настроить с помощью опций.


  • <script> элемент со ссылкой на файл easydata.min.js.


  • небольшой скрипт, который создает и запускает объект EasyDataViewDispatcher при загрузке страницы.



Пример простейшей корневой страницы вы можете увидеть в разделе Подключаем EasyData в свой проект выше.


В завершение


На данный EasyData может работать с .NET Core 3.1 и .NET 5. Очевидно, поддерживаются все версии ASP.NET Core и Entity Framework Core, которые могут работать с указанными версиями .NET (Core).
Однако не будет большой проблемы при необходимости добавить также поддержку предыдущих версий .NET Core или даже .NET Framework 4.x. Если кому-то это нужно, создавайте новый issue про это в GitHub репозитории библиотеки.


EasyData был сделан, в первую очередь, для быстрого создания прототипов новых проектов или так называемых POC (proof of concept), когда уже есть некоторое понимание с какими данными придется работать, но не хочется тратить много времени на реализацию простейших операций с этими данными.


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


Ну и конечно, не забудьте поставить звездочку в EasyData репозитории на GitHub. Особенно если эта библиотека помогла сэкономить вам немного времени.

Подробнее..
Категории: C , Net , Net core , Asp.net core , Entity framework , Net 5 , Crud

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru