StepOne | Степан Минин – Telegram
StepOne | Степан Минин
3.42K subscribers
245 photos
35 videos
6 files
310 links
StepOne by Степан Минин @ststphn

Твой первый шаг к успеху в программировании

Закрытый тг канал https://news.1rj.ru/str/tribute/app?startapp=slOA

По вопросам рекламы @Spiral_Yuri

Ютуб https://www.youtube.com/@steponeit
Download Telegram
Как изменить таймаут для конкретного запроса в HttpClient?

Одной из лучших практик по работе с HttpClient в C# считается переиспользование одного экземпляра клиента для множества запросов. Как минимум во избежание port exhaustion.

Однако, возникают ситуации, когда для разных запросов требуется разное поведение клиента. Например, разные таймауты.

Проблема в том, что HttpClient.Timeout устанавливается единожды, во время создания клиента. И несмотря на наличие public set'тера, это значение не может быть изменено впоследствии. Любые попытки пресекаются выбрасыванием InvalidOperationException.

Но я бы не писал этот пост, если бы не существовало решения проблемы. А решение довольно простое:
TimeSpan timeout = GetMyTimeout();
using (var tokenSource = new CancellationTokenSource(timeout))
{
var response = await httpClient.GetAsync(uri, tokenSource.Token);
HandleResponse(response);
}


Такое решение можно не только использовать "в лоб", но и обернуть в пайплайн из DelegatingHandler'ов. Для того чтобы оно работало, потребуется убедиться в двух вещах:

1️⃣ Пользовательский таймаут меньше того, что установлен в HttpClient.Timeout

2️⃣ Пользовательский таймаут валиден. Проще говоря, время ожидания больше 0 секунд.
👏8👍4🔥2🤩1
.NET не идеален

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

Это влияет и на пользовательский опыт, и на финансовые показатели продукта, его конкурентоспособность, репутацию.

Но даже проверенные временем платформы разработки не могут гарантировать ожидаемого поведения на 100% при соблюдении всех правил.

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

Также, автор доклада ведёт канал @epeshkblog, где можно прочитать про готовящееся им решение проблемы и множество других интересных аспектов платформы, о которых вы даже не догадывались.
👍6🔥1👏1
Обобщай это, обобщай то

В новой статье вновь аккуратно стёр границы между абстрактной алгеброй и объектно-ориентированным программированием.

А ещё подробно зарылся внутрь System.Numerics и Generic Math.

Жду вас на хабре с плюсом!

#хабр
🔥7👍3🤩1
Забавный факт, но последняя статья получила один единственный минус по причине "ничего не понял после прочтения" 😁
😁13
Почему C#?

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

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

Конечно, это не единственный язык, который я знаю, причём хорошо. Но в своё время меня привлекла концепция C#.

В далёкие времена его позиционировали как работу над ошибками Java. И если вдаться в детали, то это соответствует действительности на все 100%. Сейчас, уже Java берёт многое от своего последователя.

В общем, если рассматривать шарпы не как ЯП, а как продукт, то у него крутое честное и продающее позиционирование.

Поделитесь в комментариях историей своего выбора ЯП
👍9👏2
Пока начинаются майские праздники, на хабре вышла публикация про IDE языка C#, выполняющая запросы LINQ для таблиц Excel.

Отличная статья, отличная разработка. Но, хочу обратить внимание, что это перевод, и автор разработки не из РФ. Могут быть нюансы с использованием и поддержкой.
👍6🤯3🥰1
Многоликая регистрация в стандартном контейнере

Допустим, у нас есть некоторый класс, который реализует более одного интерфейса:

public interface IBar {}
public interface IFoo {}

public class FooBar : IFoo, IBar {}



Наша задача зарегистировать экземпляр FooBar таким образом, чтобы при внедрении зависимостей IBar и IFoo использовался один и тот же объект.

Попытавшись совершить регистрацию наивным путём, мы потерпим неудачу, зависимости будут ссылаться на разные экземпляры FooBar:

services.AddSingleton<IFoo, FooBar>();
services.AddSingleton<IBar, FooBar>();



Но проблема вполне решаема, достаточно лишь зарегистрировать вначале экземпляр FooBar и затем ссылаться на него:

services.AddSingleton<FooBar>();
services.AddSingleton<IFoo>(x => x.GetRequiredService<FooBar>());
services.AddSingleton<IBar>(x => x.GetRequiredService<FooBar>());
🔥19👍9🤯1
Всем доброе утро!

Я часто прохожу собеседования, на одном из последних встретил интересную задачу.

Решение выложу после 10 неудачных попыток в комментариях.

Надо определить, что будет выведено на экране. Допустим, у изначального потока id равен 1, а при смене потока ему можно присвоить любой другой (2, 3, 10, 77 и так далее).


public static async Task<int> GetValue1()
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId); // ?
return await Task.FromResult(100500);
}

