.NET Разработчик
6.51K subscribers
427 photos
2 videos
14 files
2.04K links
Дневник сертифицированного .NET разработчика.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 1566.
Мониторинг и Производительность Entity Framework. Вопросы и Ответы.
Окончание
Начало

4. Профилирование взаимодействий с базой данных с помощью EF: что нужно и как это помогает?
В зависимости от вашей платформы и среды существует ряд бесплатных и коммерческих инструментов профилирования. От SQL Profiler (или соответствующего расширения для Azure Data Studio), Intellitrace и Application Insights до сторонних модулей, таких как EPFrof, OrmProfiler и MiniProfiler.
В некоторых случаях поддержка инструментов может быть добавлена без изменения кода вашего приложения, в других случаях требуется разместить в приложении небольшие хуки для перехвата. Кроме того, некоторые из более поздних инструментов облегчают нахождение строки кода, вызывающей запрос. Это может быть неоценимо при попытке выяснить, откуда взялся надоедливый плохо выполняющийся запрос.

5. Каковы плюсы и минусы работы без хранимых процедур?
Многие сторонники хранимых процедур рекламируют возможности, которые они привносят с точки зрения компиляции и кэширования запросов, а также параметризации. При этом они создавали динамические запросы внутри хранимых процедур, полностью нивелируя эти преимущества. EF генерирует запросы, которые можно кэшировать и которые гарантированно будут параметризованы (если только вы не используете FromSqlRaw). В результате использование EF может быть столь же эффективным и безопасным, как и хранимые процедуры.
Кроме того, вы получаете возможность менять провайдера базы данных, не переписывая все запросы. Или при добавлении столбца в таблицу, с EF вы добавляете только свойство в модель объекта. Для хранимых процедур также потребуется управление изменениями схемы таблиц и несколько изменений хранимых процедур (для операций CRUD).
Иногда необходимо использовать хранимые процедуры. Некоторые платформы предлагают определённые функции, которые LINQ может не поддерживать (подсказки запросов, иерархии, геопространственные данные, массовое копирование и т. д.), и для них может потребоваться нативный код. В более новых версиях EF устранены некоторые из этих пробелов. Если вы будете следовать правилу 80/20, используя EF, где это возможно, а где необходимо - хранимые процедуры или другие функции, специфичные для платформы, вы часто сможете найти подход, который хорошо работает для всех сценариев приложений.

6. С какими проблемами сталкиваются организации или команды при начале работы с EF?
Проблема чаще всего не технологическая, а человеческая. Для новых приложений внедрение новых технологий часто проще. Для существующих, которые создавались годами, часто приходится полагаться на то, что универсальная среда может быть такой же хорошей (или лучше), чем созданная командой за эти годы. Некоторые из таких приложений были специально созданы для передачи таких структур ADO, как DataSets и DataTables, в пользовательский интерфейс. Попытка обосновать рентабельность инвестиций в модернизацию уровня данных в таких обстоятельствах часто может быть затруднена.
Другая типичная проблема также сводится к доверию между инженерными командами и группами поддержки (часто между разработчиками и администраторами баз данных). Группы поддержки вызываются в 3 часа ночи, когда система даёт сбой из-за плохо выполняющегося запроса, который загружает ЦП сервера базы данных. Аргумент здесь в том, что, если бы запрос был абстрагирован в хранимую процедуру, они имели бы контроль над его изменением, а не заставляли разработчиков вносить изменения в код. Это можно смягчить, используя наборы тестов производительности с соответствующим охватом и просматривая сгенерированные запросы в течение циклов разработки. Этот подход требует общения между командами для установления большего доверия с течением времени.

Источник: https://visualstudiomagazine.com/articles/2023/04/05/ef-performance.aspx
👍6
День 1567. #ЗаметкиНаПолях
Проверяем Правильность Архитектуры ПО с Помощью Тестов
Архитектура ПО — это план того, как структурировать систему. Вы можете строго или не очень строго следовать ему. Но когда сроки поджимают, и вы начинаете срезать углы, созданная вами прекрасная программная архитектура рассыпается, как карточный домик.

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

Архитектурные тесты пишутся так же, как и любой модульный тест. Есть отличная библиотека для создания архитектурных тестов, в которой уже реализован шаблонный код - NetArchTest.Rules.

Отправной точкой для написания архитектурных тестов является статический класс Types, который можно использовать для загрузки набора типов. После этого их можно дополнительно отфильтровать. Некоторые из доступных методов фильтрации:
- ResideInNamespace
- AreClasses
- AreInterfaces
- HaveNameStartingWith
- HaveNameEndingWith

Наконец, вы можете написать правило, которое хотите применить, вызвав Should или ShouldNot и применив условие, которое хотите проверить. Вот пример проверки того, что все классы домена запечатаны:
var result = Types
.InAssembly(DomainAssembly)
.That()
.AreClasses()
.Should()
.BeSealed()
.GetResult();
Assert.True(result.IsSuccessful);

Архитектурные тесты особенно полезны для проверки соблюдения правил архитектуры ПО в многоуровневой архитектуре или модульном монолите.
- Домен не должен иметь никаких зависимостей:
var result = Types
.InAssembly(DomainAssembly)
.ShouldNot()
.HaveDependencyOnAny("Application", "Infrastructure")
.GetResult();
Assert.True(result.IsSuccessful);

- Приложение не должно зависеть от Инфраструктуры:
var result = Types
.InAssembly(AplicationAssembly)
.Should()
.NotHaveDependencyOn("Infrastructure")
.GetResult();
Assert.True(result.IsSuccessful);

