C# Short Posts 🔞 – Telegram
C# Short Posts 🔞
250 subscribers
111 photos
4 videos
151 links
Здесь я, Дима Афонченко @Undermove1, публикую короткие заметки о разработке (и около). Я не претендую на правильность высказываний и открыт к дискуссиям, исправлениям и конструктивной критике. С любыми деструктивными вещами можно приходить в комменты)
Download Telegram
Channel name was changed to «C# Short Posts»
В логах с прода всегда бесило, что нет никакой возможности посмотреть номер строки, в котором произошла ошибка.

Но кажется в С# 10 есть инструмент, который поможет хотя бы частично решить эту проблему

Новый атрибут времени компиляции [CallerArgumentExpression]

Кратко про суть:

Есть у вас есть такой код:
var array = Array.Empty<int>();

вы хотите проверить его на пустоту:

public static void ItIsNotEmpty<T>(
IEnumerable<T> value,
[CallerArgumentExpression("value")] string message = "")
{
if (!value.Any())
{
throw new ArgumentException("Sequence is empty", message);
}
}


И вызываете вашу функцию вот так:

EnsureThat.ItIsNotEmpty(array);

Вот как будет выглядеть ошибка, которую выкинет этот код:

System.ArgumentException: Sequence is empty (Parameter 'array')

Обратите внимание на то что находится в скобочках. Мы видим там не название параметра,а имя той переменной, что мы передали в скобочки. ТО ЕСТЬ если мы вызовем метод вот так:

EnsureThat.ItIsNotEmpty(Array.Empty<int>());

То ошибка станет вот такой:
System.ArgumentException: Sequence is empty (Parameter 'Array.Empty<int>()')

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

В чем суть фичи. Положим у вас есть сервис вот с такими зависимостями:
public NotificationService(
IUsersRepository usersRepository,
ISubnoscriptionsService subnoscriptionsService,
IDataBaseService dataBaseService,
IBusSystem busSystem)

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

Mock<IUsersRepository> usersRepositoryMock = new Mock<IUsersRepository>();
Mock<ISubnoscriptionsService> subnoscriptionsServiceMock = new Mock<ISubnoscriptionsService>();
Mock<IDataBaseService> dataBaseServiceMock = new Mock<IDataBaseService>();
Mock<IBusSystem> busSystemMock = new Mock<IBusSystem>();

NotificationService sut = new NotificationService(
usersRepositoryMock.Object,
subnoscriptionsServiceMock.Object,
dataBaseServiceMock.Object,
busSystemMock.Object);


Я жесть как не люблю вот эту часть с объявлением моков, особенно, если учесть, что все из них нужно настраивать. А мне не всегда нужно настраивать их все.

Так вот MockRepository решает эту проблему так:
MockRepository mockRepository = new MockRepository(MockBehavior.Loose);
NotificationService sut = new NotificationService(
mockRepository.Create<IUsersRepository>().Object,
mockRepository.Create<ISubnoscriptionsService>().Object,
mockRepository.Create<IDataBaseService>().Object,
mockRepository.Create<IBusSystem>().Object);


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

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

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

MockRepository mockRepository = new MockRepository(MockBehavior.Loose);
NotificationService sut = new NotificationService(
mockRepository.Create<IUsersRepository>(MockBehavior.
Strict).Object,
mockRepository.Create<ISubnoscriptionsService>().Object,
mockRepository.Create<IDataBaseService>().Object,
mockRepository.Create<IBusSystem>().Object);

sut.Notify();

mockRepository.VerifyAll();
mockRepository.VerifyNoOtherCalls();


Два последних метода проверяют следующее - первый смотрит, что все засетапленные методы были вызваны, а второй проверяет, что не было сделано лишних вызовов, кроме тех, что вы провреяете с помощью Verify.

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

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

Быстрые релизы. Это не совсем технология, а скорее подход, но тем не менее, без него нет смысла задумываться вообще ни о чем. Суть подхода в том, что все действия по увеличению эффективности вашей работы со стороны руководства должны быть направлены на уменьшение времени выкатки кода на прод. Для такой компании не существует приемлемого времени релиза, кроме нулевого. Если в вашей компании считается нормой срок релиза в 2 недели, это тревожный звоночек. Если же компания понимает, что уменьшение времени релиза ведет к времени сокращения обратной связи и понимает, какие профиты это дает, то все ок и в такой компании вы неизбежно обнаружите следующую крутую технологию

