Rostov .net community channel
500 subscribers
23 photos
60 links
Новости ростовского .net-сообщества.

Правила: https://bit.ly/rndtech_codeofconduct
Чат: @rnddotnet
По всем вопросам: Вадим @Vadimyan
Download Telegram
DotNext в поиске спикеров!

8 и 9 сентября в Москве пройдет DotNext 2023 — участники могут прийти лично, либо подключиться к онлайн-трансляции. Вы можете стать спикером конференции как в офлайне, так и удаленно.

Вы можете выбрать любой формат выступления — доклад, воркшоп, интервью, обсуждение, BoF-сессию или придумать что-то свое.

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

Подать заявку можно на сайте.
Вышел .NET 8 Preview 3, а вместе с ним обновления для ASP.NET Core и MAUI.

В ASP.NET Core в превью появилась поддержка Native AOT — уменьшится место на диске и память, а скорость запуска повысится (в бенчмарках — в 5 раз). Стоит внимательно посмотреть на таблицу поддержки фич в анонсе, но новость точно хорошая. Остальные улучшения касаются Blazor, поддержки SSR и SIMD.

В Preview 3 меняется подход к output path (опционально), улучшается JIT и поддержка multiplatform в контейнерах. Все изменения в анонсе.

В MAUI основная работа идёт над улучшение качества и оптимизацией потребления памятью.

А ещё появилось превью трёх новых фич C#12.

1. Первичные конструкторы не только для record:

public class Student(int id, string name, IEnumerable<decimal> grades)
{
public Student(int id, string name) : this(id, name, Enumerable.Empty<decimal>()) { }
public int Id => id;
public string Name { get; set; } = name.Trim();
public decimal GPA => grades.Any() ? grades.Average() : 4.0m;
}


2. Алиасы типов:

using PathOfPoints = int[];
using DatabaseInt = int?;
using Measurement = (string Units, int Distance);

public void F(Measurement x)
{ }

3. Значения по-умолчанию внутри лямбд:

var addWithDefault = (int addTo = 2) => addTo + 1;
addWithDefault(); // 3
addWithDefault(5); // 6

И протаскивание этих значений в метаданные:

var addWithDefault = (int addTo = 2) => addTo + 1;
addWithDefault.Method.GetParameters()[0].DefaultValue; // 2

Остается только напомнить, что уже в эту среду, 12 апреля, будет открытая встреча с программным комитетом DotNext. Форма регистрации. Можно задать вопросы или поговорить про темы докладов, подготовку спикеров и подачи.
Сегодня не статья, а целое соревнование.
tl;dr: "Вероятно вам не нужен MediatR" vs. "Вам нужен медиатор"

Почти год назад великолепный автор канала @steponeit выпустил статью "Вероятно вам не нужен MediatR".

Очень кратко тезисы о том, почему всё так:
* Несмотря на название, MediatR не является реализацией шаблона Посредник вовсе. Это диспетчер команд.
* В большинстве случаев его использование это прикрытый вызов методов по сути схожий с Service Locator'ом, являющимся анти-паттерном.
* Классы вынужденно зависят от методов, которые они не используют, и тем самым нарушают ISP, что влечёт неявную глобальную связанность.
* Также есть тенденция к нарушению EDP: вместо явного запроса требуемых объектов, объявляется глобальная точка доступа ко всему домену.
* У интерфейсов в домене нет возможности именоваться в стиле единого языка.
* Код домена загрязнён методами Send и Handle.
* Классы в домене вынуждены реализовывать интерфейсы из MediatR, что приводит к большой зависимости от сторонней библиотеки.
* Стала тяжелее навигация по коду.
* IntelliSense тебе не помощник.
* Компилятор из-за непонимания происходящего помечает классы неиспользуемыми. Эту проблему приходится решать обходными приёмами.
* Вызов обработчика напрямую где-то в 50 раз быстрее и требует на порядок меньше памяти, чем через MediatR.
* Хорошая новость: MediatR можно легко заменить тривиальными практиками ООП.

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

NuGet-пакет MediatR скачали 119 миллионов раз, он используется в известных проектах dotnet-architecture/eShopOnContainers (23k звезд на гитхабе), ardalis/CleanArchitecture (12k) и dotnet-architecture/eShopOnWeb (8k), которые знают многие, кто изучал архитектуру .net приложений. И есть много статей за и против MediatR.