- Инфраструктура должна зависеть от Приложения и Домена
Здесь правило немного сложнее, т.к. мы пишем не отрицательный, а положительный фильтр. Поэтому ограничим выборку более конкретным набором типов. Например, что все репозитории должны иметь зависимость от пространства имен Домена.
var result = Types
.InAssembly(InfrastructureAssembly)
.HaveNameEndingWith("Repository")
.Should()
.HaveDependencyOn("Domain")
.GetResult();
Assert.True(result.IsSuccessful);

Ещё один ценный вариант использования — проверка соблюдения правил проектирования. Правила проектирования более конкретны и сосредоточены на деталях реализации классов. Например:
- Сервисы должны быть internal-классами,
- Сущности и объекты-значения должны быть запечатаны,
- Контроллеры не могут напрямую зависеть от репозиториев:
var result = Types
.InAssembly(ApiAssembly)
.That()
.HaveNameEndingWith("Controller")
.ShouldNot()
.HaveDependencyOn("Infrastructure.Repositories")
.GetResult();
Assert.True(result.IsSuccessful);

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

Источник: https://www.milanjovanovic.tech/blog/enforcing-software-architecture-with-architecture-tests
👍24
День 1568. #Книги
Сегодня мне пришла книга, над переводом которой я работал совместно с сообществом DotNetRu. Кристиан Венц «Безопасность ASP.NET Core» — М.: ДМК Пресс, 2023.

Я уже участвовал в переводе нескольких книг, эта стала третьей. В этот раз, к сожалению, не получилось вычитать всё, как в случае с книгой "Entity Framework Core в действии", поэтому отложил её в очередь для чтения.
👍41
День 1569. #ЗаметкиНаПолях
Избегайте Распространения DbContext или IQueryable в Приложениях. Начало
Большинство приложений .NET используют EF Core и DbContext для доступа к данным, но удобство сопровождения может пострадать, если использование DbContext или производного от него IQueryable распространяется по всему приложению.

Интерфейсы IQueryable и IQueryable<T> в .NET позволяют описывать запросы в виде деревьев выражений, которые при выполнении преобразуются провайдером в запросы к БД. В EF IQueryable используется, чтобы создавать динамические SQL-запросы, что позволяет выполнять детальные и эффективные запросы к базе данных, а не извлекать больше данных, чем необходимо, в память приложения, а затем фильтровать результат в памяти.

Ниже создаётся набор заказов, отфильтрованных по дате. В первом примере все заказы из БД передаются в приложение, а затем фильтруются. Во втором используется динамический SQL-запрос, позволяющий БД возвращать только совпадающие записи:
var since = new DateTime(2023,1,1);

// фильтрация в памяти
var all = dbContext.Orders.ToList();
var recent1 = all
.Where(o.Date > since)
.ToList();

// фильтрация в БД
var recent2 = dbContext.Orders
.Where(o.Date > since)
.ToList();

Очевидно, что скорость и эффективность второго подхода почти всегда делают его предпочтительным методом.

Можно создать выражение IQueryable с помощью серии операторов, даже в разных функциях, классах или проектах. Когда об этом было впервые объявлено, в Microsoft высоко оценили эту возможность, т.к. разработчики могли создавать нужный запрос «по требованию», где бы им это ни требовалось, гарантируя, что при окончательном выполнении запроса будут возвращены только необходимые данные. У вас может быть сервис, возвращающий «чистые» данные:
public class DataService
{
public async
Task<IQueryable<Order>> List()
{
return await
_dbContext.Orders.AsQueryable();
}
}

Сервис бизнес-логики, который накладывает некоторые ограничения на выборку:
public class OrderService
{
// сервис данных внедряется в _dataService
public async
Task<IQueryable<Order>> ActiveOrders()
{
var all = await _dataService.List();
return await all
.Where(o => !o.Canceled)
.AsQueryable();
}
}

Затем в контроллере добавляется фильтр текущего пользователя:
var viewModel =
await _orderSvc.ActiveOrders()
.Where(o => o.CreatedBy = username)
.AsQueryable();

Наконец, на странице в коде Razor мы генерируем окончательный список:
foreach(var ord in
model.Orders.Where(o => o.Shipped()))
{
// список в HTML
}

В итоге у нас получается следующий запрос, который будет выполнен:
_dbContext.Orders
.Where(o => !o.Canceled &&
o.CreatedBy == username &&
o.Shipped);

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

Окончание следует…

Источник:
https://ardalis.com/avoid-dbcontext-iqueryable-proliferation/
👍15
День 1570. #ЗаметкиНаПолях
Избегайте Распространения DbContext или IQueryable в Приложениях. Окончание
Начало

Когда вы везде передаёте IQueryable, вы позволяете логике доступа к данным распространяться по всей кодовой базе. Нет никакой инкапсуляции логики и правил доступа к данным. Приложение не следует принципу разделения ответственности, поскольку позволяет логике данных «протекать» в каждую часть системы.

Более того, код за пределами уровня данных может даже не знать, что он имеет дело с IQueryable, потому что IQueryable наследует IEnumerable. Методы могут работать с IEnumerable, ожидая поведения только в памяти, но на самом деле манипулируя деревом выражений IQueryable. Это может привести к неожиданным исключениям во время выполнения, особенно если результаты фильтруются с использованием кода, который EF не может преобразовать в SQL (например, пользовательской функции). Страдает понимание кода, а также отладка, устранение неполадок и тестирование. Место IQueryable - на уровне данных. В репозитории допустимо использование IQueryable, при условии, что он не является частью абстракции или публичного интерфейса типа.

Поскольку DbContext или DbSet<T> можно использовать для получения IQueryable в любое время, передача их за пределы уровня данных имеет такое же (негативное) влияние на разделение ответственности и инкапсуляцию, что и передача IQueryable<T>.

Какая есть альтернатива?
Во-первых, вы можно передавать дерево выражений в сервис данных и использовать его:
public IOrderRepository
{
List<Order> List(
Expression<Func<Order,bool>> filter);
}