public static async Task<int> GetValue2()
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId); // ?
return 500100;
}


public static Task<int> GetValue3()
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId); // ?
return Task.Run(() => 500100);
}

static async Task Main(string[] args)
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId); // 1
var val1 = await GetValue1();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId); // ?
var val2 = await GetValue2();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId); // ?
var val3 = await GetValue3();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId); // ?
}
🤯6🤔4👍211
StepOne | Степан Минин
Всем доброе утро! Я часто прохожу собеседования, на одном из последних встретил интересную задачу. Решение выложу после 10 неудачных попыток в комментариях. Надо определить, что будет выведено на экране. Допустим, у изначального потока id равен 1, а при…
Из-за разных обстоятельств чуть-чуть задержался с выкладыванием решения, но всё же в этом посте расставлю все точки над "i", как и обещал.

Но прежде, хочу выразить огромную радость по поводу того, какие подкованные у меня подписчики. В комментариях было много людей, ответивших правильно, причём сразу.
Затем, там началась интересная дискуссия, а далее вообще прислали уже новую задачу.
Для меня, как автора, работающего с аудиторией это большая победа, спасибо вам!

Итак, как уже было верно подмечено в комментах, программа выведет "1 1 1 1 1 <любое другое число>". Здесь не подразумевались какие-то дополнительные условия, только конфигурация по умолчанию.

Почему ответ именно такой?
Задача проверяет понимание того, в какой момент поток из ThreadPool действительно понадобится в случае await'а.

В методе GetValue2 всё очевидно, поскольку ключевого слова await не было, соответственно автомат компилятором не сгенерируется, и код метода останется неизменным.

С GetValue1 всё чуть интереснее. Кажется, что произойдут все манипуляции, ведь есть и задачка, и await. Но runtime умнее, чем мы думаем и он понимает, что Task.FromResult, ровно как и Task.CompletedTask, создаёт уже завершенную задачу, которую не требуется ожидать. Соотвественно, смена потока не требуется.

Ну а в случае GetValue3, конечно смена потока произойдёт. Несмотря на элементарность задачи, Task.Run уже возвращает запущенную, но не завершённую.
10👍1🔥1👏1🤩1
Внедрение конкретной реализации в стандартном контейнере

Допустим, у нас есть некоторый интерфейс, который имеет несколько реализаций:


public interface IDependency {}

public class DependencyImplOne : IDependency {}
public class DependencyImplTwo : IDependency {}


И мы хотим, используя стандартный DI контейнер .NET Core, внедрить в определённый сервис конкретную реализацию этого контракта.
То есть:


public class BarService : IBarService
{
public BarService(IDependency dependency) // DependencyImplOne
{
}
}

public class BazService : IBazService
{
public BazService(IDependency dependency) // DependencyImplTwo
{
}
}


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

Здесь есть несколько вариантов решения:

1⃣ Создание фабрики

2⃣ Создание Service Delegate

3⃣ Внедрение коллекции зависимостей IEnumerable<IDependency> с её последующим перебором

4⃣ Явная регистрация, например: services.AddTransient<IBazService, BazService>(_ => new BazService(new DependencyImplTwo()))

Конечно, указанные решения не идеальные.
В такой ситуации стоит посмотреть в сторону других контейнеров.
Ставьте реакции на этот пост, если хотите, чтобы я о них написал.
🔥32👍8🤩1
StepOne | Степан Минин
Внедрение конкретной реализации в стандартном контейнере Допустим, у нас есть некоторый интерфейс, который имеет несколько реализаций: public interface IDependency {} public class DependencyImplOne : IDependency {} public class DependencyImplTwo : IDependency…
Simple Injector. Условная регистрация

Возможность внедрять конкретную реализацию зависимости согласно определённому контексту в этом контейнере реализована с помощью метода RegisterConditional:

container.RegisterConditional<ILogger, NullLogger>(
c => c.Consumer.ImplementationType ==
typeof(HomeController));

container.RegisterConditional<ILogger, FileLogger>(
c => c.Consumer.ImplementationType ==
typeof(UsersController));

container.RegisterConditional<ILogger, DatabaseLogger>(c => !c.Handled);


Из приведённого примера видно, что условная регистрация позволяет настроить поставку зависимости на основе определения типа потребителя.

То есть, HomeController получит NullLogger, UsersController получит FileLogger, а все остальные потребители ILogger получат DatabaseLogger.
7👍2🔥2
StepOne | Степан Минин
Внедрение конкретной реализации в стандартном контейнере Допустим, у нас есть некоторый интерфейс, который имеет несколько реализаций: public interface IDependency {} public class DependencyImplOne : IDependency {} public class DependencyImplTwo : IDependency…
Castle Windsor. Явное указание зависимостей

Возвращаясь к нашему примеру с логгером, допустим, что у сервиса ILogger есть две реализации: некий стандартный Logger и безопасный SecureLogger, который требуется использовать в некотором сервисе TransactionProcessingEngine.

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

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