Я сам начал и закончил пользоваться MediatR сравнительно недавно. Но хочется понять, что о нём думают другие. Тут будет голосование, но, возможно, вам есть, что рассказать в комментариях :)
Вышла предварительная версия C# Dev Kit — нового расширения для VS Code для разработки на C#. Работает совместно с C# extension и новым полностью открытым Language Server Protocol. Что внутри:
* Новый Solution Explorer view
* Test Explorer с поддержкой XUnit, NUnit, MSTest, и bUnit
* Благодаря LSP навигация и подсветка начинают работать значительно быстрее, IntelliSense ускорился в 10 раз
* AI-based IntelliCode
* В GitHub Codespaces это тоже работает!

Установить можно из маркетплейса.
Что: RndTechTalks.DotNet #2
Где: Ростов-на-Дону, Города Волос, 14 (Коворкинг "Луч")
Когда: 22 июня, 19:00 (ДА, ЭТО ПРЯМО СЕЙЧАС)
Трансляция митапа начнется через 10 минут: https://www.youtube.com/watch?v=TmdExbz2imU

На улице .net-разработчиков перевернулся грузовик с радостью — потому что мы приготовили второй митап по дотнету с интересными темами, общением, афтерпати и той самой атмосферой © RndTech. В этот раз будем говорить о том, как тестировать быстро и приятно — учимся локально поднимать сервисы при тестировании API и использовать DI-контейнер для чистого поддерживаемого кода в тестах. Программа:

WebApplicationFactory для ваших проектов
🎤 Костя Косьянов
Для тестирования API в .net core появился класс WebApplicationFactory, который позволяет удобно и быстро поднимать инстансы тетсируемых сервисов внутри запуска тестов. В докладе узнаем, как им пользоваться, какие преимущества он даёт, как работать с ним в сложных сценариях и какие проблемы могут встретиться по пути.

DI-контейнеры в NUnit-тесах
🎤 Вадим Мартынов
В автотестах разных проектов я часто видел повторяющиеся проблемы — код тестов сложный для чтения, навигации и рефакторинга, тесты не параллельны и их сложно распараллеливать, запуск одного теста может вызывать длительные действия подготовки иногда не нужных ему данных, а из-за хитроспелетений [SetUp]-ов и наследования сложно понять в каком контексте работает конкретный тест. Попробуем решить эти проблемы, используя наши общепризнанные практики написания кода и посмотрим, что из этого получится.

🍙 ☕️ Закуски, кофе, подарки участникам, холивары и афтерпати в баре "КВАДРАТ" — в комплекте. Регистрируйтесь, приходите, будем рады встретиться и пообщаться :)

Отдельно благодарим наших добрых друзей из Южного ИТ-Парка за предоставленную площадку. Вы восхитительны!
Вышли .NET 8 Preview 6, ASP.NET Core 8 Preview 6 и C# 12 preview. Если быть очень краткими, то есть много улучшений для Native AOT, интересные сорс генераторы (иногда тоже для улучшения поддержки Native AOT) и другие небольшие правки. Начнём с основных изменений в .NET:

1. Улучшения в System.Text.Json (многие — для Native AOT)
1.1. Кэширование в инкрементальном source generator для улучшения производительности IDE в больших проектах.
1.2. Улучшено форматирование сгенерированного кода.
1.3. Много фиксов про разбор атрибутов, nullable-структур, модификаторы доступности и прочее.
1.4. Новый атрибут JsonStringEnumConverter<TEnum> на замену JsonStringEnumConverter.
1.5. Новое свойство JsonConverter.Type чтобы узнать тип для JsonConverter:

2. Потоковые перегрузки для ZipFile.CreateFromDirectory и ZipFile.ExtractToDirectory, которые позволяют архивировать/разрахивировать папки сразу в Stream, избегая промежуточной записи на диск.

3. Класс MetricCollector в пакете Microsoft.Extensions.Telemetry.Testing как замена InstrumentRecorder.

4. Source generator для валидации Options.

5. Внутренние улучшения source generator для configuration binding (всё это тоже для Native AOT).

6. Source generator для COM Interop. Тут в двух словах не объяснить, но tl;dr: можно пометить интерфейс атрибутом GeneratedComInterfaceAttribute и source generator будет генерировать вызовы из управляемого кода и обратно, а сгенерированные типы можно использовать в LibraryImportAttribute. В анонсе есть описание и пример.

7. Поддержка HTTPS-proxy

8. Поддержка SHA-3 в System.Security.

9. Поддержка iOS платформ в Native AOT
И пару слов про C# 12 preview features вышедшие вместе с .NET 8 Preview 6 и Visual Studio 17.7 Preview 3:

1. nameof теперь работает с именами членов. Даже в инициализаторах, атрибутах и статических членах:

internal class NameOf
{
public string S { get; } = "";
public static int StaticField;
public string NameOfLength { get; } = nameof(S.Length); // в инициализаторах работает
public static void NameOfExamples()
{
Console.WriteLine(nameof(S.Length)); // и просто работает
Console.WriteLine(nameof(StaticField.MinValue)); // и для статического поля работает
}
[Description($"String {nameof(S.Length)}")] // и в атрибуте работает
public int StringLength(string s)
{ return s.Length; }
}

2. Inline массивы. Если коротко, то эта фича позволяет пометить generic-структуру определенного вида атрибутом InlineArrayAttribute, то рантайм воспринимает это как команду реплицировать тип структуры n раз. В том числе будут реплицирована и информация об отслеживании жизненного цикла для GC. Это немного низкоуровневая оптимизация для массивов фиксированной длины, вряд ли такие структуры придётся часто создавать, но есть вероятность, что скоро мы увидим такие структуры в библиотеках и будем пользоваться ими.

// runtime replicates the layout of the struct 42 times 
[InlineArray(Length = 42)]
struct MyArray<T>
{
private T _element0;
public Span<T> SliceExample()
{
return MemoryMarshal.CreateSpan(ref _element0, 42);
}
}

3. Interceptors. Фича позволяет с помощью атрибутов перенаправить определенные вызовы методов в другой код (например, подменить вызовы методов в определенном месте другими вызовами. Громоздкий интерфейс требует явного указания перехватываемого вызова в исходном коде и поэтому предназначен и востребован может быть только в сорс-генераторах, но при этом выглядит довольно магически:

using System;
using System.Runtime.CompilerServices;

var c = new C();
c.InterceptableMethod(1); // prints `interceptor 1`
c.InterceptableMethod(1); // prints `other interceptor 1`
c.InterceptableMethod(1); // prints `interceptable 1`

class C
{
[Interceptable]
public void InterceptableMethod(int param)
{
Console.WriteLine($"interceptable {param}");
}
}

// generated code
static class D
{
// 'position' is a number of characters from the start of the file
[InterceptsLocation("Program.cs", position: 73)]
public static void InterceptorMethod(this C c, int param)
{
Console.WriteLine($"interceptor {param}");
}

[InterceptsLocation("Program.cs", position: 125)]
public static void OtherInterceptorMethod(this C c, int param)
{
Console.WriteLine($"other interceptor {param}");
}
}
NET8 стал ещё ближе — сегодня вышел .NET 8 Release candidate 1 — https://devblogs.microsoft.com/dotnet/announcing-dotnet-8-rc1/
Продолжает допиливаться System.Text.Json, сорсгенератор для конфигураций, добавились keyed services в minimal apis. Изменения с предыдущего превью не такие значительные, хотя их и достаточно много. Но статус RC уже позволяет более уверенно переводить проекты на следующий LTS релиз дотнета до которого осталось буквально пару месяцев.
Представляем ваш худший кошмар — библиотека Cysharp/PrivateProxy. Если коротко — это обёртка над UnsafeAccessor в .net8, который позволяет получить доступ к приватным членам без рефлексии и с поддержкой AOT. Вот как это выглядит. Во-первых, добавим новый тип и пометим его атрибутом для сорсгенератора [GeneratePrivateProxy]:

using PrivateProxy;

public class Sample
{
private int _field1;
private int PrivateAdd(int x, int y) => x + y;
}

[GeneratePrivateProxy(typeof(Sample))]
public partial struct SampleProxy;


И на этом всё, можно пользоваться:

var sample = new Sample();
sample.AsPrivateProxy()._field1 = 10;

Естественно, всё это с проверками времени компиляции, без потери производительности, с поддержкой статических членов, ref значений, in/out параметров и изменяемых структур.
В продолжение июньского митапа RndTechTalks.DotNet #2 — если вы ещё не видели, то на нашем ютуб-канале уже лежат записи митапов с улучшенным звуком. Можно посмотреть плейлист: https://www.youtube.com/playlist?list=PLZKHRHuHZs0i-0guyRAYcm-LZzgKtzNSq
Или выбрать отдельные доклады.

WebApplicationFactory для ваших проектов https://youtu.be/eJpUs8P3rRw
🎤 Костя Косьянов
Для тестирования API в .net core появился класс WebApplicationFactory, который позволяет удобно и быстро поднимать инстансы тетсируемых сервисов внутри запуска тестов. В докладе узнаем, как им пользоваться, какие преимущества он даёт, как работать с ним в сложных сценариях и какие проблемы могут встретиться по пути.

DI-контейнеры в NUnit-тесах https://youtu.be/usTY7g1L3G8
🎤 Вадим Мартынов
В автотестах разных проектов часто можно встретить повторяющиеся проблемы — код тестов сложный для чтения, навигации и рефакторинга; тесты не параллельны, и их сложно распараллеливать; запуск одного теста может вызывать длительные действия подготовки иногда не нужных ему данных; из-за хитроспелетений [SetUp]-ов и наследования сложно понять, в каком контексте работает конкретный тест. Попробуем решить эти проблемы, используя общепризнанные практики написания кода и посмотрим, что из этого получится.
.NET Aspire — это стек для создания отказоустойчивых, наблюдаемых и конфигурируемых облачных приложений на базе .NET.

Он содержит тщательно подобранный набор компонентов для cloud-native разработки, включая service discovery, телеметрию и хелсчеки по-умолчанию. В сочетании со богатым, но простым инструментарием для локальной разработки .NET Aspire позволяет легко найти и нстроить необходимые зависимости для новых и существующих приложений .NET, использующих .NET 8+.

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

Новость в блоге https://devblogs.microsoft.com/dotnet/introducing-dotnet-aspire-simplifying-cloud-native-development-with-dotnet-8/
Гитхаб https://github.com/dotnet/aspire
Незаметно и тихо вышел релиз NUnit 4.0.0
Изменений очень много, при этом они эволюционные, так что обратная совместимость почти не ломается. Лучше почитайте полный список в анонсе https://docs.nunit.org/articles/nunit/release-notes/framework.html#nunit-400---november-26-2023 а тут оставим пару интересных на наш взгляд фич.
4521 Proposal: Async test case sources
4385 Add support for Test Cancellation
3899 Allow randomizing 'Guid' test arguments with [Random]
3856 Theories in nested Testfixtures
3391 TestCaseSource to recognize new async streams
3376 Nullable Reference Types annotations
2843 Replacing ThrowsAsync with a composable async alternative
4416 Move classic asserts into its own project
🔒.NET 9: тип System.Threading.Lock 🔒

В .NET 9 появляется новый тип для работы с блокировками — System.Threading.Lock.

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

private readonly object _lock = new object();
public void DoSomething()
{
lock (_lock)
{
// Do something
}
}


И вот новый подход с Lock:

private readonly Lock _lock = new Lock();
public void DoSomething()
{
lock (_lock)
{
// Do something
}
}


Да, фактическое использование остается прежним. Так зачем же новый тип?
1. Ясность Намерений: Lock явно предназначен для блокировок, устраняя двусмысленность в использовании обычных объектов для блокировок.
2. Потенциальная Производительность: В то время как старый подход всегда задействовал Monitor, новый тип Lock может использовать более эффективные подходы, что теоретически делает его быстрее.
3. Совместимость с паттерном Lock statement: пропозал о дополнительном использовании Lock через шаблон using предоставляет более чистый и привычный способ управления блокировками, что может улучшить читаемость и поддержку кода.

class MyDataStructure
{
private readonly Lock _lock = new();
void Foo()
{
using (_lock.EnterLockScope())
{
// do something
}
}
}


Собственно, последний пример многим уже может быть знаком из библиотеки AsyncEx:

public async Task UseLockAsync()
{
// Attempt to take the lock only for 2 seconds.
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));