Вы создаёте фильтр заранее, он исполняется в сервисе данных, а возвращаемый результат – данные в памяти.

Ещё лучше использовать паттерн Спецификация. В этом случае вместо передачи LINQ-выражения вы передаёте экземпляр спецификации, а выражение строится в ней:
public IOrderRepository
{
List<Order> List(
Specification<Order> spec);
}

В вызывающем коде вы определяете необходимую спецификацию как часть модели предметной области, а затем используете её в том месте, где вам нужны данные:
public class ShippedOrdersForUserSpec :
Specification<Order>
{
public ShippedOrdersForUserSpec(
string username)
{
Query.Where(o => !o.Canceled &&
o.CreatedBy == username &&
o.Shipped);
}
}

// в контроллере/на странице
var spec = new ShippedOrdersForUserSpec(username);
var viewModel = await _repo.ListAsync(spec);

Таким образом запрос по-прежнему выполняется в базе данных, но IQueryable больше не используется нигде за пределами реализации репозитория. Вы можете посмотреть на использование этого паттерна в примере eShopOnWeb (см. папку /src/ApplicationCore/Specifications/).

Итого
Распространение IQueryable по всему приложению позволяет расширять запросы к БД из любого места. Это и хорошо, и плохо, и соответствует определению антипаттерна, поскольку сначала кажется чем-то стоящим, но на практике приводит к проблемам. Решение состоит не в том, чтобы получать нефильтрованные данные и затем фильтровать их на сервере приложений, а в том, чтобы идентифицировать конкретные запросы, которые потребуются отдельным страницам или конечным точкам, и определять их как первоклассные абстракции в модели предметной области приложения. Паттерны Спецификация и Репозиторий прекрасно подходят для достижения такого дизайна и обеспечивают гораздо более удобный и тестируемый результат.

Источник: https://ardalis.com/avoid-dbcontext-iqueryable-proliferation/
👍13
День 1571. #ЧтоНовенького
Новинки для Разработки API в .NET 8
В .NET 8 превью 4 представлены несколько новинок, которые могут упростить жизнь при разработке API.

1. Расширена поддержка привязки форм в минимальных API
Параметры отправленной формы выводятся без необходимости использования атрибута FromForm. Поддержка включает: IFormCollection, IFormFile и IFormFileCollection. Метаданные OpenAPI для параметров формы также выводятся автоматически для поддержки интеграции со Swagger UI.

В приведённом ниже примере кода демонстрируется реализация минимального API, который обрабатывает загрузку файлов, используя выведение параметра типа IFormFile:
var app = WebApplication.Create();

string GetPath(
string fileName,
string dir = "uploads")
{
var dirPath = Path.Combine(
app.Environment.ContentRootPath,
dir);
Directory.CreateDirectory(dirPath);
return Path.Combine(dirPath, fileName);
}

async Task Upload(
IFormFile file,
string fileName)
{
var filePath = GetPath(fileName);
await using var stream =
new FileStream(filePath, FileMode.Create);
await file.CopyToAsync(stream);
}

app.MapPost("/upload", async (IFormFile file) =>
{
var fileName = Guid.NewGuid().ToString("N")
+ Path.GetExtension(file.FileName);

await Upload(file, fileName);
return TypedResults.Ok("Uploaded successfully!");
});

app.Run();

Эта функция поддерживается как в минимальных API, использующих генерацию кода во время выполнения, так и в минимальных API, использующих новую генерацию кода во время компиляции для сценариев Native AOT.

Примечание: При реализации форм в приложении важно защищаться от XSRF-атак. В этом расширенном примере показано, как использовать сервисы ASP.NET для создания и проверки маркеров защиты от подделки межсайтовых запросов в минимальных API.

2. Шаблон проекта API включает файл .http
Шаблон проекта API (созданный с помощью dotnet new api) теперь включает файл .http, который можно использовать для отправки запросов на конечные точки, определённые в приложении, из нового редактора HTTP в Visual Studio.
@MyApi_HostAddress = https://localhost:5233

GET {{MyApi_HostAddress}}/todos/
Accept: application/json

###

GET {{MyApi_HostAddress}}/todos/1
Accept: application/json

###

Коротко про редактор HTTP в Visual Studio рассказывается в этом видео.

Источник: https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-dotnet-8-preview-4/#api-authoring
👍11
День 1572. #Карьера
3 Фразы, Которые Помогут Справиться с Эмоциями и Улучшить Отношения
Эмоциональные моменты могут заставить вас сказать или сделать то, о чём вы потом пожалеете. Сегодня рассмотрим три фразы, которые могут помочь.

Вы замечали за собой, что вы не всегда можете быть приятным собеседником? Были пассивно-агрессивны, позволяли негативному мышлению взять верх и говорили или делали то, о чём потом сожалели? Такие ошибки могут стоить очень дорого даже в финансовом плане, не говоря уже о влиянии на личную жизнь. Чаще всего они возникают из-за неспособности справиться с собственными эмоциями.

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

1. Как противостоять пассивно-агрессивному поведению
Пассивная агрессия — это проявление негативных чувств, обиды и агрессии в неуверенной или «пассивной» манере. Это может проявляться в том, что человек заявляет, что с ним всё в порядке (когда очевидно, что это не так), начинает «играть в молчанку» или избегает конфронтации только для того, чтобы позже втихую саботировать планы. Необходимо противостоять пассивно-агрессивному поведению. С этим поможет фраза «Атакуйте проблему, а не человека.»

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

2. Как относиться к критике
Если вы всегда воспринимаете критику «в штыки», обычно вы испытываете одно из двух чувств: либо желание защитить себя, либо подавленность. Ни одно из них не является продуктивным, т.к. они мешают извлечь пользу из критики. Иногда критика оправдана, иногда нет, но критика ценна, потому что даёт вам представление о том, как вас воспринимают другие.