container.Register(
Component.For<ITransactionProcessingEngine>().ImplementedBy<TransactionProcessingEngine>()
.DependsOn(Dependency.OnComponent<ILogger, SecureLogger>())
);
👍101👏1
Autofac. Именованные сервисы

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

Например, у нас есть сервис IDisplay, отображающий какие-то произведения искусства IArtwork.
Чтобы указать, что мы хотим внедрить конкретную реализацию MyPainting, можно использовать атрибут KeyFilterAttribute.
По указанному ключу, он проведёт фильтрацию и выберет нужную зависимость.

Пример:


public class ArtDisplay : IDisplay
{
public ArtDisplay([KeyFilter("MyPainting")] IArtwork art) { ... }
}

// ...

var builder = new ContainerBuilder();

builder.RegisterType<MyPainting>().Keyed<IArtwork>("MyPainting");
builder.RegisterType<ArtDisplay>().As<IDisplay>().WithAttributeFiltering();

// ...
var container = builder.Build();
7👍1👏1
С Днём Победы!

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

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

Орден Славы он получил за то, что, выполняя приказ командования 24 декабря 1943 года, под вражеским обстрелом 13 раз устранял порванную связь с наблюдательными пунктами батарей дивизиона.
Дважды подвергаясь автоматному обстрелу, он сумел смотать 3 километра вражеского кабеля и, использовав его, установил связь с передовыми наблюдательными пунктами.
Чем обеспечил своевременное открытие огня дивизиону.

Смог бы так я?
Большой вопрос.
Хотя в мирной жизни прадед работал начальником АХО завода «Калибр».

Светлая память, с Праздником!
26🤡11👍4👏2🕊1🥱1
StepOne | Степан Минин
Внедрение конкретной реализации в стандартном контейнере Допустим, у нас есть некоторый интерфейс, который имеет несколько реализаций: public interface IDependency {} public class DependencyImplOne : IDependency {} public class DependencyImplTwo : IDependency…
StructureMap. Настройка конструктора

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

Например, у нас есть сервис для отправки сообщений IMessageService, который реализуют соответственно SmsService и EmailService. И есть некоторые сценарии, в которых нужно использовать разные имплементации, тогда конфигурация будет выглядеть примерно следующим образом:

var container = new Container(x => {
x.For<FooScenario>().Use<FooScenario>()
.Ctor<IMessageService>("messageService")
.Is<SmsService>();
x.For<BarScenario>().Use<BarScenario>()
.Ctor<IMessageService>("messageService")
.Is<EmailService>();
});

// ...

public class FooScenario
{
public FooScenario(IMessageService messageService) // sms
}

// ...

public class BarScenario
{
public BarScenario(IMessageService messageService) // email
}


Какие-то ещё контейнеры уже не вижу смысла рассматривать, поскольку их подходы буду похожи на то, о чём я уже рассказал. Также, через какое-то время по совету подписчика сделаю бенчмарк, а пока переключусь на другую тему.
👍32👏2🔥1🤩1👌1🕊1💯1
Анекдот, классический как книги дяди Боба:

Заходит однажды тестировщик в бар.
Забегает в бар.
Пролезает в бар.
Танцуя, проникает в бар.
Крадется в бар.
Врывается в бар.
Прыгает в бар.

Заказывает:
кружку пива,
2 кружки пива,
0 кружек пива,
999999999 кружек пива,
ящерицу в стакане,
–1 кружку пива,
qwerty кружек пива.

Первый реальный клиент заходит в бар и спрашивает, где туалет. Бар вспыхивает пламенем, все погибают.
😁30👍3🤩2🔥1
StepOne | Степан Минин
Многоликая регистрация в стандартном контейнере Допустим, у нас есть некоторый класс, который реализует более одного интерфейса: public interface IBar {} public interface IFoo {} public class FooBar : IFoo, IBar {} Наша задача зарегистировать экземпляр…
Пока не получается отойти от темы Dependency Injection на все 100%, как того хотелось бы.

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

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


var container = new Container(x =>
{
x.ForConcreteType<FooBar>().Configure.Singleton();

x.Forward<FooBar, IFoo>();
x.Forward<FooBar, IBar>();
});
👍5🤔3👏1🤯1
Константное время превыше всего!😁
😁27👍3🤩1
Если вы хотите получить более глубокие знания в области внедрения зависимостей в .NET, то рекомендую прочитать книгу

Dependency Injection in .NET, Mark Seemann

В ней представлены ключевые паттерны DI на чистом C#, объяснение принципа работы контейнеров и раскрывается вопрос интеграции со стандартными технологиями Microsoft.
👍13🔥42👌1
В настоящий момент работаю над докладом для одной известной технической конференции.

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

https://youtu.be/f5KeOR-XmVw
🔥132