Load Testing. Тут все просто, если в компании есть нагрузочное тестирование, значит у комапнии есть хайлоад. Где есть хайлоад, там есть новые вызовы. Раньше нагрузочное тестирование было уделом спецаильно обученных людей. Сегодня с помощью k6 к нему сожет приобщиться в рамках самообучения любой разработчик

Feature Toggles. Это также скорее подход, но за нима порой стоят вполне конкретные технологии. Самый топовый уровень здесь, это когда вы можете протестировать фичу прямо на своей дебажной сборке. Ну или раскатить фичу на 10% пользователей, чтобы провести A/B тест. Наверное вас триггернуло про дебаг на проде. Наверное вы подумали, что это же "Хуяк хуяк и в продакшен!" ОЙ как плохо и все такое. Но следующая технология позволяет так делать

Chaos Testing. Пантеон богов. Если у вас в работе применяется такой подход, то вы уже должны писать статьи о том, как делали, как применяли, где вам помогло, а где нет. Зачастую, присутствие  Chaos Testing в стеке технологий приводит к тому, что ваш прод становится неуязвим не то что к дебажным сборкам, а вообще к падениям датацентров из-за отключения электричества.

Contract Testing. Если дебажные сборки вам не страшны, то каскадные отказы из-за релиза несовместимых апи все еще могут быть проблемой. Но и для нее есть решение в виде контрактного тестирования. Просто представьте как круто, когда вы удаляете поле и вам не нужно думать о том, что в каком-то далеком проекте, о котором вы даже не знали, словмается совместимость. Вам просто придет уведомление об этом еще до катастрофы и вы сможете избежать подобных проблем. К сожалению контрактное тестирование жутко сложная в поддержке технология, поэтому доступна единицам, ка кв принципе и Chaos Testing
Почему вам следует перестать ругать чужой код?
Когда я только начал свой путь в разработке, практически сразу заметил, что тут принято ругать решения своих коллег. Мой тимлид открыл солюшен и практически сразу наткнулся на какой-то лютый дженерик, после чего сказал "Пиздец, что это?!" Эту фразу в различных вариациях я слышу практически каждый раз, когда сажусь с кем-то писать код. Да ее уже можно сделать звуком по умолчанию при открытии файла в любой IDE, чтобы людям уже не приходилось переутруждать себя!

Часто в программистских чатах так или иначе всплывают мемы типа такого: "Когда увидел код, который ты написал год назад." Эти мемасы из года в год собирают кучу лайков.

И это плохо, потому что именно поэтому вы перестаете развиваться.

Год назад я заметил, что мне стало тяжело писать код. Каждая строчка, каждое название переменной и метода дается с огромным трудом. Заношу руку над клавиатурой, а в голове слышу: "пиздец, что это? Ты вообще знал, что это может привести к дедлоку? Ты знаешь как выглядит дедлок?"

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

В один из дней я услышал от коллеги: "Хочется написать такой код, чтобы пришли следующие разработчики исказали, что код написан классно" Это меня и триггернуло. В этот момент я попытался вспомнить, бывало ли в моей жизни вообще так, чтобы я пришел куда-то и сказал, "ну да, этот код хороший" Или чтобы мои коллеги похвалили чей-то код. Возможно и были, но это были исключения подтверждающие правило. А правило простое: увидел чужой код – сказал "Пиздец, что это?"

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

И есть только один способ разорвать этот порочный круг – перестать ругать чужой код.

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

А дальше произошло что-то вроде перелома ошибки выжившего! Когда вы постоянно ругаете чужой код, то подмечаете в нем только плохие решения, и не замечаете хорошие. Улавливаете суть?
Теперь после собеса вы сможете достоверно проверить ответ на вопрос "Во что разворачивается foreach"

Воу воу воу! Я очень давно хотел найти программу, которая показала бы результат AOT компиляции. Так как на собесах часто спрашивают тупые вопросы типа "Во что разворачивается (введите название любой херни)"

И наконец я узнал о такой охренительной штуке, как sharplab.io Это онлайн-компилятор, который... позволяет сделать то что я и хотел – посмотреть результат AOT компиляции!