Здесь поможет вторая фраза: «Не чувствуйте себя атакованным.»
Эта фраза заставляет вас сделать паузу, чтобы справиться с первоначальными эмоциями и сосредоточиться на анализе и решении проблемы, а также даст возможность извлечь выгоду из критики.

3. Как перестать думать и начать двигаться вперед
Если вы слишком много думаете, легко впасть в аналитический паралич. Вы можете потратить кучу времени, беспокоясь о вещах, которые никогда не произойдут, что мешает вам двигаться вперед. Это не только ограничивает вас, но и вредит отношениям, потому что люди видят в вас человека, который боится принимать решения.

Чтобы справиться с этой проблемой, вы должны напомнить себе: «Проведите эксперимент.»
Это включает следующие шаги:
1. Исследуйте тему. Найдите разумное время, чтобы изучить ситуацию. Стремитесь рассмотреть вещи с разных сторон и познакомиться с разными точками зрения.
2. Определите основную проблему. Это то, что вы контролируете? Если нет, идите дальше. Если да, составьте список вариантов решения.
3. Сузьте список. Три варианта — хорошо, два - лучше.
4. Взвесьте все за и против. Составьте список положительных и отрицательных сторон, если это поможет увидеть вещи более ясно.
5. Установите дедлайн.
6. Выберите вариант и запустите эксперимент.
Рассматривая это как эксперимент, вы напоминаете себе, что это не окончательное решение. Это выбор двигаться вперёд, а не стоять на месте.

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

Эмоции – это то, делает нас людьми. Ключ к эмоциональному интеллекту — заставить эмоции работать на вас, а не против вас.

Источник: https://www.inc.com/justin-bariso/emotional-intelligence-how-to-cope-communicate-improve-relationships.html
👍16
День 1573. #ЗаметкиНаПолях
Генерация Больших Файлов с Помощью PowerShell
Сегодня очень короткая заметка, которую захотел сохранить, чтобы найти её в ленте, когда понадобится.

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

# Здесь $pwd – текущий путь
$path = Join-Path $pwd "test.txt"
$size = 1GB
$content = New-Object byte[] $size
(New-Object System.Random).NextBytes($content)
# Set-Content очень медленный, поэтому
# используем метод .NET напрямую
[System.IO.File]::WriteAllBytes($path, $content)

Если вам нужен пустой файл (заполненный нулями), то в Windows можно просто использовать fsutil, что намного быстрее:
$size = 1GB
fsutil file createNew test.txt $size

Источник: https://www.meziantou.net/generate-large-files-using-powershell.htm
👍23
День 1574. #DevOps #ProjectManagement
Без Паники! Пособие по Управлению Любым Инцидентом в Продакшене. Начало
Независимо от того, насколько сильна ваша организация, насколько тщательно планирование и развертывание… всё когда-нибудь ломается. Системы выходят из строя, ключевые функции перестают работать, и в какой-то момент карьеры каждого разработчика возникает проблема.

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

Шаг 1. Не паникуйте и определите проблему
В один прекрасный день базовая функциональность приложения перестаёт работать. Команда разработчиков собирается в совещательной комнате, чтобы найти решение проблемы. Инстинктивная реакция: «Быстрее, откатывай всё назад!» Это понятное чувство, мы создали проблемы, и, естественно, есть желание от них избавиться. Но с быстрыми действиями приходят и быстрые ошибки, и более опытные разработчики здесь резонно зададут вопрос: «А почему не работает?». Да, вашей реакцией может быть: «Кого это волнует? Наша ошибка видна всему миру!» Но спокойный характер и аналитическая манера поведения успокоят команду и убедят, что то, что происходит в этой комнате, правильно: надо задавать вопросы и исследовать.

Шаг 2. Диагностика и понимание источника проблемы
Это звучит как очевидная вещь, но из-за беспокойства и паники никто может не задаться этим вопросом. Оставьте проблему, чтобы убедиться, что вы знаете, почему это не работает. Перепроверьте журналы исключений, отдельные рабочие процессы или даже не было ли чего-то странного на системном уровне. Воспроизведите ошибку (здесь очень поможет, если ваши среды разработки настроены максимально идентично производственной среде). После того, как вы поймёте, что сделали не так, и наберётесь достаточно уверенности для следующего выпуска, начинайте откат. Это тонкий момент, но учитесь всегда использовать все возможности для поиска природы ошибки, прежде чем откатиться и потерять свой лучший источник информации: реальную проблему в дикой природе.

Также важно найти кого-то, кто займётся техническими вопросами и сможет помочь координировать усилия по исправлению (обычно это более старший разработчик), и кого-то, кто будет отвечать за «пиар для внешней среды» и «прикрытие с воздуха» тех, кто может захотеть потратить время на изучение ошибки (обычно это директор или технический руководитель). Это делается для защиты самого ценного ресурса во время кризиса: времени и внимания тех, кто действительно может реализовать план исправления.
Более технический специалист нужен, чтобы помочь разбить решение на этапы и делегировать или разделить работу, которую необходимо выполнить. А лидер инцидента (как его часто иронически называют) должен помогать, но не диктовать. Лучшие лидеры инцидентов задают два вопроса: «На каком этапе мы находимся?» и «Что вам нужно?», а также могут держать рассерженных пользователей подальше от специалистов, решающих проблему.

Окончание следует…

Источник:
https://stackoverflow.blog/2023/05/03/dont-panic-a-playbook-for-managing-any-production-incident/
👍10
День 1575. #DevOps #ProjectManagement
Без Паники! Пособие по Управлению Любым Инцидентом в Продакшене. Окончание
Начало