// If the lock isn't available after 2 seconds, this will
// raise OperationCanceledException.
using (await _mutex.LockAsync(cts.Token))
{
await Task.Delay(TimeSpan.FromSeconds(1));
}
}
👀 C# 13 может скоро получить новую фичу, которая позволит использовать ref и unsafe в итераторах и асинхронных методах.

📌 Мотивация

В современном мире вы не можете сделать так:


async Task MyMethodAsync()
{
await AnAsyncMethod();
ref int x = ref GetRef();
DoSomething(ref x);
await AnohterAsnycMethod();
}


Проблема с await и ref заключается в том, что компилятор не может гарантировать, что ссылка все еще будет действительна после выполнения await. Но в данном случае это не должно быть проблемой, так как x используется только между двумя вызовами await, где ссылка все еще действительна.

То же самое относится к ref структурам, таким как Span<T> или ReadOnlySpan<T>. Их нельзя использовать в итераторах (yield) или асинхронных методах.

Предложение как раз и позволит это делать:


async Task MyMethodAsync()
{
var result = await AnAsyncMethod();
ReadOnlySpan<char> span = result.AsSpan();
DoSomething(span);
await AnohterAsnycMethod();
}


#proposal
Please open Telegram to view this post
VIEW IN TELEGRAM
Change my mind
Вот такой пример принёс соседний канал. Ждём? Знаете, где будете применять?
🔥 Non-allocating split finally: совсем скоро в .NET 9