Теперь после собеса вы сможете достоверно проверить ответ на вопрос "Во что разворачивается foreach".

Надеялся, что есть еще какой-нибудь плагин для райдера или для VS Code, но такого нет. Но самое главное, что приложуха опенсорсная, так что любой может написать плагин если захочет. https://github.com/ashmind/SharpLab
#инструменты
👍1
Понемногу старамеся подступиться к хаос тестингу в компании и в прошедшую пятницу попробовали Simmy: https://github.com/Polly-Contrib/Simmy

Это библиотечка от создателей Polly, которая создает рандомные падения в вашем приложении. Стратегии падения разные и совместимы со стратегиями из Polly. То есть если вы хотите, чтобы ваше приложение выкидывало сообщение об ошибке каждые 5 секунд со все возрастающей частотой, то вы можете сделать это с помощью Simmy.

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

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

Возможно создатели Simmy тоже это поняли, потому что библиотека не развивается уже больше года. А может Simmy уже идеален?🙂
Своя консольная тулза за 5 шагов

Открыл одну крутую тему! Вы знали как делать консольные тулзы на C#? Я вот не знал, а это оказывается охрененно просто:

1) Создаем консолькую приложуху (в моем случае она просто выводит мне мое имя и текущую дату)
2) В .csproj добавляем
<PackAsTool>true</PackAsTool>
<ToolCommandName>myname</ToolCommandName>
<PackageOutputPath>./nupkg</PackageOutputPath>
3) Делаем dotnet pack
4) Переходим в папку с собранным пакетом cd MyName.Cli
5) Запускаем установку dotnet tool install --global --add-source ./nupkg myname.cli

Все готово!

Можно вызывать myname в консоли!

Для macOS нужно будет еще выполнить пару действий, которые появятся в консоли после 4-го шага.

Для продвинутых: это знание полезно не только, чтобы делать консольные приколюхи для себя, но и для кастомных шагов в Github Actions.
Экономия до 40% на сериализации. Не кликбейт

Когда в .net 5 вышли Source Generators я честно был восхищен, но был нюанс 🙂 Типа, круто, что мы теперь в компилятор что-то подпихнуть можем, но нахера?))) Так вот, .net 6 так сказать прояснил ситуацию для тупых, добавив фичу JsonSerializerContext.

Поговаривают, с ее использованием можно поднять производительность json-сериализации до 40%. Сильно ли нужно заморочиться, чтобы вкрутить у себя? Я и сам в шоке, но оказывается – нет!

Есть у вас класс Person, который вы хотите передать по Интернетам:

public class Person
{
public string FirstName { get; set; } = default!;
public string SecondName { get; set; } = default! ;
}

И раньше вы делали так:

var person = new Person
{
FirstName = "Mr",
SecondName = "Anderson"
};

var personText = JsonSerializer.Serialize(person);

А потом передавали уже в виде байтиков сериализованную персону.

Что нужно сделать, чтобы ускорить эту строку на 40%? Создать вот такой partial класс:

[JsonSerializable(typeof(Person))]
public partial class PersonJsonContext: JsonSerializerContext
{
}

И в строчке прописать один параметр PersonJsonContext.Default.Person:

var personText = JsonSerializer.Serialize(person, PersonJsonContext.Default.Person);

Один параметр, Карл! Ради сорока процентов производительности! Мне кажется, что это стоит того напряжения, учитывая, сколько сериализаций содержит твое web-приложение!
Начни бенчмаркать уже сегодня!
У вас есть уникальная возможность обвинить меня в пиздобольстве!

Постом выше я писал, что вы можете сэкономить до 40% на сериализации. И кажется, что под таким постом нужны пруфы, Билли! Нам нужны пруфы!

Так вот, получите пруфы сами! Для этого вам просто нужно поставить темплейт проекта с Benchmark .NET

dotnet new -i BenchmarkDotNet.Templates

Создать проект по темплейту и в классе Benchmark.cs в методе Scenario1 вызовите старый метод, а в Scenario2 новый.

Никогда еще бенчмаркать не было так легко, как с темплейтами для бенчмарка! С этими темплейтами бенчмаркать может даже моя бабуля!
Забудьте о классах, у нас есть records! :
Главное, что подкупает в рекордах это возможность сделать так:

var neoPerson = person with { FullName = "Neo" };

Чем это круто? Тем, что neoPerson будет совершенно новым объектом, со своей ссылкой, но с одним измененным свойством. Но самое главное – это вообще единственный способ поменять что-то в рекорде. Это дает простой способ сделать класс (а рекорды под капотом это обычные классы) иммутабельным.

🕔 Раньше, если вы хотели добиться от класса подобной иммутабельности, то вы бы поели говна из копипасты. Типа:

var personClass = new PersonClass("Laurence Fishburne", new DateOnly(0,1,1));

var morpheusPersonClass = new PersonClass("Morpheus", personClass.DateOfBirth);

И, чем больше полей, тем больше копипасты. Либо пришлось бы придумывать какие-то методы, которые бы по-умному клонировали объект. В общем дело было швах, а теперь иммутабельность можно нести в массы!

🛡Как это применить в проде?
Если у вас есть вот такой класс, к примеру

public class PersonClass
{
public PersonClass(string fullName, DateOnly dateOfBirth)
{
FullName = fullName;
DateOfBirth = dateOfBirth;
}

public string FullName { get; }
public DateOnly DateOfBirth { get; }
}

Можете смело заменить его на

public record Person(string FullName, DateOnly DateOfBirth);

Нужно будет поправить некоторые места в проекте, но по одному классу в день и рано или поздно весь проект переползет на рекорды.
Попробуй сейчас и используй вечно!
💻 Попробуйте скопировать себе в Program.cs вот эти три строки (разумеется с включенным С# 10):

var neo = new Person("Mr Anderson", new DateOnly(1980, 2, 1));
Console.WriteLine(neo);
public record Person(string FullName, DateOnly DateOfBirth);

🧐 Сейчас будет вопрос, как на собесах: что же выведется в консоли?

Если бы это был просто поганый класс, то вывелось бы тупое и бесполезное:
Person

Но что вышло у нас? Правильно!
Person { FullName = Mr Anderson, DateOfBirth = 02/01/1980 }

Супер-полезное описание со значением всех полей! Почему так? Потому что рекорды за вас грамотно определяют метод ToString() так чтобы он выводил полезную информацию, а не просто тупое имя типа.


🤩Пользуйся рЕкордами! Пользуйся рЕкордами мазафака!🤩
Работаешь? А вот и не надо, ведь сегодня выходной!
Отдыхай с чистой совестью, а работа не волк – всегда можно найти и получше 😈
🛑 Как же это странно. Я тут просто хотел писать с матерком и шуточками про C#, а тут такая ситуация, что уже не до шуточек. Но я сделаю так. Напишу здесь, что я против войны с Украиной! И продолжу постить с шуточками и матерком про C#. Не потому что мне все равно, а потому что мне вообще не все равно, и я считаю, что продолжая постить можно принести хоть немножко спокойствия, а вместе с ним и пользы. Всем мира!

Вот пост, написанный до всех этих чудовищных событий.

В ASP.NЕT 6.0 больше не нужен Startup.cs!

Готовил для вас очередной пост про record’ы (trollface), как вдруг наткнулся на отсутствие файла Startup.cs в новых темплейтах ASP.NET.

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

🖇Вот такая статья об этом подробно рассказывает и показывает, как все поменялось между ASP.NET 5.0 и ASP.NET 6.0: https://docs.microsoft.com/en-us/aspnet/core/migration/50-to-60-samples?view=aspnetcore-6.0

Чтобы вам не открывать лишний раз доку (и Жоку). Покажу, как теперь это будет выглядеть:
Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddScoped<SomeService>();

builder.Services.AddControllers();

builder.Configuration.AddJsonFile("appsettings.json");

var app = builder.Build();

app.MapControllers();

app.Run();


📍 Ну что же “Press F to pay respect” нашему Startup.cs. Думаю, скучать не будем.

Есть у этого пожалуй один минус – придется вручную выпиливать этот файл, если хотите переехать на новую версию билдера.
🛑 Писал это до войны. Нет войне!

Продолжим про record’ы. Что они такое внутри?
Народ в общем пока стремается record’ов. И это понятно, если думать об этом, как о какой-то новой сущности в C#. Но так думать не надо, потому что под капотом это обычный класс.

Тут понятно, что каждый умник может сказать “под капотом это обычный …”. Вот уж реально заебали эти умники со своим “под капотом”. Давайте просто посмотрим, как sharplab.io декомпильнет вот такую строку:

public record Person(string FullName, DateOnly DateOfBirth);

Результатами можно насладиться по ссылке https://vk.cc/cbcKK8. Но вот главное:

1️⃣ За вас реализованы свойства с приватными сеттерами и инитами, а также конструктор, в котором эти свойства инициализированы.
2️⃣ Реализован интерфейс IEquatable
3️⃣ Есть метод Deconstruct, который позволяет писать

var neoPerson = person with { FullName = "Neo" };

4️⃣ И конечно грамотно переопределен метод ToString(), про что я подробно рассказывал в следующем микропосте

Итого: Одна, всего одна! строка экономит вам около 135 строк кода и кучу нервов!
Рубрика: задачи, которые не дают на собесах.
Вот вам задача: Есть сайт, на котором люди пишут код на C#, решая заданные алгоритмические задачи. Ваша задача, компилировать этот код на сервере, запускать на нем тесты и выдавать результат компиляции и прогона тестов. Как вы будете это делать? Как будете хранить условия задач? Как будете проверят результат выполнения?

Ответили? Немного усложним задачу: что, если появился еще один язык программирования? Как будете строить архитектуру такого решения?

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

😵‍💫 Но ребята из Хекслета сделали все грациознее, показав, кто тут батя, и, где раки зимуют:
1. Они сохраняют файлы с кодом на сервак, как есть.
2. Потом эту папку подтягивает к докер-образу, в котором лежит компилятор, рантайм и прочее. Также там внутри лежат юнит тесты, которые проверяют решение
3. Докер-образ запускает все вышеозначенное на пользовательской папке и выдает ответ!
4. Сами задачи хранятся в обычном гитхаб-репозитории (вот в этом https://github.com/hexlet-basics), что избавляет нас от необходимости вообще создавать админку для менеджмента задач для платформы!

🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥О-ХУ-ЕТЬ! 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥

Вот как можно было такое придумать? Бывалые может скажут: "Ну и что тут такого?" А я просто испытаю радость неофита при виде такого решения!

Вот тут есть статья Жени Васильева, нашего супер-техлида, из которой я достал эту информацию: https://habr.com/ru/company/hexlet/blog/652773/

Ну и, разумеется, ссылка на сорцы
https://github.com/hexlet-basics/hexlet-basics/tree/b32b228be35bed12615553515b1df05195d88e4b
😁1
Мутантное тестирование! Протестируй свои тесты, детка.
Блин, меня просто распирает от крутости этой фичи. В общем, если вы пишете по TDD, то знаете, что там самое главное – написать в итоге такие тесты, чтобы они были точно корректными.

А как, блин, понять, что они корректные? Раньше так мог только гуру разработки. Так сказать, “с высоты своего опыта”. А сейчас появился инструмент под названием Stryker.

Очень забавное название, учитывая что это тулза для мутантного тестирования 😊
Для непосвященных Страйкер – это чувак, который заделал Росомаху. Не в смысле, как батя сына, а в смысле, как ученый мутанта с металлическими костями.

Ладно, я как всегда растекаюсь мыслей по древу (пиздобольствую), вот суть.
Эта штука берет код ваших тестов, видит метод, который ваши тесты тестируют и начинает этот метод ломать разными способами. Если при поломке кода все ваши тесты упали, то все ок, значит вы учли все сценарии. Но если нет… Значит вы дятел 🦆Шучу, не дятел 😎

Рассмотрим пример. У вас есть вот такой метод:
public static int Subtract(int a, int b)
{
return a - b;
}

Вы написали вот такой тест для него:
[Test]
[TestCase(5,5,0)]
public void Test1(int a, int b, int expected)
{
var result = TryStrykerCalc.Subtract(a, b);

Assert.AreEqual(expected, result);
}

Ну очевидно, что тут не все тест-кейсы предусмотрены, но давайте посмотрим, что скажет страйкер.

1. Ставим его себе на тачку: dotnet tool install dotnet-stryker --global

2. Запускаем в папке с .csproj файлом теста: dotnet stryker

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

4. Понимаем, что нужно накинуть еще один тест-кейс

5. Профит! 💸

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

Короче, попробуйте запустить у себя на проекте Страйкера и посмотрите, что произойдет. Всего две строки и вы знаете, где маленько проебались!
Убойный релиз: MediatR теперь могет в AsyncEnumerable
Не особо давно у MediatR случился релизец, в котором библиотеку научили работать с AsyncEnumerable и это довольно охуенно, друзья.
https://jimmybogard.com/mediatr-10-0-released/

Вообще я фан IAsyncEnumerable и всей этой фичи, поскольку она реально улучшает работу вашего приложения с потоками.

👎А еще всегда удобно делать не вот это говно:

List<object> result = new List<object>();
foreach (var value in someCrazyList)
{
var shit = await RequestShitFromDB(value);
result.Add(shit);
}

return result;

👍А просто и лаконично написать красоту:

foreach (var value in someCrazyList)
{
yield return await RequestShitFromDB(value);
}

Но перейдем к сути релиза.

Раньше вы не могли вернуть IAsyncEnumerable в хэндлере, то есть сделать так:

public async IAsyncEnumerable<WeatherResponse> Handle(WeatherRequest request, CancellationToken cancellationToken)

Но после 6-го января 2022 года (видать на новогодних Джимми Боггарт не оливьеху наворачивал! 🥗) Вы можете делать асинхронные энумераторы в хэндлерах! Выглядит оно вот так. Вид от лица хэндлера, так сказать:

public async IAsyncEnumerable<WeatherResponse> Handle(WeatherRequest request, CancellationToken cancellationToken)
{
for (int i = 0; i < request.DaysCount; i++)
{
// imitation of DB
await Task.Delay(100, cancellationToken);
yield return new WeatherResponse
{
Date = DateTime.Now.AddDays(i),
TemperatureC = Random.Shared.Next(-20, 55)
};
}
}

А вот как это выглядит в контроллере:

[HttpGet(Name = "GetWeatherForecast")]
public IAsyncEnumerable<WeatherResponse> Get([FromQuery]WeatherRequest request)
{
return _mediator.CreateStream(request);
}

Не знаю по каким причинам, но меня трясет от страха, при виде слова Stream. Ни малейшего понятия, почему так. Возможно из-за собесов (ставь лайк если тоже заебали заумные собесы). Но тут можно не бояться, ибо метод CreateStream это тот же Send, только с другим возвращаемым типом. Думаю, не нужно пояснять с каким (IAsyncEnumerable).

🅰️ Итого: Вы спросите меня, а что делать-то, Димас? А вот что! Если у вас в приложении есть MediatR и где-то вы собираете рузльтат, прежде чем отдать его, то выпиливайте эту срань к чертям и меняйте на прекрасный AsyncEnumerable!

PS. А ты поставил, кстати, звезду медиатру на гитхабе? Если нет, то помни, Джимми старался на новогодних ради тебя, возможно не ел оливьеху. Поставь ему звезду, не постесняйся, а он тебе еще апдейтов накидает 🤩 https://github.com/jbogard/MediatR
Вопросы из зала: Могу ли я использовать record’ы для описания конфигурации?
🩳 Коротко: да, но не так просто, ибо у них нет конструктора по умолчанию.

Когда видишь рекорды в первый раз (да и во второй), то задаешься вопросом, а могу ли я использовать для конфигурации?

🤟Это, детектив, правильный вопрос, ведь написать вот так:

public record BotConfiguration(string BotToken, string HostAddress);

💩Гораздо удобнее, чем вот так:

public class BotConfiguration
{
public string BotToken { get; set; }
public string HostAddress { get; set; }
}

Но вот неудобная правда. Если просто попробовать зарегать такую конфигурацию в ASP.NET:

builder.Services.Configure<BotConfiguration>(
builder.Configuration.GetSection("BotConfiguration"));

То при попытке достать конфигурацию:

var botConfig = builder.Configuration
.GetSection("BotConfiguration")
.Get<BotConfiguration>();

😬 Рантайм (угадай заблюренное слово) опиздюлит вас ошибкой:
System.InvalidOperationException: Cannot create instance of type 'TestRec.BotConfiguration' because it is missing a public parameterless constructor.

И в принципе будет прав. Но! Кого вообще устроит такая ситуация? Меня точно не устроит! Поэтому я полез искать самые извращенные решения этой проблемы и нашел, пожалуй не самое извращенное, но доступное только с версии .NET 6.0

Для этого вам понадобится пакет DevTrends.ConfigurationExtensions, у которого всего три звезды на гитхабе (включая мою, разумеется): https://github.com/devtrends-org/configuration-extensions

Этот пакет позволяет сделать такой финт ушами. Зарегать конфигурацию как синглтон:

builder.Services.AddSingleton(builder.Configuration.Bind<BotConfiguration>());

И использовать как синглтон:

var service = app.Services.GetRequiredService<BotConfiguration>();

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

🅰️Итого: я бы сказал, что пока говорить о конфигах на рекордах скорее рановато, но для пет-проектов под пиво 🍻 попрёт.

P.S. Ну и, разумеется, по старой-доброй традиции, я предложу вам втащить репозиторию. Для вас это клик, а для Paul Hiles это приятная неожиданность.
Как писать интеграционные тесты без InMemoryDB или SQLite?
Этот крутой вопрос поднял мой добрый коллега Паша Кириллин. И я в очередной раз убедился, насколько же я дуболом 🤖.

💩 Обычно, если я хочу написать тесты, которым зачем-то нужна реальная база, то я втупую напишу docker-compose.yaml с конфигурацией этой базы. И каждый раз при запуске дергаю docker compose up. В общем, довольно грубый метод.

А вот Паша посидел разок и задумался, а как бы это сделать автоматизированно, чтобы не пришлось делать docker compose up? И сразу нашел решение в виде такого репозитория: https://github.com/HofmeisterAn/dotnet-testcontainers

Вы только посмотрите, как это работает:

using System.Threading.Tasks;
using DotNet.Testcontainers.Containers.Builders;
using DotNet.Testcontainers.Containers.Configurations.Databases;
using DotNet.Testcontainers.Containers.Modules.Abstractions;
using DotNet.Testcontainers.Containers.Modules.Databases;
using MySql.Data.MySqlClient;
using NUnit.Framework;

namespace TryTestContainers;

public sealed class UnitTest1
{
private readonly TestcontainerDatabase _testcontainers = new TestcontainersBuilder<MySqlTestcontainer>()
.WithDatabase(new MySqlTestcontainerConfiguration
{
Database = "db",
Username = "mysql",
Password = "mysql",
})
.Build();

[SetUp]
public async Task SetUp()
{
await _testcontainers.StartAsync();
}

[TearDown]
public async Task TearDown()
{
await _testcontainers.DisposeAsync().AsTask();
}

[Test]
public void TestMethod()
{
using MySqlConnection connection = new MySqlConnection(_testcontainers.ConnectionString);
using var command = new MySqlCommand();
connection.Open();
command.Connection = connection;
command.CommandText = "SELECT 1";
command.ExecuteReader();
}
}

Если вы, как и я, пропускаете большие блоки кода при чтении (да-да, ты не один такой ленивый, нас как минимум двое 👬), то вот вам текстовое описание происходящего:

1. В переменной _testcontainers мы создаем конфигурацию контейнера, также как мы это обычно делаем в docker-compose.yaml
2. В методе SetUp мы запускаем эти контейнеры. Они запускаются в вашем локальном Docker
3. В методе TestMethod мы подключаемся к базе данных и делаем запрос.
4. В методе TearDown мы их гасим, после всех тестов

Звучит достойно! Но еще более достойно звучит тот факт, что таким же макаром вы можете поднимать RabbitMq, Kafka и еще всякие штуки!

🅰️Итого: Если вам хочется делать тесты на уровне классов, которые ходят в инфраструктурные сервисы, вы можете использовать эту штуку. Но кажется надо посмотреть, как такие тесты поднимать в Github Actions? Как запускать их в параллель? Я попробую ответить на эти вопросы завтра, а вам пореомендую не сцать и тоже попробовать эту штуку!

P.S. Естественно, я въебал звезду на гитхабе этому проекту. И ты тоже не сиди, пойди и въеби звезду! У проекта уже 994 , так что это серьезно!