Шаг 3. Исправление: начнём работать над проблемой
Мы знаем, что у нас есть проблема, мы знаем источник проблемы, теперь давайте составим план и исправим её. Мы все любим переходить к этому шагу, сразу приступать к исправлению. И иногда у нас есть такая роскошь для простых вопросов, когда проблема настолько очевидна, что подтверждение и понимание источника проблемы — быстрые шаги. Но если проблема зашла далеко или она серьезная, нужно поступать более обдуманно.

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

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

Шаг 4. Проверка и обучение
Если все пойдёт хорошо, тесты подтвердятся, и вся ценная информация, полученная на шагах 1 и 2, даст вам уверенность в новых планах тестирования, вы можете развернуть исправление. Как только оно будет опубликовано, дайте время команде на всех уровнях, чтобы изучить его и подтвердить, что всё работает как надо. Если инженерам в начале этих инцидентов даются время и свобода, то они более уверенно и спокойно относятся к последующим релизам и исправлениям.

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

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

Источник: https://stackoverflow.blog/2023/05/03/dont-panic-a-playbook-for-managing-any-production-incident/
👍8
День 1576. #ЗаметкиНаПолях
Загрузка Больших Файлов в
ASP.NET Core. Начало
Недавно мы рассмотрели, как создавать большие файлы для тестов, сегодня посмотрим, как обрабатывать загрузку больших файлов в ASP.NET Core.

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

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

1. Настройка Kestrel
Нужно настроить Kestrel в файле Program.cs, чтобы разрешить загрузку больших файлов:
builder.WebHost.ConfigureKestrel(opts =>
{
opts.Limits.MaxRequestBodySize
= long.MaxValue;
});
Здесь long.MaxValue приведено для примера и позволяет загружать файлы практически любого размера. В реальности задайте значение, которое нужно вам. Также можно использовать атрибут RequestSizeLimit на отдельном методе действия.

2. Атрибуты валидации
Определим 2 атрибута фильтров действия для валидации контента.
1) MultipartFormDataAttribute проверит, что способом отправки является "multipart/form-data":
public class MultipartFormDataAttribute 
: ActionFilterAttribute
{
public override void
OnActionExecuting(
ActionExecutingContext ctx)
{
var r = ctx.HttpContext.Request;
if (r.HasFormContentType
&& r.ContentType
.StartsWith("multipart/form-data"))
return;

ctx.Result = new
StatusCodeResult(
StatusCodes.Status415UnsupportedMediaType);
}
}

2) Также нужно отключить проверку модели, чтобы связыватели модели не проверяли тело и не загружали его в память или на диск:
public class DisableModelBindingAttribute : 
Attribute, IResourceFilter
{
public void OnResourceExecuting(
ResourceExecutingContext ctx)
{
var f = ctx.ValueProviderFactories;
f.RemoveType<FormValueProviderFactory>();
f.RemoveType<FormFileValueProviderFactory>();
f.RemoveType<JQueryFormValueProviderFactory>();
}
}

Окончание следует…

Источник:
https://code-maze.com/aspnetcore-upload-large-files/
👍17
День 1577. #ЗаметкиНаПолях
Загрузка Больших Файлов в
ASP.NET Core. Окончание
Начало

3. Теперь посмотрим на метод действия контроллера и сервис для загрузки файлов:
[HttpPost("upload")]
[MultipartFormData]
[DisableModelBinding]
public async Task<IActionResult> Upload()
{
var len = await _fileSvc
.UploadAsync(
HttpContext.Request.Body,
Request.ContentType);

return CreatedAtAction(nameof(Upload), len);
}

Здесь у нас есть метод действия HTTP POST, который имеет атрибуты, созданные в предыдущем посте, гарантирующие, что входящий запрос имеет правильный тип содержимого "multipart/form-data" и отключающие проверку модели.

Метод вызывает UploadAsync из сервиса FileService, который показан ниже:
public async Task<long> UploadAsync(
Stream file, string contentType)
{
long size = 0;
var bound =
HeaderUtilities.RemoveQuotes(
MediaTypeHeaderValue
.Parse(contentType).Boundary)
.Value;
var reader =
new MultipartReader(bound, file);
var sect =
await reader.ReadNextSectionAsync();

while (sect != null)
{
var fileSec = sect.AsFileSection();
if (fileSec is null) continue;

var path = Path.Combine("uploads",
fileSec.FileName);
await using var stream =
new FileStream(path,
FileMode.Create,
FileAccess.Write,
FileShare.None,
1024);

await fileSec.FileStream?
.CopyToAsync(stream);

size += fileSec.FileStream.Length;

sect = await
reader.ReadNextSectionAsync();
}
return size;
}

Метод UploadAsync считывает составные данные из входного потока и сохраняет каждый файл на диск. Файлы должны быть разделены границей, определённой в заголовке Content-Type. Мы извлекаем эту границу в переменную bound, затем читаем каждый файл в цикле. Также вычисляем общий размер загруженных файлов.

Полный и более подробный код проекта на GitHub.

Посмотрим, как отправлять файлы с помощью Postman:
Нужно выбрать опцию form-data и добавить пары ключ-значение, где ключом будет имя файла, а значением - сам файл. Также проверьте, что установлен заголовок Content-Type со значением «multipart/form-data; border=…» на вкладке заголовков.

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

Источник: https://code-maze.com/aspnetcore-upload-large-files/
👍16
День 1578. #Testing
Проверяем Юнит-Тесты с Помощью Мутационного Тестирования
Когда-то давно (практически на заре существования канала 😊) я уже писал про мутационное тестирование и как оно помогает проверить, правильно ли ваши юнит-тесты проверяют ваш код.

Недавно на канале JetBrains вышло видео, в котором Stefan Pölz, старший разработчик в Tryport, Microsoft MVP и контрибутор в JetBrains, подробно рассказывает, что такое мутационное тестирование, и как его интегрировать в Azure Pipelines или GitHub Actions.

https://youtu.be/9BoKyeZapLs
👍3
День 1579. #Карьера
Как Пережить Плохой День на Работе
Когда у вас плохой день, ваш разум зацикливается на неудачах. Вы теряете самообладание или плохо реагируете даже на незначительные неудобства. Может показаться, что все хотят вас достать, а мелкие конфликты перерастают в серьёзные проблемы. Хотя трудно искать светлую сторону вещей, когда разум забит негативом, есть четыре вещи, которые нужно сделать, чтобы плохой день не превратился в плохую неделю.

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

Шаг 2. Позвольте себе замедлиться
Плохой день на работе не заставит ваши планы исчезнуть. Но разум не может сосредоточиться на работе. Большинство людей в этой ситуации либо:
- Уходят в самокритику, т.к не могут сделать запланированное.
- Заставляют себя действовать «через не могу».
Ругая себя за свои недостатки, вы только больше демотивируете себя. А превозмогание стресса может повлиять на психическое здоровье и личное благополучие. Вместо этого, проявите сочувствие к себе. Остановитесь, чтобы сказать себе: «сейчас мне действительно трудно», как я могу себе помочь? Исследования показывают, что сочувствие к себе тесно связано с эмоциональной устойчивостью и психологическим благополучием. Будьте добры к себе и отложите принятие важных решений или сделайте что-нибудь, что очистит ваш разум. Заботиться о себе, когда вы расстроены, важнее, чем быть продуктивным.

Шаг 3. Возродите позитивные связи
В плохой день всё, что заряжает вас энергией, обязательно изменит ваше настроение и поможет увидеть, что всё не так плохо, как казалось сначала. Сделайте паузу и определите, что вас заряжает энергией. Не обязательно пункт из списка дел и результат деятельности не имеет значения. Смысл в самоощущении. Небольшое позитивное действие разорвет негативный цикл размышлений и заменит его позитивными мыслями. Чтобы сохранить это чувство, подумайте о своих сильных сторонах и навыках, прошлых достижениях или обо всём, что вы сделали хорошо. Это вдохновит вас переключиться с деструктивных мыслей на созидательные действия. Вы всё ещё можете быть разочарованы событиями дня, но это разочарование не приведёт к ощущению безнадёжности и беспомощности.

Шаг 4: Работа на будущее
Взлеты и падения – то, что делает жизнь нескучной. Но в плохой день, легко поддаться негативу. Разум поглощен неприятностями, вы зацикливаетесь на вещах, которые даже не имеют значения в долгосрочной перспективе. Вместо этого спроецируйте плохой день на будущее:
- Как то, что произошло сегодня, влияет на ваши цели?
- Что это будет значить для вас через неделю, месяц, год?
- Это в вашей власти? Что вы можете сделать, чтобы этого не повторилось?
Так вы поймете, что тратите время и энергию на мелочи или заботы, которые в долгосрочной перспективе не будут иметь значения. Даже если это что-то важное, найти решение гораздо продуктивнее, чем погружаться в гнев и разочарование.

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

Источник: https://betterprogramming.pub/how-to-deal-with-a-bad-day-at-work-89d85ecc57bb
👍15
День 1580. #ЧтоНовенького
.NET 8 Расширяет Возможности Blazor и WebAssembly
Microsoft работает над повышением производительности веб-приложений с помощью рендеринга Blazor на стороне сервера и потокового рендеринга, а также улучшений среды выполнения Blazor WebAssembly.

На прошлой неделе Microsoft подробно изложили планы будущей среды разработки .NET 8. Для веб-разработки .NET 8 будет сочетать сильные стороны рендеринга на стороне сервера и на стороне клиента в компонентной модели Blazor.

Теперь доступный в версии превью 4 .NET 8 использует отрисовку на стороне сервера с компонентами Blazor, улучшенную навигацию и обработку форм, а также потоковую отрисовку. По словам Microsoft, потоковая отрисовка, при которой обновления контента передаются в поток ответов, может улучшить взаимодействие с пользователем для страниц, генерируемых на стороне сервера, которым необходимо выполнять длительные асинхронные задачи для полной визуализации.

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

Разработчики смогут добавлять клиентскую интерактивность для каждого компонента или страницы в отдельности, а также выбирать режим рендеринга во время выполнения. Рендеринг на стороне сервера и потоковый рендеринг стали доступны в .NET 8 Preview 3 и .NET 8 Preview 4 соответственно. Дополнительные механизмы будут добавлены в следующих предварительных версиях.

В .NET 8 разработчики также могут взять компонент Blazor и отобразить его полностью вне контекста HTTP-запроса. Компонент может отображаться через HTML как строка или поток, независимо от среды размещения (серверной или WebAssembly). Это полезно для создания фрагментов HTML, таких как автоматическое электронное письмо. В Microsoft заявляют, что в будущем это позволит генерировать статический контент сайта для Blazor.

Microsoft также работает над улучшением производительности .NET в браузерах с помощью WebAssembly. Jiterpreter в .NET 8 предлагает частичную поддержку JIT и повышает производительность среды выполнения .NET WebAssembly. Microsoft сообщает, что рендеринг пользовательского интерфейса в тестах стал на 20% быстрее благодаря jiterpreter, а сериализация и десериализация JSON выполняются в два раза быстрее. Последние спецификации WebAssembly, такие как SIMD для предварительной (AOT) компиляции, наряду с улучшениями горячей перезагрузки, также используются для WebAssembly.

Также для приложений Blazor WebAssembly используется новый удобный для Интернета формат упаковки Webcil. Webcil представляет собой веб-ориентированную упаковку сборок .NET, которая удаляет всё содержимое, относящееся к нативному выполнению Windows, чтобы избежать проблем при развертывании в средах, которые блокируют загрузку или использование DLL-файлов.

Источники:
-
https://www.infoworld.com/article/3697728/microsoft-net-8-boosts-blazor-webassembly.html
-
https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-dotnet-8-preview-4/
👍14
День 1581. #ЗаметкиНаПолях
Мультитенантные Приложения с EF Core. Начало
Сегодня большинство программ основано на концепции мультитенантости, т.е. обслуживания нескольких клиентов, сохраняя при этом их данные изолированными. Это можно реализовать двумя способами:
1. Единая база данных и логическая изоляция,
2. Несколько баз данных и физическая изоляция.

1. Мультитенантость в единой базе данных
Для реализации мультитенантости понадобятся две вещи:
- Узнать текущего клиента,
- Отфильтровать данные только для этого клиента.

Типичный подход к мультитенантости в одной базе данных — наличие столбца TenantId в таблицах. А затем фильтрация по этому столбцу при запросе к базе данных. В EF Core мы можем использовать глобальный фильтр запросов. Внутри метода OnModelCreating мы настраиваем фильтр запросов для объекта Order:
public class OrdersContext : DbContext
{
private string _tenantId;

public OrdersContext(
DbContextOptions<OrdersContext> opt,
TenantProvider tp) : base(opt)
{
_tenantId = tp.TenantId;
}

protected override void
OnModelCreating(ModelBuilder mb)
{
mb.Entity<Order>
.HasQueryFilter(o =>
o.TenantId == _tenantId);
}
}

Мы используем класс TenantProvider для получения текущего клиента:
public class TenantProvider
{
private IHttpContexAccessor _hca;

public TenantProvider(IHttpContexAccessor hca)
{
_hca = hca;
}

public string TenantId => _hca
.HttpContext.Request.Headers["X-TenantId"];
}

В этом примере TenantId получается из заголовка HTTP-запроса. Также его можно извлекать из строки запроса, JWT Claim, ключа API и т.п. Последние два варианта более безопасны.

Окончание следует…

Источник:
https://www.milanjovanovic.tech/blog/multi-tenant-applications-with-ef-core
👍15
День 1582. #ЗаметкиНаПолях
Мультитенантные Приложения с EF Core. Окончание
Начало

2. Мультитенантность в раздельных базах данных с помощью EF Core
В некоторых отраслях, таких как здравоохранение, требуется высокая степень изоляции данных, и использование базы данных для каждого клиента является обязательным. Если мы хотим изолировать каждого клиента в отдельной базе данных, нам нужно:
- Применить разные строки подключения для каждого клиента,
- Каким-то образом определять строку подключения для каждого клиента.

Здесь нельзя использовать фильтры запросов, так как мы работаем с разными базами. Поэтому нужно где-то хранить информацию о клиенте и строке подключения. Простым примером (если клиентов относительно немного) может быть сохранение их в настройках приложения:
"Tenants": {
{ "Id": "tenant-1", "ConnectionString":
"Host=tenant1.db;Database=tenant1" },
{ "Id": "tenant-2", "ConnectionString":
"Host=tenant2.db;Database=tenant2" }
}
Затем можно зарегистрировать экземпляр IOptions со списком объектов Tenant. Вот здесь подробнее про паттерн Options.

Также нужно изменить класс TenantProvider, чтобы он возвращал строку подключения для текущего клиента:
public sealed class TenantProvider
{
private IHttpContexAccessor _hca;
private TenantSettings _ts;

public TenantProvider(
IHttpContexAccessor hca,
IOptions<TenantSettings> opt)
{
_hca = hca;
_ts = opt.Value;
}

public string TenantId => _hca
.HttpContext.Request
.Headers["X-TenantId"];

public string GetConnectionString()
{
return _ts.Tenants
.Single(t => t.Id == TenantId);
}
}

И последняя часть — это регистрация DbContext для динамического разрешения строки подключения для текущего клиента:
builder.Services
.AddDbContext<OrdersContext>((sp, o) =>
{
var tp = sp.GetRequiredService<TenantProvider>();
var cs = tp.GetConnectionString();

o.UseSqlServer(cs);
});

При каждом запросе мы создаём новый OrdersContext и подключаемся к соответствующей базе данных для этого клиента. Обязательно следует хранить строки подключения клиента в безопасном месте, таком как Azure Key Vault.

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

Источник: https://www.milanjovanovic.tech/blog/multi-tenant-applications-with-ef-core
👍11
День 1583. #ЗаметкиНаПолях #TipsAndTricks
Интересные Вещи, Которые Можно Делать с Кортежами
Сегодня рассмотрим, что такое кортежи и некоторые вещи, которые можно сделать с ними.

Кортежи (ValueTuple) в C# — это упрощённый изменяемый тип-значение, который позволяет группировать несколько значений или связанных данных вместе для различных целей, таких как возврат нескольких значений из метода, создание структурированных данных и т.п. Он обеспечивает более высокую производительность по сравнению с традиционными ссылочными типами, поскольку не требует аллокации на куче.

(string Name, int Age) person = ("Jon Smith", 42);
person.Name; // "Jon Smith"
person.Age; // 42

static (string Name, int Age) GetPerson()
=> ("Jon Smith", 42);

Что же можно делать с помощью кортежей?

1. Поменять местами два значения
Допустим, мы хотим поменять местами значения двух переменных:
int a = 1;
int b = 2;
// Замена
(a, b) = (b, a);

2. Простая реализация IEquatable
А также простой способ сравнения двух объектов или получения хэш-кода:
public class Dimension : IEquatable<Dimension>
{
public Dimension(int width, int height)
=> (Width, Height) = (width, height);

public int Width { get; }
public int Height { get; }

public bool Equals(Dimension d)
=> (Width, Height) == (d?.Width, d?.Height);

public override bool Equals(object o)
=> o is Dimension d && Equals(d);

public override int GetHashCode()
=> (Width, Height).GetHashCode();
}

3. Сопоставление по шаблону
Кортежи позволяют писать элегантный и выразительный код:
(string Name, int Age) person = ("Alice", 25);
string desc = person switch
{
("Alice", >= 0 and < 13) => "Alice ребёнок.",
("Alice", >= 13 and < 20) => "Alice подросток.",
("Alice", _) => "Alice взрослая.",
(_, >= 0 and < 13) => "Это ребёнок.",
(_, >= 13 and < 20) => "Это подросток.",
_ => "Это взрослый."
};

4. Деконструкция пользовательских типов
Кортежи позволяют легко разбить тип на отдельные переменные:
public class Student
{
public string Name { get; set; }
public int Age { get; set; }

public void Deconstruct(out string name, out int age)
{
name = Name;
age = Age;
}
}

var student = new Student { Name = "John", Age = 21 };
var (studentName, studentAge) = student;

См. подробнее о деконструкции

Источник: https://steven-giesel.com/blogPost/bcbb602f-4080-4e89-9929-019bb4e1cb58
👍23
День 1584.
Папки Против Пространств Имён
Что, если ваша структура папок не будет соответствовать структуре пространств имён?

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

Инструменты и шаблоны часто подталкивают вас к организации кода в соответствии с техническими проблемами: контроллеры, модели, представления и т. д. Код организован в папки, такие как Controllers, Models, DataAccess и т. п. Вы кладёте все контроллеры в папку Controllers и убеждаетесь, что пространство имён совпадает иерархии папок. Распространённой критикой является то, что это неправильный способ организации кода. Но это подразумевает, что есть правильный способ. Например, файлы должны быть организованы по папкам в соответствии с функциональностью, а не с техническим предназначением.

Однако если вы решите игнорировать соглашение о том, что структура пространств имён должна отражать структуру папок, у вас появится вторая ось вариативности. Например, если оставить организацию файлов по папкам в соответствии с техническим предназначением, но организовать пространства имён в соответствии с функциями: Users, Orders, Reservations и т.д., то открыв в IDE окно Class View, можно заметить, что иерархия классов там отличается от иерархии в Обозревателе Решений (Solution Explorer), классы организованы по функциям.

Этот пост не является рекомендацией. Это не что иное, как первоначальное исследование идеи. Несмотря на то, что это даёт две оси вариативности, вам всё равно придётся идти на компромиссы. С контроллерами всё понятно, но куда вы поместите сквозные задачи или общую логику предметной области?

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

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

Итого
Теоретически структура пространства имён может не следовать структуре каталогов. Вы можете реорганизовать кодовую базу так, чтобы файловая структура соответствовала деталям реализации, а иерархия пространств имен — функциям (или наоборот). Я это попробовал в своём проекте, и после нескольких часов реорганизации кода, не могу сказать, что меня пугает эта идея. Хотя, возможно, в более крупной базе кода она столкнётся с проблемами. С другой стороны, я все ещё не убежден, что навязывание иерархии сложному дизайну программного обеспечения как-то особенно выгодно.

Источник: https://blog.ploeh.dk/2023/05/15/folders-versus-namespaces/
Автор оригинала: Марк Симанн
👍8
День 1585. #ЧтоНовенького
Единые Настройки в Visual Studio
Разработчики Visual Studio решили упростить настройку IDE для всех ваших рабочих сред, независимо от того, работаете ли вы на ноутбуке дома, на компьютере в офисе или используете Dev Box или виртуальную машину Azure. Единые Настройки (Unified Settings) – прототип, который ещё находится в стадии разработки, призванный переосмыслить то, как вы персонализируете свою IDE и совместно использовать настройки между различными рабочими средами и установками Visual Studio.

Единые настройки помогают настроить среду как индивидуальными параметрами, так и параметрами группы/рабочей области. Настройки синхронизируются с облаком, поэтому настройка новой среды может занять минуты, а не часы. У вас будет доступ ко всем вашим настройкам в файле JSON на случай, если вы захотите просмотреть их или поделиться ими с товарищем по команде. На форуме сообщества создана тема, посвящённая прототипу единых настроек, где разработчики утилиты собирают мнения и отзывы по мере её реализации. В этом коротком видео поясняется суть утилиты и её отличие от текущего UI настроек.

Сегодня опыт настройки IDE под свои предпочтения полон препятствий и боли. Это приводит к тому, что многие варианты кастомизации никогда не используются или даже не обнаруживаются. Единые настройки призваны решить некоторые распространённые проблемы:
1. Последовательность
Сегодня каждая страница в параметрах инструментов имеет собственный UI. Поэтому часто параметры не отражают текущую тему, непоследовательно масштабируются под разрешение монитора или используют разный UI для похожих настроек и т.п.

2. Область применения
Сейчас настройки обычно меняют поведение этой конкретной установки VS. Но иногда может потребоваться управлять настройками, которые лучше всего применять к целым репозиториям. В Единых Настройках представлены «области действия», чтобы предоставить больше гибкости и контроля над вашей IDE. Предполагаются следующие области применения:
- Пользовательская (User): применяется ко всем проектам и решениям для данной установки Visual Studio. Как правило, это личные настройки: тема, шрифты и цвета, параметры отладки.
- Рабочей области (Workspace): применяется к конкретному проекту или репозиторию. Может включать специальные настройки отладки, настраиваемый внешний вид вкладки и т.п.

3. Общий доступ и синхронизация настроек
Единые настройки синхронизируются с облаком и хранятся в удобочитаемом файле JSON, поэтому вы сможете поделиться ими с товарищем по команде или вручную изменить значения настроек. Вы также сможете выбрать синхронизацию всех или отдельных настроек.

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

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

Источник: https://devblogs.microsoft.com/visualstudio/unifiedsettings/
👍9