День 1866. #ЗаметкиНаПолях
Определяем Разные Строковые Представления Объекта
Даже если внутренние данные одинаковы, иногда их можно представить по-разному. Взять структуру DateTime. Используя разные модификаторы, вы можете представлять одну и ту же дату в разных форматах.
Одна дата, но написана по-разному. Также можно предоставить культуру:
Интерфейс IFormattable позволяет определить такое поведение в наших классах, переопределив метод ToString(). Например:
Теперь мы можем определить различные форматы. Согласно принципу близости данных (хранить данные рядом с местом их использования), добавим в Person вложенный класс с доступными форматами:
Наконец, реализуем ToString() с нашими форматами и случаем, когда формат не распознается:
Заметьте, что в случае Formats.Full мы используем FormattableString, чтобы применить параметр IFormatProvider к конечному результату.
Тестируем
Источник: https://www.code4it.dev/csharptips/iformattable-interface/
Определяем Разные Строковые Представления Объекта
Даже если внутренние данные одинаковы, иногда их можно представить по-разному. Взять структуру DateTime. Используя разные модификаторы, вы можете представлять одну и ту же дату в разных форматах.
var dt = new DateTime(2024, 1, 1, 8, 53, 14);
dt.ToString("yyyy-MM-dddd"); //2024-01-Monday
dt.ToString("Y"); //January 2024
Одна дата, но написана по-разному. Также можно предоставить культуру:
var it = new CultureInfo("it-IT");
dt.ToString("yyyy-MM-dddd", it); //2024-01-lunedì
Интерфейс IFormattable позволяет определить такое поведение в наших классах, переопределив метод ToString(). Например:
public class Person : IFormattable
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public string ToString(
string? format,
IFormatProvider? fp)
{
// Здесь определяем логику форматирования
}
}
Теперь мы можем определить различные форматы. Согласно принципу близости данных (хранить данные рядом с местом их использования), добавим в Person вложенный класс с доступными форматами:
public class Person : IFormattable
{
…
public static class Formats
{
public const string FirstAndLastName = "FL";
public const string Mini = "Mini";
public const string Full = "Full";
}
}
Наконец, реализуем ToString() с нашими форматами и случаем, когда формат не распознается:
public string ToString(
string? format,
IFormatProvider? fp)
{
switch (format)
{
case Formats.FirstAndLastName:
return $"{FirstName} {LastName}";
case Formats.Full:
{
FormattableString fs =
$"{FirstName} {LastName} ({BirthDate:D})";
return fs.ToString(fp);
}
case Formats.Mini:
return $"{FirstName[0]}.{LastName[0]}";
default:
return this.ToString();
}
}
Заметьте, что в случае Formats.Full мы используем FormattableString, чтобы применить параметр IFormatProvider к конечному результату.
Тестируем
var p = new Person
{
FirstName = "Albert",
LastName = "Einstein",
BirthDate = new DateTime(1879, 3, 14)
};
p.ToString(Person.Formats.FirstAndLastName, it);
//Albert Einstein
p.ToString(Person.Formats.Full, it);
//Albert Einstein (venerdì 14 marzo 1879)
p.ToString(Person.Formats.Full, null);
//Albert Einstein (Friday, March 14, 1879)
p.ToString("INVALID FORMAT", CultureInfo.InvariantCulture);
//Person
$"I am not {p:Full}";
//I am not Albert Einstein (Friday, March 14, 1879)
Источник: https://www.code4it.dev/csharptips/iformattable-interface/
👍22
День 1867. #BestPractices
Избегайте Нескольких Булевых Параметров
Булевы параметры хороши, но сложно отследить, что делает каждый из них, если их несколько. Сегодня посмотрим, почему лучше избегать использования нескольких булевых параметров и как это отрефакторить.
Почему это плохо?
Посмотрим на следующий метод:
Это пример из библиотеки компонентов ImageListView. Проблема здесь в том, что два логических параметра обозначают четыре разные комбинации значений:
И в некоторой степени вы ожидаете четырёх разных вариантов поведения. Но в реализации это не так:
Разрешены только определённые комбинации. Кроме того, «сила» и «ленивость» кажутся в этом контексте противоречивыми. Трудно запомнить, что делает каждая комбинация. Можно сказать, что это в некоторой степени нарушает «принцип единой ответственности», поскольку метод выполняет несколько задач.
Другой частый пример: есть уже существующий метод, и какой-то новый метод должен расширить/изменить его поведение. Для простоты предположим, что нужна сортировка. Самый простой способ — добавить булев параметр:
Проблема здесь – понять, что происходит. И чем больше параметров, тем больше комбинаций и тем сложнее понять метод.
Рефакторинг
1. Сделать каждую комбинацию отдельным методом
Так все варианты очевидны, и вы можете использовать более выразительное имя метода. Из примера выше:
Теперь ясно, что недопустимо использовать оба параметра сразу. При этом мы возлагаем на вызывающую сторону ответственность за решение, какой метод вызывать. Клиент знает, что он хочет сделать, и может выразить это более чётко. Внутри эти методы, безусловно, могут совместно использовать большую часть кода. Кроме того, если нужно изменить поведение одного из методов, это гораздо проще сделать, поскольку между ними меньше связи.
Замечание: надо различать общедоступный API и закрытые методы. Можно иметь несколько булевых параметров в закрытых методах, но надо стараться избегать их в публичных API. Публичный API - интерфейс с внешним миром и должен быть максимально понятным.
2. Использовать перечисления с возможными вариантами
Хотя здесь показаны все возможные варианты, это не так ясно, как предыдущий подход. Все ещё возможно, что метод делает слишком много. Здесь всегда компромисс между слишком большим количеством параметров и слишком большим количеством методов.
Источник: https://steven-giesel.com/blogPost/9994b00c-8bc2-4794-ae74-80e6ee4cd5e5/avoid-multiple-boolean-parameters
Избегайте Нескольких Булевых Параметров
Булевы параметры хороши, но сложно отследить, что делает каждый из них, если их несколько. Сегодня посмотрим, почему лучше избегать использования нескольких булевых параметров и как это отрефакторить.
Почему это плохо?
Посмотрим на следующий метод:
/// <summary>
/// Refreshes the control.
/// </summary>
/// <param name="force">Forces a refresh even if the renderer is suspended.</param>
/// <param name="lazy">Refreshes the control only if a set amount of time
/// has passed since the last refresh.</param>
void Refresh(bool force, bool lazy);
Это пример из библиотеки компонентов ImageListView. Проблема здесь в том, что два логических параметра обозначают четыре разные комбинации значений:
Refresh(true, true);
Refresh(true, false);
Refresh(false, true);
Refresh(false, false);
И в некоторой степени вы ожидаете четырёх разных вариантов поведения. Но в реализации это не так:
internal void Refresh(bool force, bool lazy)
{
if (force)
base.Refresh();
else if (lazy)
{
rendererNeedsPaint = true;
lazyRefreshTimer.Start();
}
else if (CanPaint())
base.Refresh();
else
rendererNeedsPaint = true;
}
Разрешены только определённые комбинации. Кроме того, «сила» и «ленивость» кажутся в этом контексте противоречивыми. Трудно запомнить, что делает каждая комбинация. Можно сказать, что это в некоторой степени нарушает «принцип единой ответственности», поскольку метод выполняет несколько задач.
Другой частый пример: есть уже существующий метод, и какой-то новый метод должен расширить/изменить его поведение. Для простоты предположим, что нужна сортировка. Самый простой способ — добавить булев параметр:
IEnumerable<Item> GetItems(Filter f);
IEnumerable <Item> GetItems(Filter f, bool sort);
// или ещё хуже:
IEnumerable <Item> GetItems(Filter f, bool sort, bool descending);
Проблема здесь – понять, что происходит. И чем больше параметров, тем больше комбинаций и тем сложнее понять метод.
Рефакторинг
1. Сделать каждую комбинацию отдельным методом
Так все варианты очевидны, и вы можете использовать более выразительное имя метода. Из примера выше:
void Refresh();
void RefreshForce();
void RefreshLazy();
Теперь ясно, что недопустимо использовать оба параметра сразу. При этом мы возлагаем на вызывающую сторону ответственность за решение, какой метод вызывать. Клиент знает, что он хочет сделать, и может выразить это более чётко. Внутри эти методы, безусловно, могут совместно использовать большую часть кода. Кроме того, если нужно изменить поведение одного из методов, это гораздо проще сделать, поскольку между ними меньше связи.
Замечание: надо различать общедоступный API и закрытые методы. Можно иметь несколько булевых параметров в закрытых методах, но надо стараться избегать их в публичных API. Публичный API - интерфейс с внешним миром и должен быть максимально понятным.
2. Использовать перечисления с возможными вариантами
enum RefreshMode { Default, Force, Lazy }
void Refresh(RefreshMode mode);
Хотя здесь показаны все возможные варианты, это не так ясно, как предыдущий подход. Все ещё возможно, что метод делает слишком много. Здесь всегда компромисс между слишком большим количеством параметров и слишком большим количеством методов.
Источник: https://steven-giesel.com/blogPost/9994b00c-8bc2-4794-ae74-80e6ee4cd5e5/avoid-multiple-boolean-parameters
👍20
День 1868. #Шпаргалка #Git
Популярные Настройки Git Config. Начало
Джулия Эванс спросила у коллег-программистов мнения о самых популярных и полезных настройках Git, которые они используют. Вот такой список получился.
Описание всех настроек можно найти в документации Git.
1. pull.ff only или pull.rebase true
Эти две были самыми популярными. Обе они преследуют схожие цели: избежать случайного создания коммита слияния при запуске
- pull.rebase true эквивалентен запуску
- pull.ff only эквивалентен запуску
Скорее всего, нет смысла устанавливать обе одновременно, поскольку
2. merge.conflictstyle zdiff3
Делаем конфликты слияний более читабельными!
По умолчанию в git конфликты слияния выглядят так:
Вам предлагается решить, что лучше:
Вот как выглядит тот же конфликт слияния с merge.conflictstyle diff3:
Здесь содержится дополнительная информация: теперь исходная версия кода находится посередине! Итак, мы видим:
- одна сторона изменила
- другая сторона переименовала
Поэтому, по-видимому, правильным решением конфликта слияния является
zdiff3 — это тот же diff3, но он лучше выносит любые общие строки в начале или конце за пределы зоны конфликта. То есть, если обе стороны сделали изменения, которые имеют общие строки в начале или в конце, то они будут корректно слиты и не будут входить в зону конфликта.
3. rebase.autosquash true
Цель autosquash — упростить модификацию старых коммитов. Допустим, у вас есть коммит с исправлением другого коммита (3 коммита назад), который вы хотели бы объединить с ним. Вы фиксируете новый коммит с помощью
Теперь, когда вы запускаете
rebase.autosquash true означает, что
4. rebase.autostash true
Это автоматически запускает
Продолжение следует…
Источник: https://jvns.ca/blog/2024/02/16/popular-git-config-options/
Популярные Настройки Git Config. Начало
Джулия Эванс спросила у коллег-программистов мнения о самых популярных и полезных настройках Git, которые они используют. Вот такой список получился.
Описание всех настроек можно найти в документации Git.
1. pull.ff only или pull.rebase true
Эти две были самыми популярными. Обе они преследуют схожие цели: избежать случайного создания коммита слияния при запуске
git pull
на ветке, где восходящая ветвь расходится.- pull.rebase true эквивалентен запуску
git pull --rebase
каждый раз,- pull.ff only эквивалентен запуску
git pull --ff-only
каждый раз.Скорее всего, нет смысла устанавливать обе одновременно, поскольку
--ff-only
переопределяет --rebase
.2. merge.conflictstyle zdiff3
Делаем конфликты слияний более читабельными!
По умолчанию в git конфликты слияния выглядят так:
<<<<<<< HEAD
def parse(input):
return input.split("\n")
=======
def parse(text):
return text.split("\n\n")
>>>>>>> somebranch
Вам предлагается решить, что лучше:
input.split("\n")
или text.split("\n\n")
. Но как? Что делать, если вы не помните, нужно \n
или \n\n
?Вот как выглядит тот же конфликт слияния с merge.conflictstyle diff3:
<<<<<<< HEAD
def parse(input):
return input.split("\n")
||||||| base
def parse(input):
return input.split("\n\n")
=======
def parse(text):
return text.split("\n\n")
>>>>>>> somebranch
Здесь содержится дополнительная информация: теперь исходная версия кода находится посередине! Итак, мы видим:
- одна сторона изменила
\n\n
на \n
,- другая сторона переименовала
input
в text
.Поэтому, по-видимому, правильным решением конфликта слияния является
return text.split("\n")
, поскольку он объединяет изменения с обеих сторон.zdiff3 — это тот же diff3, но он лучше выносит любые общие строки в начале или конце за пределы зоны конфликта. То есть, если обе стороны сделали изменения, которые имеют общие строки в начале или в конце, то они будут корректно слиты и не будут входить в зону конфликта.
3. rebase.autosquash true
Цель autosquash — упростить модификацию старых коммитов. Допустим, у вас есть коммит с исправлением другого коммита (3 коммита назад), который вы хотели бы объединить с ним. Вы фиксируете новый коммит с помощью
git commit --fixup OLD_COMMIT_ID
, что даёт новому коммиту сообщение «fixup! …».Теперь, когда вы запускаете
git rebase --autosquash main
, он автоматически объединяет все коммиты-исправления (fixup!) со своими целями.rebase.autosquash true означает, что
--autosquash
всегда автоматически передаётся в git rebase
.4. rebase.autostash true
Это автоматически запускает
git stash
перед git rebase
и git stash pop
после. По сути, он передаёт --autostash
в git rebase
. Это означает, что вы можете запустить rebase на «грязном» рабочем дереве. Однако будьте осторожны: применение изменений из stash после успешного перебазирования может привести к нетривиальным конфликтам. Хотя, похоже, это не очень часто встречается у людей, поскольку этот вариант конфигурации кажется действительно популярным.Продолжение следует…
Источник: https://jvns.ca/blog/2024/02/16/popular-git-config-options/
👍13
День 1869. #Шпаргалка #Git
Популярные Настройки Git Config. Продолжение
Начало
5. push.default simple и push.default current
Параметры push.default сообщают git push автоматически отправлять текущую ветку в удалённую ветку с тем же именем.
-
-
current кажется хорошей настройкой, если вы уверены, что никогда случайно не создадите локальную ветку с тем же именем, что и несвязанная удалённая ветка. У многих людей есть соглашения об именах веток (например,
6. init.defaultBranch main
Создавать ветку main вместо ветки master при создании нового репозитория.
7. commit.verbose true
Добавит все различия коммита в текстовый редактор, где вы пишете сообщение о коммите, чтобы помочь вам запомнить, что вы делали.
8. rerere.enabled true
Позволяет использовать функцию rerere (reuse recovered resolution - повторно использовать восстановленное разрешение), которая запоминает, как вы разрешали конфликты слияния во время git rebase, и автоматически разрешает конфликты за вас, когда это возможно.
9. help.autocorrect 10
Если git обнаружит опечатки и сможет определить ровно одну действительную команду, аналогичную ошибке, он попытается предложить правильную команду или даже автоматически запустить предложение. Возможные значения:
-
-
-
-
-
10. core.pager delta
«Пейджер» — это то, что git использует для отображения результатов git diff, git log, git show и т. д.
delta – это модный инструмент для просмотра различий с подсветкой синтаксиса.
11. diff.algorithm histogram
Алгоритм сравнения Git по умолчанию часто плохо справляется с переупорядочением функций. Например:
Это сильно путает. Но с diff.algorithm histogram всё становится гораздо понятнее:
Ещё один популярный вариант - patience.
12. core.excludesFile – глобальный .gitignore
Окончание следует…
Источник: https://jvns.ca/blog/2024/02/16/popular-git-config-options/
Популярные Настройки Git Config. Продолжение
Начало
5. push.default simple и push.default current
Параметры push.default сообщают git push автоматически отправлять текущую ветку в удалённую ветку с тем же именем.
-
push.default simple
— значение по умолчанию в Git. Это работает только в том случае, если ваша ветка уже отслеживает удалённую ветку.-
push.default current
- аналогична, но она всегда передаёт локальную ветку в удалённую ветку с тем же именем.current кажется хорошей настройкой, если вы уверены, что никогда случайно не создадите локальную ветку с тем же именем, что и несвязанная удалённая ветка. У многих людей есть соглашения об именах веток (например,
julia/my-change
), которые делают конфликты такого рода маловероятными, либо у них просто мало сотрудников, поэтому конфликтов имён ветвей, вероятно, не происходит.6. init.defaultBranch main
Создавать ветку main вместо ветки master при создании нового репозитория.
7. commit.verbose true
Добавит все различия коммита в текстовый редактор, где вы пишете сообщение о коммите, чтобы помочь вам запомнить, что вы делали.
8. rerere.enabled true
Позволяет использовать функцию rerere (reuse recovered resolution - повторно использовать восстановленное разрешение), которая запоминает, как вы разрешали конфликты слияния во время git rebase, и автоматически разрешает конфликты за вас, когда это возможно.
9. help.autocorrect 10
Если git обнаружит опечатки и сможет определить ровно одну действительную команду, аналогичную ошибке, он попытается предложить правильную команду или даже автоматически запустить предложение. Возможные значения:
-
0
(по умолчанию) - показать предложенную команду.-
положительное число
- запустить предложенную команду через указанные децисекунды (0,1 секунды).-
"immediate"
- немедленно запустить предложенную команду.-
"prompt"
- показать предложение и запросить подтверждение для запуска команды.-
"never"
- не запускать и не показывать предлагаемые команды.10. core.pager delta
«Пейджер» — это то, что git использует для отображения результатов git diff, git log, git show и т. д.
delta – это модный инструмент для просмотра различий с подсветкой синтаксиса.
11. diff.algorithm histogram
Алгоритм сравнения Git по умолчанию часто плохо справляется с переупорядочением функций. Например:
-.header {
+.footer {
margin: 0;
}
-.footer {
+.header {
margin: 0;
+ color: green;
}
Это сильно путает. Но с diff.algorithm histogram всё становится гораздо понятнее:
-.header {
- margin: 0;
-}
-
.footer {
margin: 0;
}
+.header {
+ margin: 0;
+ color: green;
+}
Ещё один популярный вариант - patience.
12. core.excludesFile – глобальный .gitignore
core.excludesFile = ~/.gitignore
позволяет установить глобальный файл gitignore, который применяется ко всем репозиториям, для таких вещей, как .idea или .vs, которые вы никогда не хотите коммитить в какой-либо репозиторий. По умолчанию это ~/.config/git/ignore
.Окончание следует…
Источник: https://jvns.ca/blog/2024/02/16/popular-git-config-options/
👍12
День 1870. #Шпаргалка #Git
Популярные Настройки Git Config. Окончание
Начало
Продолжение
13. includeIf: отдельные конфигурации git для личного и рабочего
Многие используют это для настройки разных email для личных и рабочих репозиториев. Вы можете настроить это примерно так:
14. url."[email protected]:".insteadOf 'https://github.com/'
Если вы часто случайно клонируете HTTP-версию репозитория вместо SSH-версии, а затем приходится вручную заходить в ~/.git/config и редактировать удалённый URL, это заменит https://github.com в удалённых репозиториях на [email protected]:
Кто-то вместо этого использует pushInsteadOf для замены только в git push, потому что не хочет разблокировать свой SSH-ключ при извлечении из общедоступного репозитория.
15. fsckobjects
Проверяет получаемые/отправляемые объекты. Если будет обнаружено повреждение или ссылка на несуществующий объект, операция прервётся:
16. Остальное
-
Позволяет указать файл с коммитами, который следует игнорировать во время git blame, чтобы гигантские переименования не мешал.
-
Заставит git branch сортировать ветки по последнему использованию, а не по алфавиту, чтобы упростить поиск ветвей. tag.sort taggerdate аналогично для тегов.
-
Отключает подстветку.
-
В Windows для работы совместно с коллегами на Unix.
-
Использовать emacs (или другой редактор) для сообщений коммита.
-
Использовать difftastic (или другой редактор) для отображения различий.
-
Использовать meld (или другой редактор) для разрешения конфликтов слияния.
-
Автоматически удалит локальные ветки и теги, которых больше нет в удалённом репозитории.
-
Позволит подписывать коммиты ключами SSH.
-
Отобразит даты как 2024-03-14 15:54:51 вместо Thu Mar 14 15:54:51 2024
-
Чтобы избавиться от файлов .orig, которые git создаёт при слиянии конфликтов.
-
Добавит новые теги вместе с добавляемыми коммитами.
-
Не позволит удалять коммиты во время rebase.
Источник: https://jvns.ca/blog/2024/02/16/popular-git-config-options/
Популярные Настройки Git Config. Окончание
Начало
Продолжение
13. includeIf: отдельные конфигурации git для личного и рабочего
Многие используют это для настройки разных email для личных и рабочих репозиториев. Вы можете настроить это примерно так:
[includeIf "gitdir:~/code/<work>/"]
path = "~/code/<work>/.gitconfig"
14. url."[email protected]:".insteadOf 'https://github.com/'
Если вы часто случайно клонируете HTTP-версию репозитория вместо SSH-версии, а затем приходится вручную заходить в ~/.git/config и редактировать удалённый URL, это заменит https://github.com в удалённых репозиториях на [email protected]:
[url "[email protected]:"]
insteadOf = https://github.com/
Кто-то вместо этого использует pushInsteadOf для замены только в git push, потому что не хочет разблокировать свой SSH-ключ при извлечении из общедоступного репозитория.
15. fsckobjects
Проверяет получаемые/отправляемые объекты. Если будет обнаружено повреждение или ссылка на несуществующий объект, операция прервётся:
transfer.fsckobjects = true
fetch.fsckobjects = true
receive.fsckObjects = true
16. Остальное
-
blame.ignoreRevsFile .git-blame-ignore-revs
Позволяет указать файл с коммитами, который следует игнорировать во время git blame, чтобы гигантские переименования не мешал.
-
branch.sort -committerdate
Заставит git branch сортировать ветки по последнему использованию, а не по алфавиту, чтобы упростить поиск ветвей. tag.sort taggerdate аналогично для тегов.
-
color.ui false
Отключает подстветку.
-
core.autocrlf false
В Windows для работы совместно с коллегами на Unix.
-
core.editor emacs
Использовать emacs (или другой редактор) для сообщений коммита.
-
diff.tool difftastic
Использовать difftastic (или другой редактор) для отображения различий.
-
merge.tool meld
Использовать meld (или другой редактор) для разрешения конфликтов слияния.
-
fetch.prune true
и fetch.prunetags
Автоматически удалит локальные ветки и теги, которых больше нет в удалённом репозитории.
-
gpg.format ssh
Позволит подписывать коммиты ключами SSH.
-
log.date iso
Отобразит даты как 2024-03-14 15:54:51 вместо Thu Mar 14 15:54:51 2024
-
merge.keepbackup false
Чтобы избавиться от файлов .orig, которые git создаёт при слиянии конфликтов.
-
push.followtags true
Добавит новые теги вместе с добавляемыми коммитами.
-
rebase.missingCommitsCheck error
Не позволит удалять коммиты во время rebase.
Источник: https://jvns.ca/blog/2024/02/16/popular-git-config-options/
👍7
День 1871. #ВопросыНаСобеседовании #ASPNET
Самые часто задаваемые вопросы на собеседовании по C#
30. Какие существуют возможности управления конфигурацией приложения .NET Core в разных средах (разработка/тестирование/производство)?
.NET Core предоставляет решение для управления конфигурациями в различных средах с помощью поставщиков конфигурации.
Во-первых, задать среду. Приложения .NET Core делают это, читая переменную окружения ASPNETCORE_ENVIRONMENT. Значение по умолчанию – "Production". Формально значение может быть любым, но лучше придерживаться одного из трёх стандартных:
- Development
- Staging
- Production
Это даст доступ, например, к методам расширения, таким как IHostingEnvironment.IsDevelopment(). Значение можно задать в переменных окружения машины. IDE, такие как Visual Studio, также могут брать это значение из файла launch.json из корня проекта.
Кроме того, среду можно задать принудительно в коде, вручную настроив веб хост:
Среда определяется на раннем этапе старта приложения, поэтому дальнейшую настройку можно определять в зависимости от среды. Например, для управления конфигурацией можно использовать файл appsettings.json, который является файлом по умолчанию, считываемым приложением .NET Core. Можно иметь отдельные файлы конфигурации для каждой среды, например appsettings.Development.json или appsettings.Production.json, что даёт возможность переопределить настройки по умолчанию.
Вообще есть несколько вариантов настройки:
1. Использовать различные настройки в коде в зависимости от среды, например:
2. Использовать различные файлы appsettings.{Environment}.json для хранения конфигураций, специфичных для среды.
3. Использовать поставщика конфигурации переменных окружения, чтобы задать параметры, специфичные для конкретной машины.
4. Важно исключить конфиденциальные данные, такие как строки подключения и секреты приложений, из системы управления версиями (например, файлов appsettings.json). Их лучше хранить либо в переменных среды, либо использовать безопасные методы, такие как пользовательские секреты в среде разработки или сервисы вроде Azure Key Vault в производственной среде.
В приложении использовать интерфейс IConfiguration для доступа к настроенным параметрам. См. подробнее.
См. также про изменения в конфигурации введённые в .NET6.
Источники:
- https://dev.to/bytehide/net-core-interview-question-answers-4bc1
- Эндрю Лок “ASP.NET Core в действии”. 2-е изд. – М.: ДМК Пресс, 2021. Глава 11.
Самые часто задаваемые вопросы на собеседовании по C#
30. Какие существуют возможности управления конфигурацией приложения .NET Core в разных средах (разработка/тестирование/производство)?
.NET Core предоставляет решение для управления конфигурациями в различных средах с помощью поставщиков конфигурации.
Во-первых, задать среду. Приложения .NET Core делают это, читая переменную окружения ASPNETCORE_ENVIRONMENT. Значение по умолчанию – "Production". Формально значение может быть любым, но лучше придерживаться одного из трёх стандартных:
- Development
- Staging
- Production
Это даст доступ, например, к методам расширения, таким как IHostingEnvironment.IsDevelopment(). Значение можно задать в переменных окружения машины. IDE, такие как Visual Studio, также могут брать это значение из файла launch.json из корня проекта.
Кроме того, среду можно задать принудительно в коде, вручную настроив веб хост:
var builder = new WebHostBuilder()
.UseEnvironment(Environments.Production)
.UseKestrel()
.… ;
Среда определяется на раннем этапе старта приложения, поэтому дальнейшую настройку можно определять в зависимости от среды. Например, для управления конфигурацией можно использовать файл appsettings.json, который является файлом по умолчанию, считываемым приложением .NET Core. Можно иметь отдельные файлы конфигурации для каждой среды, например appsettings.Development.json или appsettings.Production.json, что даёт возможность переопределить настройки по умолчанию.
Вообще есть несколько вариантов настройки:
1. Использовать различные настройки в коде в зависимости от среды, например:
…
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
…
2. Использовать различные файлы appsettings.{Environment}.json для хранения конфигураций, специфичных для среды.
3. Использовать поставщика конфигурации переменных окружения, чтобы задать параметры, специфичные для конкретной машины.
4. Важно исключить конфиденциальные данные, такие как строки подключения и секреты приложений, из системы управления версиями (например, файлов appsettings.json). Их лучше хранить либо в переменных среды, либо использовать безопасные методы, такие как пользовательские секреты в среде разработки или сервисы вроде Azure Key Vault в производственной среде.
В приложении использовать интерфейс IConfiguration для доступа к настроенным параметрам. См. подробнее.
См. также про изменения в конфигурации введённые в .NET6.
Источники:
- https://dev.to/bytehide/net-core-interview-question-answers-4bc1
- Эндрю Лок “ASP.NET Core в действии”. 2-е изд. – М.: ДМК Пресс, 2021. Глава 11.
👍22
День 1872. #ЗаметкиНаПолях
Простая Шина Сообщений в Памяти, Используя Каналы. Начало
Обмен сообщениями играет важную роль в современной архитектуре ПО, обеспечивая координацию между слабосвязанными компонентами. Шина сообщений в памяти особенно полезна, когда критическими требованиями являются высокая производительность и низкая задержка.
Недостатки
- Потеря сообщений при сбое процесса программы.
- Работает только внутри одного процесса, поэтому бесполезна в распределённых системах.
Практический вариант использования — создание модульного монолита. Можно реализовать связь между модулями через доменные события. Когда нужно будет выделить какой-то модуль в отдельный сервис, можно заменить шину в памяти на распределённую.
Абстракции для шины
Нам нужны две абстракции для публикации сообщений и для обработчика сообщений.
Интерфейс IEventBus предоставляет метод PublishAsync для публикации сообщений. Также определено ограничение, которое позволяет передавать только экземпляр IDomainEvent.
Используем MediatR для модели издатель-подписчик. Интерфейс IDomainEvent будет наследоваться от INotification. Это позволит легко определять обработчики IDomainEvent с помощью INotificationHandler<T>. Кроме того, в IDomainEvent добавим идентификатор, чтобы отслеживать выполнение. Абстрактный класс DomainEvent будет базовым для конкретных реализаций.
Простая очередь в памяти с использованием каналов
Пространство имён System.Threading.Channels предоставляет структуры данных для асинхронной передачи сообщений между производителями и потребителями. Производители асинхронно создают данные, а потребители асинхронно потребляют их. В отличие от традиционных очередей сообщений, каналы полностью работают в памяти. Недостатком этого подхода является возможность потери сообщения в случае сбоя приложения.
MessageQueue создаёт неограниченный канал, т.е. у канала может быть любое количество читателей и писателей. Он также предоставляет ChannelReader и ChannelWriter, которые позволяют клиентам публиковать и потреблять сообщения.
Зарегистрируем сервис как синглтон:
Реализация шины событий
Класс EventBus использует MessageQueue для доступа к ChannelWriter и записи события в канал.
Шину также регистрируем как синглтон, т.к. она не сохраняет состояния:
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/lightweight-in-memory-message-bus-using-dotnet-channels
Простая Шина Сообщений в Памяти, Используя Каналы. Начало
Обмен сообщениями играет важную роль в современной архитектуре ПО, обеспечивая координацию между слабосвязанными компонентами. Шина сообщений в памяти особенно полезна, когда критическими требованиями являются высокая производительность и низкая задержка.
Недостатки
- Потеря сообщений при сбое процесса программы.
- Работает только внутри одного процесса, поэтому бесполезна в распределённых системах.
Практический вариант использования — создание модульного монолита. Можно реализовать связь между модулями через доменные события. Когда нужно будет выделить какой-то модуль в отдельный сервис, можно заменить шину в памяти на распределённую.
Абстракции для шины
Нам нужны две абстракции для публикации сообщений и для обработчика сообщений.
Интерфейс IEventBus предоставляет метод PublishAsync для публикации сообщений. Также определено ограничение, которое позволяет передавать только экземпляр IDomainEvent.
public interface IEventBus
{
Task PublishAsync<T>(
T domainEvent,
CancellationToken ct = default)
where T : class, IDomainEvent;
}
Используем MediatR для модели издатель-подписчик. Интерфейс IDomainEvent будет наследоваться от INotification. Это позволит легко определять обработчики IDomainEvent с помощью INotificationHandler<T>. Кроме того, в IDomainEvent добавим идентификатор, чтобы отслеживать выполнение. Абстрактный класс DomainEvent будет базовым для конкретных реализаций.
using MediatR;
public interface IDomainEvent : INotification
{
Guid Id { get; init; }
}
public abstract record DomainEvent(Guid Id)
: IDomainEvent;
Простая очередь в памяти с использованием каналов
Пространство имён System.Threading.Channels предоставляет структуры данных для асинхронной передачи сообщений между производителями и потребителями. Производители асинхронно создают данные, а потребители асинхронно потребляют их. В отличие от традиционных очередей сообщений, каналы полностью работают в памяти. Недостатком этого подхода является возможность потери сообщения в случае сбоя приложения.
MessageQueue создаёт неограниченный канал, т.е. у канала может быть любое количество читателей и писателей. Он также предоставляет ChannelReader и ChannelWriter, которые позволяют клиентам публиковать и потреблять сообщения.
internal class MessageQueue
{
private readonly Channel<IDomainEvent> _channel =
Channel.CreateUnbounded<IDomainEvent>();
public ChannelReader<IDomainEvent>
Reader => _channel.Reader;
public ChannelWriter<IDomainEvent>
Writer => _channel.Writer;
}
Зарегистрируем сервис как синглтон:
builder.Services.AddSingleton<MessageQueue>();
Реализация шины событий
Класс EventBus использует MessageQueue для доступа к ChannelWriter и записи события в канал.
internal class EventBus(MessageQueue queue)
: IEventBus
{
public async Task PublishAsync<T>(
T domainEvent,
CancellationToken ct = default)
where T : class, IDomainEvent
{
await queue.Writer.WriteAsync(
domainEvent, ct);
}
}
Шину также регистрируем как синглтон, т.к. она не сохраняет состояния:
builder.Services.AddSingleton<IEventBus, EventBus>();
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/lightweight-in-memory-message-bus-using-dotnet-channels
👍17
День 1873. #ЗаметкиНаПолях
Простая Шина Сообщений в Памяти, Используя Каналы. Окончание
Начало
Потребление событий
Потребление опубликованного IDomainEvent можно реализовать через фоновый сервис (IHostedService). EventProcessorJob использует MessageQueue для чтения (потребления) сообщений. Используем ChannelReader.ReadAllAsync, чтобы получить IAsyncEnumerable, что позволит асинхронно обрабатывать все сообщения в канале.
IPublisher из MediatR поможет связать IDomainEvent с обработчиками. Если мы используем scoped-сервисы, важно получать их из области, для этого внедрим IServiceScopeFactory.
Зарегистрируем сервис:
Использование шины
Сервис IEventBus запишет сообщение в канал и немедленно вернёт управление. Это позволяет публиковать сообщения неблокирующим способом, что повышает производительность. Допустим, при регистрации нового пользователя, нам нужно опубликовать и обработать доменное событие NewUserEvent. Производитель:
Для потребителя нужно определить реализацию INotificationHandler, обрабатывающую доменное событие NewUserEvent, - NewUserEventHandler. Когда фоновое задание EventProcessorJob считает NewUserEvent из канала, оно опубликует сообщение в MediatR и выполнит обработчик.
Возможные улучшения
- Устойчивость. Мы можем добавить повторные попытки при возникновении исключений, что повысит надёжность шины сообщений.
- Идемпотентность. Надо ли вы обрабатывать одно и то же сообщение дважды? Если нет, стоит регистрировать обработанные события и проверять перед обработкой, не были ли они обработаны ранее.
- Очередь недоставленных сообщений. Иногда мы не можем правильно обработать сообщение. Можно создать постоянное хранилище для этих сообщений, что позволит устранить неполадки позднее.
Источник: https://www.milanjovanovic.tech/blog/lightweight-in-memory-message-bus-using-dotnet-channels
Простая Шина Сообщений в Памяти, Используя Каналы. Окончание
Начало
Потребление событий
Потребление опубликованного IDomainEvent можно реализовать через фоновый сервис (IHostedService). EventProcessorJob использует MessageQueue для чтения (потребления) сообщений. Используем ChannelReader.ReadAllAsync, чтобы получить IAsyncEnumerable, что позволит асинхронно обрабатывать все сообщения в канале.
IPublisher из MediatR поможет связать IDomainEvent с обработчиками. Если мы используем scoped-сервисы, важно получать их из области, для этого внедрим IServiceScopeFactory.
internal class EventProcessorJob(
MessageQueue queue,
IServiceScopeFactory sf,
ILogger<EventProcessorJob> logger)
: BackgroundService
{
protected override async Task
ExecuteAsync(CancellationToken ct)
{
await foreach (IDomainEvent e in
queue.Reader.ReadAllAsync(ct))
{
try
{
using var scope = sf.CreateScope();
var pub = scope.ServiceProvider
.GetRequiredService<IPublisher>();
await pub.Publish(e, ct);
}
catch (Exception ex)
{
logger.LogError(ex,
"Failed! {DomainEventId}",
e.Id);
}
}
}
}
Зарегистрируем сервис:
csharp
builder.Services
.AddHostedService<EventProcessorJob>();
Использование шины
Сервис IEventBus запишет сообщение в канал и немедленно вернёт управление. Это позволяет публиковать сообщения неблокирующим способом, что повышает производительность. Допустим, при регистрации нового пользователя, нам нужно опубликовать и обработать доменное событие NewUserEvent. Производитель:
internal class UserService(
IUserRepository userRepo,
IEventBus eventBus)
{
public async Task<User> Register(
User user,
CancellationToken ct)
{
// Добавляем пользователя
userRepo.Add(user);
// Публикуем событие
await eventBus.PublishAsync(
new NewUserEvent(user.Id),
ct);
return user;
}
}
Для потребителя нужно определить реализацию INotificationHandler, обрабатывающую доменное событие NewUserEvent, - NewUserEventHandler. Когда фоновое задание EventProcessorJob считает NewUserEvent из канала, оно опубликует сообщение в MediatR и выполнит обработчик.
internal class NewUserEventHandler
: INotificationHandler<NewUserEvent>
{
public async Task Handle(
NewUserEvent event,
CancellationToken ct)
{
// Асинхронно обрабатываем событие, например
// отправим email новому пользователю
}
}
Возможные улучшения
- Устойчивость. Мы можем добавить повторные попытки при возникновении исключений, что повысит надёжность шины сообщений.
- Идемпотентность. Надо ли вы обрабатывать одно и то же сообщение дважды? Если нет, стоит регистрировать обработанные события и проверять перед обработкой, не были ли они обработаны ранее.
- Очередь недоставленных сообщений. Иногда мы не можем правильно обработать сообщение. Можно создать постоянное хранилище для этих сообщений, что позволит устранить неполадки позднее.
Источник: https://www.milanjovanovic.tech/blog/lightweight-in-memory-message-bus-using-dotnet-channels
👍7
Сколько областей видимости переменных в блоке try...catch...finally, обрабатывающем два вида исключений?
#Quiz #CSharp
#Quiz #CSharp
Anonymous Quiz
14%
1
16%
2
27%
3
38%
4
5%
5
👍2
День 1874. #ЗаметкиНаПолях
Структура Данных Куча и Очередь с Приоритетом в .NET. Начало
В .NET термин «куча» обычно используется при обсуждении управления памятью. Но сегодня мы рассмотрим, что такое структура данных куча, чем она полезна и как используется в типе PriorityQueue в .NET.
Куча — это структура данных, которая обычно представляется в виде дерева и удовлетворяет следующим правилам:
- Минимальная куча — для каждого узла в дереве значение узла меньше (или равно) значений его дочерних элементов.
- Максимальная куча — для каждого узла в дереве значение узла больше (или равно) значений его дочерних элементов.
На рисунке ниже показана визуализация минимальной кучи, включая некоторые термины. Куча на рисунке - двоичная, (каждый узел имеет до два дочерних). Также говорят, что арность кучи 2.
Обычно отношения описываются так, как если бы куча представляла собой генеалогическое древо:
- корневой узел — узел наверху кучи. В минимальной куче корневой узел имеет наименьшее значение.
- братья — узлы, имеющие общего родителя.
- кузены — узлы, имеющие общего предка.
На рисунке ниже видно, что для каждого узла родительское значение меньше (или равно) дочернему. Однако между братьями нет никакой связи (кроме того, что они больше родителя). Так же, кузены не связаны между собой, но гарантированно больше, чем их общий предок (в минимальной куче). Обратите внимание, что это отличается от двоичного дерева поиска, в котором существуют отношения между братьями.
Кучи обычно реализуются как массивы, в которых каждый элемент представляет узел кучи. Позиция определяет связь между узлами, как показано на рисунке ниже. Здесь:
- Элемент с индексом 0 - корневой. В минимальной куче это минимальное значение в куче.
- Элементы 1 и 2 - дочерние корневого.
- 3 и 4 - дочерние элемента 1, 5 и 6 — дочерние элемента 2 и т. д.
Основное использование кучи — реализация очереди с приоритетом. «Обычная» очередь хранит элементы так же, как очередь в магазине: вы добавляете элементы в очередь на одном конце, и удаляете элементы из очереди с другого конца. Очередь с приоритетом представляет собой разновидность стандартной очереди. Вы по-прежнему можете ставить элементы в очередь и удалять их из очереди, но вместо того, чтобы возвращать элементы в том порядке, в котором вы их добавили, вы всегда возвращаете наименьший из оставшихся элементов.
В качестве примера рассмотрим тип PriorityQueue, появившийся в .NET 6:
Элементы извлекаются в порядке возрастания приоритета. Куча —распространённый (но не единственный) способ реализации очереди с приоритетом. Очередь с приоритетом имеет множество применений. Одно из наиболее известных — графовые алгоритмы, таких как алгоритм Дейкстры, для определения наименьшего расстояния между двумя узлами.
Окончание следует…
Источник: https://andrewlock.net/an-introduction-to-the-heap-data-structure-and-dotnets-priority-queue/
Структура Данных Куча и Очередь с Приоритетом в .NET. Начало
В .NET термин «куча» обычно используется при обсуждении управления памятью. Но сегодня мы рассмотрим, что такое структура данных куча, чем она полезна и как используется в типе PriorityQueue в .NET.
Куча — это структура данных, которая обычно представляется в виде дерева и удовлетворяет следующим правилам:
- Минимальная куча — для каждого узла в дереве значение узла меньше (или равно) значений его дочерних элементов.
- Максимальная куча — для каждого узла в дереве значение узла больше (или равно) значений его дочерних элементов.
На рисунке ниже показана визуализация минимальной кучи, включая некоторые термины. Куча на рисунке - двоичная, (каждый узел имеет до два дочерних). Также говорят, что арность кучи 2.
Обычно отношения описываются так, как если бы куча представляла собой генеалогическое древо:
- корневой узел — узел наверху кучи. В минимальной куче корневой узел имеет наименьшее значение.
- братья — узлы, имеющие общего родителя.
- кузены — узлы, имеющие общего предка.
На рисунке ниже видно, что для каждого узла родительское значение меньше (или равно) дочернему. Однако между братьями нет никакой связи (кроме того, что они больше родителя). Так же, кузены не связаны между собой, но гарантированно больше, чем их общий предок (в минимальной куче). Обратите внимание, что это отличается от двоичного дерева поиска, в котором существуют отношения между братьями.
Кучи обычно реализуются как массивы, в которых каждый элемент представляет узел кучи. Позиция определяет связь между узлами, как показано на рисунке ниже. Здесь:
- Элемент с индексом 0 - корневой. В минимальной куче это минимальное значение в куче.
- Элементы 1 и 2 - дочерние корневого.
- 3 и 4 - дочерние элемента 1, 5 и 6 — дочерние элемента 2 и т. д.
Основное использование кучи — реализация очереди с приоритетом. «Обычная» очередь хранит элементы так же, как очередь в магазине: вы добавляете элементы в очередь на одном конце, и удаляете элементы из очереди с другого конца. Очередь с приоритетом представляет собой разновидность стандартной очереди. Вы по-прежнему можете ставить элементы в очередь и удалять их из очереди, но вместо того, чтобы возвращать элементы в том порядке, в котором вы их добавили, вы всегда возвращаете наименьший из оставшихся элементов.
В качестве примера рассмотрим тип PriorityQueue, появившийся в .NET 6:
// добавляем элементы с разным приоритетом
var q = new PriorityQueue<string, int>([
("A", 15), ("B", 7), ("C", 23), ("D", 2), ("E", 22)
]);
// выбираем элементы в порядке приоритета
while (q.TryDequeue(out var elem, out var priority))
Console.WriteLine($"{elem}: {priority}");
// Вывод
// D: 2
// B: 7
// A: 15
// E: 22
// C: 23
Элементы извлекаются в порядке возрастания приоритета. Куча —распространённый (но не единственный) способ реализации очереди с приоритетом. Очередь с приоритетом имеет множество применений. Одно из наиболее известных — графовые алгоритмы, таких как алгоритм Дейкстры, для определения наименьшего расстояния между двумя узлами.
Окончание следует…
Источник: https://andrewlock.net/an-introduction-to-the-heap-data-structure-and-dotnets-priority-queue/
👍11
День 1875. #ЗаметкиНаПолях
Структура Данных Куча и Очередь с Приоритетом в .NET. Окончание
Начало
Согласно документации, тип PriorityQueue в .NET использует «четвертичную минимальную кучу на основе массива». Т.е. каждый узел в куче имеет 4 дочерних элемента.
Двоичные и d-арные кучи имеют несколько разные характеристики времени выполнения. Двоичные кучи немного быстрее удаляют корневой узел, а d-арные кучи быстрее выполняют некоторые другие операции. В целом, d-арные кучи, как правило, имеют более высокую производительность во время выполнения, чем двоичные кучи, из-за принципа работы кэширования памяти, поэтому их часто предпочитают.
PriorityQueue в .NET имеет множество методов, типичных для реализации очереди с приоритетом:
PriorityQueue имеет несколько других вспомогательных методов для контроля ее работы:
Источник: https://andrewlock.net/an-introduction-to-the-heap-data-structure-and-dotnets-priority-queue/
Структура Данных Куча и Очередь с Приоритетом в .NET. Окончание
Начало
Согласно документации, тип PriorityQueue в .NET использует «четвертичную минимальную кучу на основе массива». Т.е. каждый узел в куче имеет 4 дочерних элемента.
Двоичные и d-арные кучи имеют несколько разные характеристики времени выполнения. Двоичные кучи немного быстрее удаляют корневой узел, а d-арные кучи быстрее выполняют некоторые другие операции. В целом, d-арные кучи, как правило, имеют более высокую производительность во время выполнения, чем двоичные кучи, из-за принципа работы кэширования памяти, поэтому их часто предпочитают.
PriorityQueue в .NET имеет множество методов, типичных для реализации очереди с приоритетом:
// создание с одновременным «упорядочиванием» в кучу
var queue = new PriorityQueue<string, int>([
("A", 15), ("B", 7), ("C", 23), ("D", 2), ("E", 22)
]);
// Поиск минимального элемента без извлечения
string peek = queue.Peek(); // "D"
// Извлечение минимального элемента
// Это приводит к удалению корня, поэтому
// куча должна быть «пересортирована»
string dequeue = queue.Dequeue(); // "D"
// Добавление элемента с приоритетом
// Скорее всего приводит к «пересортировке»
queue.Enqueue(element: "F", priority: 42);
// Добавление нескольких элементов
queue.EnqueueRange([("G", 3), ("H", 13)]);
// Одновременное извлечение и добавление нового элемента
// Т.к. для каждой операции требуется «пересортировка», это более эффективно, чем последовательный вызов Dequeue() и Enqueue()
string deque1 =
queue.DequeueEnqueue("I", 19); // "G"
// И наоборот
string deque2 =
queue.EnqueueDequeue("J", 31); // "H"
PriorityQueue имеет несколько других вспомогательных методов для контроля ее работы:
// Выдаёт элементы, не учитывая приоритет, позволяя их перечислить
var unordered = queue.UnorderedItems;
// Количество элементов
var count = queue.Count;
// Очистка очереди
queue.Clear();
// Позволяет задать компаратор для приоритета. По умолчанию это минимальная куча, следующий код создаёт максимальную кучу
var inverse = Comparer<int>
.Create((a, b) => 0 - a.CompareTo(b));
var maxQueue =
new PriorityQueue<string, int>(inverse);
// Уменьшает ёмкость массива, лежащего в основе кучи,
// если новая ёмкость меньше 90% от текущей
queue.TrimExcess();
Источник: https://andrewlock.net/an-introduction-to-the-heap-data-structure-and-dotnets-priority-queue/
👍4
День 1876. #Курсы
Изучаем ИИ в .NET 8 с помощью Новых Руководств
Если вы думали о том, чтобы внедрить в свои .NET-приложения искусственный интеллект и большие языковые модели (LLM), сейчас самое время. Microsoft предлагают новые краткие руководства, которые помогут вам.
Недавно выпущены несколько кратких руководств с практическими примерами приложений, которые вы можете использовать с большими языковыми моделями от OpenAI (скоро появятся и другие модели):
- Саммари текста
- Построение приложения чата
- Анализ данных в ИИ-чате
- Azure функция с ИИ
- Генерация изображений
Каждое из них по шагам знакомит вас с кодом, необходимым для выполнения этой темы, с использованием Azure OpenAI SDK. Скоро мы также будут добавлены версии этих примеров с использованием Semantic Kernel SDK.
Если ChatGPT, LLM, модели и OpenAI — это новые для вас термины или вы только начинаете работать в этой области, вот несколько дополнительных ресурсов, которые вы можете использовать, чтобы помочь изучить основные концепции:
- Get started with OpenAI in .NET – Об использовании OpenAI в .NET.
- Get started with OpenAI Completions with .NET – Введение в завершения с помощью OpenAI: ответы, генерируемые такой моделью, как GPT.
- Level up your GPT game with prompt engineering – Введение в запросы к ИИ, как их усовершенствовать и получать более релевантные результаты.
- Get started with ChatGPT in .NET - Описывает, что такое ChatGPT, а также основные понятия, такие как роли и история чата.
- Плейлист видео: Generative AI with .NET for Beginners
В Microsoft ждут ваших отзывов и предложений о вашем опыте изучения ИИ в .NET и о том, как сделать его лучше. Вы можете принять участие в опросе разработчиков .NET + AI.
Источник: https://devblogs.microsoft.com/dotnet/get-started-with-dotnet-ai-quickstarts/
Изучаем ИИ в .NET 8 с помощью Новых Руководств
Если вы думали о том, чтобы внедрить в свои .NET-приложения искусственный интеллект и большие языковые модели (LLM), сейчас самое время. Microsoft предлагают новые краткие руководства, которые помогут вам.
Недавно выпущены несколько кратких руководств с практическими примерами приложений, которые вы можете использовать с большими языковыми моделями от OpenAI (скоро появятся и другие модели):
- Саммари текста
- Построение приложения чата
- Анализ данных в ИИ-чате
- Azure функция с ИИ
- Генерация изображений
Каждое из них по шагам знакомит вас с кодом, необходимым для выполнения этой темы, с использованием Azure OpenAI SDK. Скоро мы также будут добавлены версии этих примеров с использованием Semantic Kernel SDK.
Если ChatGPT, LLM, модели и OpenAI — это новые для вас термины или вы только начинаете работать в этой области, вот несколько дополнительных ресурсов, которые вы можете использовать, чтобы помочь изучить основные концепции:
- Get started with OpenAI in .NET – Об использовании OpenAI в .NET.
- Get started with OpenAI Completions with .NET – Введение в завершения с помощью OpenAI: ответы, генерируемые такой моделью, как GPT.
- Level up your GPT game with prompt engineering – Введение в запросы к ИИ, как их усовершенствовать и получать более релевантные результаты.
- Get started with ChatGPT in .NET - Описывает, что такое ChatGPT, а также основные понятия, такие как роли и история чата.
- Плейлист видео: Generative AI with .NET for Beginners
В Microsoft ждут ваших отзывов и предложений о вашем опыте изучения ИИ в .NET и о том, как сделать его лучше. Вы можете принять участие в опросе разработчиков .NET + AI.
Источник: https://devblogs.microsoft.com/dotnet/get-started-with-dotnet-ai-quickstarts/
День 1877. #ЧтоНовенького
Garnet — Быстрое Хранилище Кэша с Открытым Кодом
Исследователи Microsoft уже почти десять лет работают над механизмами хранения данных для поддержки быстрого развития интерактивных веб-приложений и сервисов. Новая система хранения кэша Garnet, которая предлагает ряд преимуществ по сравнению с другими хранилищами кэша, была развернута в нескольких сервисах в Microsoft, например, в платформе Windows & Web Experiences, Azure Resource Manager и Azure Resource Graph, а также теперь доступна для загрузки.
Проблема с хранилищем кэша
Программный уровень хранилища кэша, развернутый как отдельно масштабируемый удаленный процесс, может снизить затраты на запросы данных и повысить производительность приложений. Это способствовало развитию индустрии хранилищ, включая многие системы с открытым исходным кодом, такие как Redis, Memcached, KeyDB и Dragonfly.
Современные кэши предлагают богатые API и наборы функций: необработанные строки, аналитические структуры данных, такие как Hyperloglog, и сложные типы данных, такие как отсортированные наборы и хэши. Они позволяют пользователям задавать точки восстановления и восстанавливать кэш, создавать фрагменты данных, поддерживать реплицированные копии, а также поддерживать транзакции и пользовательские расширения.
Однако существующие системы достигают этого богатства функций за счёт сохранения простоты конструкции системы, что ограничивает возможность полного использования новейших аппаратных возможностей (например, нескольких ядер, многоуровневого хранилища, быстрых сетей). Кроме того, многие из этих систем специально не предназначены для лёгкого расширения разработчиками приложений или для хорошей работы на различных платформах и операционных системах.
Garnet
В Microsoft Research работу над базой данных «ключ-значение» начали в 2016 году, тогда она называлась FASTER. С 2021 года, основываясь на требованиях сценариев использования, в Microsoft начали создавать новое удаленное хранилище кэша со всеми необходимыми функциями – Garnet.
Преимущества
1. Использует популярный протокол RESP, что позволяет использовать Garnet из немодифицированных клиентов Redis, доступных сегодня в большинстве языков программирования.
2. Предлагает гораздо лучшую масштабируемость и пропускную способность благодаря множеству клиентских подключений и небольшим пакетам, что приводит к экономии затрат для крупных приложений и сервисов.
3. Меньшие задержки на 99-м и 99,9-м процентилях, что имеет решающее значение для реальных сценариев.
4. Кроссплатформенный, расширяемый и современный. Cпроектирован таким образом, чтобы под него можно было легко разрабатывать ПО без ущерба для производительности. Благодаря использованию .NET, обеспечивает высочайшую производительность как в Linux, так и в Windows.
Возможности
1. Широкий спектр API, включая необработанные строки, аналитические и объектные операции, описанные ранее.
2. Режим кластера с сегментированием, репликацией и динамической миграцией ключей.
3. Транзакции RESP на стороне клиента и хранимые процедуры на стороне сервера на C#, что позволяет пользователям определять собственные операции как с необработанными строками, так и с новыми типами объектов.
Производительность
Судя по тестам в созданной Microsoft (sic!) утилите Resp.benchmark, Garnet превосходит конкурентов по производительности. Подробности можно найти здесь.
Кластерный режим
Помимо выполнения на одном узле, Garnet поддерживает режим кластера, который позволяет пользователям создавать и управлять сегментированным и реплицируемым развёртыванием. Garnet также поддерживает эффективную и динамическую схему миграции ключей для перебалансировки узлов. Пользователи могут использовать стандартные команды кластера Redis для создания кластеров Garnet и управления ими, а узлы общаются между собой для обмена данными и развития состояния кластера.
Источник: https://www.microsoft.com/en-us/research/blog/introducing-garnet-an-open-source-next-generation-faster-cache-store-for-accelerating-applications-and-services/
Garnet — Быстрое Хранилище Кэша с Открытым Кодом
Исследователи Microsoft уже почти десять лет работают над механизмами хранения данных для поддержки быстрого развития интерактивных веб-приложений и сервисов. Новая система хранения кэша Garnet, которая предлагает ряд преимуществ по сравнению с другими хранилищами кэша, была развернута в нескольких сервисах в Microsoft, например, в платформе Windows & Web Experiences, Azure Resource Manager и Azure Resource Graph, а также теперь доступна для загрузки.
Проблема с хранилищем кэша
Программный уровень хранилища кэша, развернутый как отдельно масштабируемый удаленный процесс, может снизить затраты на запросы данных и повысить производительность приложений. Это способствовало развитию индустрии хранилищ, включая многие системы с открытым исходным кодом, такие как Redis, Memcached, KeyDB и Dragonfly.
Современные кэши предлагают богатые API и наборы функций: необработанные строки, аналитические структуры данных, такие как Hyperloglog, и сложные типы данных, такие как отсортированные наборы и хэши. Они позволяют пользователям задавать точки восстановления и восстанавливать кэш, создавать фрагменты данных, поддерживать реплицированные копии, а также поддерживать транзакции и пользовательские расширения.
Однако существующие системы достигают этого богатства функций за счёт сохранения простоты конструкции системы, что ограничивает возможность полного использования новейших аппаратных возможностей (например, нескольких ядер, многоуровневого хранилища, быстрых сетей). Кроме того, многие из этих систем специально не предназначены для лёгкого расширения разработчиками приложений или для хорошей работы на различных платформах и операционных системах.
Garnet
В Microsoft Research работу над базой данных «ключ-значение» начали в 2016 году, тогда она называлась FASTER. С 2021 года, основываясь на требованиях сценариев использования, в Microsoft начали создавать новое удаленное хранилище кэша со всеми необходимыми функциями – Garnet.
Преимущества
1. Использует популярный протокол RESP, что позволяет использовать Garnet из немодифицированных клиентов Redis, доступных сегодня в большинстве языков программирования.
2. Предлагает гораздо лучшую масштабируемость и пропускную способность благодаря множеству клиентских подключений и небольшим пакетам, что приводит к экономии затрат для крупных приложений и сервисов.
3. Меньшие задержки на 99-м и 99,9-м процентилях, что имеет решающее значение для реальных сценариев.
4. Кроссплатформенный, расширяемый и современный. Cпроектирован таким образом, чтобы под него можно было легко разрабатывать ПО без ущерба для производительности. Благодаря использованию .NET, обеспечивает высочайшую производительность как в Linux, так и в Windows.
Возможности
1. Широкий спектр API, включая необработанные строки, аналитические и объектные операции, описанные ранее.
2. Режим кластера с сегментированием, репликацией и динамической миграцией ключей.
3. Транзакции RESP на стороне клиента и хранимые процедуры на стороне сервера на C#, что позволяет пользователям определять собственные операции как с необработанными строками, так и с новыми типами объектов.
Производительность
Судя по тестам в созданной Microsoft (sic!) утилите Resp.benchmark, Garnet превосходит конкурентов по производительности. Подробности можно найти здесь.
Кластерный режим
Помимо выполнения на одном узле, Garnet поддерживает режим кластера, который позволяет пользователям создавать и управлять сегментированным и реплицируемым развёртыванием. Garnet также поддерживает эффективную и динамическую схему миграции ключей для перебалансировки узлов. Пользователи могут использовать стандартные команды кластера Redis для создания кластеров Garnet и управления ими, а узлы общаются между собой для обмена данными и развития состояния кластера.
Источник: https://www.microsoft.com/en-us/research/blog/introducing-garnet-an-open-source-next-generation-faster-cache-store-for-accelerating-applications-and-services/
👍16
День 1878. #УрокиРазработки
Уроки 50 Лет Разработки ПО
Урок 1. Если вы неверно определили требования, неважно, как хорошо вы выполните остальную часть работы
IT-отдел взялся за создание новой информационной системы для своей компании. Разработчики считали, что прекрасно понимают требования и без опроса пользователей. Однако реакция пользователей на готовую систему была: «А если серьезно, где наше приложение?» Они категорически забраковали систему. Отказ от взаимодействия с пользователями, которое помогает убедиться в правильном понимании требований, был серьёзным упущением. Разработчики в итоге переделали систему, на этот раз внимательно прислушиваясь к пользователям. Это был дорогой урок о важности участия клиента в составлении требований.
Требования — это фундамент для всей последующей работы над проектом. Многочисленные исследования показали, что эффективная разработка и своевременное информирование о требованиях являются решающими факторами успеха любого проекта. И наоборот, неадекватное ви́дение проекта, неполные и неточные требования, а также меняющиеся требования и цели проекта — частые причины неудачи. При отсутствии качественной проработки требований заинтересованные стороны могут удивиться тому, что предлагает команда разработчиков. Такие программные сюрпризы часто оказываются неприятными.
Когда?
Нереально иметь полный набор требований перед началом реализации. Всегда будут появляться новые идеи, изменения и исправления, которые вы должны учитывать в своих планах развития. Но для любой части системы, которую вы создаёте, необходимо иметь как можно более полные и правильные требования. Либо планируйте доработку после окончания реализации. В проектах, практикующих Agile, предусмотрен дополнительный этап проверки требований. Чем дальше первоначальные требования от того, что действительно нужно клиентам, тем больше доработок понадобится. Учитывая, что всегда можно что-то добавить, вы никогда не сможете идеально выполнить требования. Но оговорённый объём разработки требует, чтобы вы сделали всё правильно, иначе успеха не видать.
Если вы создаёте инновационный продукт, т.е. никто и никогда не делал ничего подобного, то у вас вряд ли всё получится с первой попытки. Первая попытка — по сути, этап проверки гипотез и определения требований экспериментальным путем. Однако в конечном счёте эксперименты приведут к тому, что вы разберётесь в возможностях и характеристиках нового продукта — его требованиях.
Как?
Ничто не заменит постоянного взаимодействия с клиентами. Нельзя просто провести встречу в самом начале, а затем сказать клиентам: «Мы позвоним вам, когда закончим». В идеале команда должна быть на связи с представителями клиентов на протяжении всего времени разработки проекта. У разработчиков будет много вопросов и моментов, требующих уточнения.
Участники команды должны прописать общие требования на ранних этапах и постепенно уточнять их в дальнейшем. Команде нужна надёжная частая обратная связь с пользователями, чтобы подтвердить правильность понимания требований и предлагаемых решений.
Не всегда получается уговорить клиентов на такое тесное взаимодействие. Один из способов убедить клиента — привести примеры проблем, с которыми столкнулась организация из-за недостаточного участия клиентов в разработке продукта. Ещё лучше рассказать об опыте, когда взаимодействие с клиентами окупилось. Другой метод — предложить чёткий план взаимодействий для уточнения требований, который может предусматривать ряд неформальных обсуждений, встреч, обзоров требований и утверждения эскизов, прототипов и последовательности релизов.
Клиенты вероятнее позитивно оценят проект и будут охотнее вносить свой вклад, если будут видеть прогресс, например в виде периодического выпуска новых версий работающего ПО. Иногда трудно убедить заказчиков принять обновлённую программную систему. Представители заказчиков, работавшие с командой разработчиков, знающие суть нововведений и понимающие их необходимость, могут помочь облегчить этот переход.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 2.
Уроки 50 Лет Разработки ПО
Урок 1. Если вы неверно определили требования, неважно, как хорошо вы выполните остальную часть работы
IT-отдел взялся за создание новой информационной системы для своей компании. Разработчики считали, что прекрасно понимают требования и без опроса пользователей. Однако реакция пользователей на готовую систему была: «А если серьезно, где наше приложение?» Они категорически забраковали систему. Отказ от взаимодействия с пользователями, которое помогает убедиться в правильном понимании требований, был серьёзным упущением. Разработчики в итоге переделали систему, на этот раз внимательно прислушиваясь к пользователям. Это был дорогой урок о важности участия клиента в составлении требований.
Требования — это фундамент для всей последующей работы над проектом. Многочисленные исследования показали, что эффективная разработка и своевременное информирование о требованиях являются решающими факторами успеха любого проекта. И наоборот, неадекватное ви́дение проекта, неполные и неточные требования, а также меняющиеся требования и цели проекта — частые причины неудачи. При отсутствии качественной проработки требований заинтересованные стороны могут удивиться тому, что предлагает команда разработчиков. Такие программные сюрпризы часто оказываются неприятными.
Когда?
Нереально иметь полный набор требований перед началом реализации. Всегда будут появляться новые идеи, изменения и исправления, которые вы должны учитывать в своих планах развития. Но для любой части системы, которую вы создаёте, необходимо иметь как можно более полные и правильные требования. Либо планируйте доработку после окончания реализации. В проектах, практикующих Agile, предусмотрен дополнительный этап проверки требований. Чем дальше первоначальные требования от того, что действительно нужно клиентам, тем больше доработок понадобится. Учитывая, что всегда можно что-то добавить, вы никогда не сможете идеально выполнить требования. Но оговорённый объём разработки требует, чтобы вы сделали всё правильно, иначе успеха не видать.
Если вы создаёте инновационный продукт, т.е. никто и никогда не делал ничего подобного, то у вас вряд ли всё получится с первой попытки. Первая попытка — по сути, этап проверки гипотез и определения требований экспериментальным путем. Однако в конечном счёте эксперименты приведут к тому, что вы разберётесь в возможностях и характеристиках нового продукта — его требованиях.
Как?
Ничто не заменит постоянного взаимодействия с клиентами. Нельзя просто провести встречу в самом начале, а затем сказать клиентам: «Мы позвоним вам, когда закончим». В идеале команда должна быть на связи с представителями клиентов на протяжении всего времени разработки проекта. У разработчиков будет много вопросов и моментов, требующих уточнения.
Участники команды должны прописать общие требования на ранних этапах и постепенно уточнять их в дальнейшем. Команде нужна надёжная частая обратная связь с пользователями, чтобы подтвердить правильность понимания требований и предлагаемых решений.
Не всегда получается уговорить клиентов на такое тесное взаимодействие. Один из способов убедить клиента — привести примеры проблем, с которыми столкнулась организация из-за недостаточного участия клиентов в разработке продукта. Ещё лучше рассказать об опыте, когда взаимодействие с клиентами окупилось. Другой метод — предложить чёткий план взаимодействий для уточнения требований, который может предусматривать ряд неформальных обсуждений, встреч, обзоров требований и утверждения эскизов, прототипов и последовательности релизов.
Клиенты вероятнее позитивно оценят проект и будут охотнее вносить свой вклад, если будут видеть прогресс, например в виде периодического выпуска новых версий работающего ПО. Иногда трудно убедить заказчиков принять обновлённую программную систему. Представители заказчиков, работавшие с командой разработчиков, знающие суть нововведений и понимающие их необходимость, могут помочь облегчить этот переход.
Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 2.
👍13
День 1879. #Оффтоп
Сегодня порекомендую вам очередное виде с ютуба, а также канал ThePrimeTime. Честно говоря, не смог найти подробной информации об авторе. Знаю только, что он работает в Netflix. Стек у него не .NET, но и в своих видео он мало касается какой-то определённой технологии, больше рассуждает об общих проблемах разработки.
Одно из последних его видео, которое мне понравилось, на самом деле, реакция на видео с канала Awesome (тоже про разработку). Видео называется The Harsh Reality of Good Software (Жёсткая Реальность Хорошего ПО).
В двух словах. Всем плевать, сколько вы потратили времени на ПО, какой красивый и хорошо структурированный у вас код и т.д., и т.п. Программе достаточно сломаться 1 раз у конечного пользователя – и ваше ПО оценивается как говно. Несмотря на всё это, нам надо создавать хорошее ПО. И прочие рассуждения на тему чистого и читаемого кода.
Почему я выбрал реакцию, а не оригинальное видео? Просто ThePrimeTime очень здорово и смешно комментирует, добавляя истории из собственного опыта, а автор оригинала рассказывает довольно сухо, хоть и с претензией на шутки.
Наслаждайтесь (потребуются навыки английского, либо автосубтитры)
https://youtu.be/NiljDyzAOcI
PS: не знал про американские горки в Excel. Это великолепно!!!
Сегодня порекомендую вам очередное виде с ютуба, а также канал ThePrimeTime. Честно говоря, не смог найти подробной информации об авторе. Знаю только, что он работает в Netflix. Стек у него не .NET, но и в своих видео он мало касается какой-то определённой технологии, больше рассуждает об общих проблемах разработки.
Одно из последних его видео, которое мне понравилось, на самом деле, реакция на видео с канала Awesome (тоже про разработку). Видео называется The Harsh Reality of Good Software (Жёсткая Реальность Хорошего ПО).
В двух словах. Всем плевать, сколько вы потратили времени на ПО, какой красивый и хорошо структурированный у вас код и т.д., и т.п. Программе достаточно сломаться 1 раз у конечного пользователя – и ваше ПО оценивается как говно. Несмотря на всё это, нам надо создавать хорошее ПО. И прочие рассуждения на тему чистого и читаемого кода.
Почему я выбрал реакцию, а не оригинальное видео? Просто ThePrimeTime очень здорово и смешно комментирует, добавляя истории из собственного опыта, а автор оригинала рассказывает довольно сухо, хоть и с претензией на шутки.
Наслаждайтесь (потребуются навыки английского, либо автосубтитры)
https://youtu.be/NiljDyzAOcI
PS: не знал про американские горки в Excel. Это великолепно!!!
YouTube
The harsh reality of good software
Recorded live on twitch, GET IN
https://twitch.tv/ThePrimeagen
Become a backend engineer. Its my favorite site
https://boot.dev/?promo=PRIMEYT
This is also the best way to support me is to support yourself becoming a better backend engineer.
Reviewed…
https://twitch.tv/ThePrimeagen
Become a backend engineer. Its my favorite site
https://boot.dev/?promo=PRIMEYT
This is also the best way to support me is to support yourself becoming a better backend engineer.
Reviewed…
👍18
День 1880. #ЧтоНовенького
.NET Smart Components
Новые достижения в области ИИ обещают революционизировать то, как мы взаимодействуем с ПО и используем его. Но добавление функций ИИ в существующее ПО может оказаться сложной задачей. Microsoft представили новые интеллектуальные компоненты .NET — набор компонентов UI на базе ИИ, которые можно быстро и легко добавлять в приложения .NET.
.NET Smart Components находятся на этапе эксперимента и пока доступны для страниц Blazor, MVC и Razor с .NET 6 и более поздних версий. Также планируется предоставить компоненты для других инфраструктур .NET, таких как .NET MAUI, WPF и Windows Forms.
Умная вставка
Smart Paste автоматически заполняет формы, используя данные из буфера обмена пользователя, одним нажатием кнопки. Вы можете использовать её с любой существующей формой веб-приложения. Это помогает пользователям добавлять данные из внешних источников ввода каждого поля в отдельности.
Умные поля для ввода
Вы можете настроить автоматическое завершение предложений в полях для ввода текса, используя свой предпочитаемый стиль текста, тон, политики, URL-адреса и т. д. Это поможет пользователям печатать быстрее.
Умный выпадающий список
Обновляет традиционное поле списка, предлагая предложения на основе семантического соответствия. Это поможет пользователям найти нужный вариант.
Примеры
Демонстрацию работы .NET Smart Components можно посмотреть в этом видео. А попробовать их в Blazor или MVC/RazorPages, используя примеры приложений смарт-компонентов .NET на GitHub.
Для работы .NET Smart Components потребуется развернуть серверную часть Azure OpenAI, и добавить в файл конфигурации в корне решения ключ API, имя развертывания и URL конечной точки.
Добавление в существующее приложение
Чтобы добавить .NET Smart Components в существующие приложения Blazor, MVC или Razor Pages, посмотрите эти руководства:
- Начало работы с .NET Smart Components в Blazor
- Начало работы с .NET Smart Components в MVC или страницах Razor.
Обратная связь и поддержка
.NET Smart Components в настоящее время являются экспериментальными и официально не поддерживаются. В Microsoft хотят знать ваше мнение, полезны ли они и как их улучшить.
Источник: https://devblogs.microsoft.com/dotnet/introducing-dotnet-smart-components/
.NET Smart Components
Новые достижения в области ИИ обещают революционизировать то, как мы взаимодействуем с ПО и используем его. Но добавление функций ИИ в существующее ПО может оказаться сложной задачей. Microsoft представили новые интеллектуальные компоненты .NET — набор компонентов UI на базе ИИ, которые можно быстро и легко добавлять в приложения .NET.
.NET Smart Components находятся на этапе эксперимента и пока доступны для страниц Blazor, MVC и Razor с .NET 6 и более поздних версий. Также планируется предоставить компоненты для других инфраструктур .NET, таких как .NET MAUI, WPF и Windows Forms.
Умная вставка
Smart Paste автоматически заполняет формы, используя данные из буфера обмена пользователя, одним нажатием кнопки. Вы можете использовать её с любой существующей формой веб-приложения. Это помогает пользователям добавлять данные из внешних источников ввода каждого поля в отдельности.
Умные поля для ввода
Вы можете настроить автоматическое завершение предложений в полях для ввода текса, используя свой предпочитаемый стиль текста, тон, политики, URL-адреса и т. д. Это поможет пользователям печатать быстрее.
Умный выпадающий список
Обновляет традиционное поле списка, предлагая предложения на основе семантического соответствия. Это поможет пользователям найти нужный вариант.
Примеры
Демонстрацию работы .NET Smart Components можно посмотреть в этом видео. А попробовать их в Blazor или MVC/RazorPages, используя примеры приложений смарт-компонентов .NET на GitHub.
Для работы .NET Smart Components потребуется развернуть серверную часть Azure OpenAI, и добавить в файл конфигурации в корне решения ключ API, имя развертывания и URL конечной точки.
Добавление в существующее приложение
Чтобы добавить .NET Smart Components в существующие приложения Blazor, MVC или Razor Pages, посмотрите эти руководства:
- Начало работы с .NET Smart Components в Blazor
- Начало работы с .NET Smart Components в MVC или страницах Razor.
Обратная связь и поддержка
.NET Smart Components в настоящее время являются экспериментальными и официально не поддерживаются. В Microsoft хотят знать ваше мнение, полезны ли они и как их улучшить.
Источник: https://devblogs.microsoft.com/dotnet/introducing-dotnet-smart-components/
👍7
День 1881. #ЗаметкиНаПолях
Массовая Вставка в C# и EF Core. Начало
Независимо от того, создаёте ли вы платформу анализа данных, переносите устаревшую систему или привлекаете новых пользователей, вероятно, наступит момент, когда вам потребуется вставить огромный объём данных в БД. Традиционные методы слишком медленные. Поэтому необходимо понимание методов быстрой массовой вставки в C#. Сегодня рассмотрим несколько вариантов.
Примечание: Мы не будем рассматривать некоторые крайние варианты, вроде ручной генерации SQL запросов или использования параметров, возвращающих табличные значения (Table-Valued parameters).
Допустим, нам нужно вставить множество экземпляров класса User в соответствующую таблицу БД.
1. Простой (плохой) пример EF Core
Для сравнения рассмотрим простейший (плохой) вариант, где пользователи добавляются по одному:
Результаты ужасные (как и ожидалось из-за множества запросов в БД):
Большее количество проверять бессмысленно, они занимают слишком много времени. Возьмём это за пример, как не надо делать массовую вставку.
*Заметьте, что время приведено только для сравнения между вариантами.
2. Добавление всех и сохранение в EF Core
Можно поправить пример выше, сначала добавив все записи в контекст, а затем 1 раз вызвав SaveChanges().
Результаты:
3. AddRange в EF Core
Мы можем добавить в контекст сразу всю коллекцию, используя метод AddRange:
Результаты аналогичны примеру выше, но код короче.
4. EF Core Bulk Extensions
Библиотеку EF Core Bulk Extensions можно использовать для повышения производительности. Кстати, она позволяет делать не только массовую вставку. Библиотека с открытым кодом, но платная для коммерческого использования. Для нашего варианта подойдёт метод BulkInsertAsync:
Результаты впечатляющие:
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/fast-sql-bulk-inserts-with-csharp-and-ef-core
Массовая Вставка в C# и EF Core. Начало
Независимо от того, создаёте ли вы платформу анализа данных, переносите устаревшую систему или привлекаете новых пользователей, вероятно, наступит момент, когда вам потребуется вставить огромный объём данных в БД. Традиционные методы слишком медленные. Поэтому необходимо понимание методов быстрой массовой вставки в C#. Сегодня рассмотрим несколько вариантов.
Примечание: Мы не будем рассматривать некоторые крайние варианты, вроде ручной генерации SQL запросов или использования параметров, возвращающих табличные значения (Table-Valued parameters).
Допустим, нам нужно вставить множество экземпляров класса User в соответствующую таблицу БД.
1. Простой (плохой) пример EF Core
Для сравнения рассмотрим простейший (плохой) вариант, где пользователи добавляются по одному:
using var ctx = new AppDbContext();
foreach (var u in GetUsers())
{
ctx.Users.Add(u);
await ctx.SaveChangesAsync();
}
Результаты ужасные (как и ожидалось из-за множества запросов в БД):
100 пользователей - 20 мс,
1000 – 260 мс,
10000 – 8,86 с.
Большее количество проверять бессмысленно, они занимают слишком много времени. Возьмём это за пример, как не надо делать массовую вставку.
*Заметьте, что время приведено только для сравнения между вариантами.
2. Добавление всех и сохранение в EF Core
Можно поправить пример выше, сначала добавив все записи в контекст, а затем 1 раз вызвав SaveChanges().
…
foreach (var u in GetUsers())
context.Users.Add(u);
await ctx.SaveChangesAsync();
Результаты:
100 - 2 мс,
1000 - 18 мс,
10000 - 203 мс,
100000 - 2,13 с,
1000000 - 21,56 с.
3. AddRange в EF Core
Мы можем добавить в контекст сразу всю коллекцию, используя метод AddRange:
…
ctx.Users.AddRange(GetUsers());
await context.SaveChangesAsync();
Результаты аналогичны примеру выше, но код короче.
4. EF Core Bulk Extensions
Библиотеку EF Core Bulk Extensions можно использовать для повышения производительности. Кстати, она позволяет делать не только массовую вставку. Библиотека с открытым кодом, но платная для коммерческого использования. Для нашего варианта подойдёт метод BulkInsertAsync:
…
await context.BulkInsertAsync(GetUsers());
Результаты впечатляющие:
100 - 1.9 мс,
1000 - 8 мс,
10000 - 76 мс,
100000 - 742 мс,
1000000 - 8,3 с.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/fast-sql-bulk-inserts-with-csharp-and-ef-core
👍22
День 1882. #ЗаметкиНаПолях
Массовые Вставки в C# и EF Core. Окончание
Начало
5. Dapper
Dapper — это простой преобразователь SQL в объекты .NET. Он позволяет нам легко вставить коллекцию объектов в БД:
Результаты:
6. SQL Bulk Copy
Наконец, для SQL Server мы можем попробовать использовать SqlBulkCopy. Эта реализация немного сложнее. Во-первых, нужно создать объект DataTable, содержащий объекты, которые мы хотим вставить:
Теперь настроим SqlBulkCopy для выполнения вставки:
Производительность невероятно высока:
Итого
SqlBulkCopy работает быстрее всех, однако EF Core Bulk Extensions обеспечивают фантастическую производительность, сохраняя при этом простоту использования, которой славится Entity Framework Core. Поэтому выбор зависит от требований вашего проекта:
- Главное – производительность? SqlBulkCopy или EF Core Bulk Extensions.
- Не хочется платить и нужна простота разработки? Грамотное использование EF Core всё равно даёт неплохие результаты.
Источник: https://www.milanjovanovic.tech/blog/fast-sql-bulk-inserts-with-csharp-and-ef-core
Массовые Вставки в C# и EF Core. Окончание
Начало
5. Dapper
Dapper — это простой преобразователь SQL в объекты .NET. Он позволяет нам легко вставить коллекцию объектов в БД:
using var conn = new SqlConnection(connString);
conn.Open();
var sql = @"
INSERT INTO Users (FirstName, LastName, Email, …)
VALUES (@FirstName, @LastName, @Email, …);";
await conn.ExecuteAsync(sql, GetUsers());
Результаты:
100 пользователей - 10 мс
1000 - 113 мс,
10000 - 1,02 с,
100000 - 10,9 с,
1000000 - 109,065 с.
6. SQL Bulk Copy
Наконец, для SQL Server мы можем попробовать использовать SqlBulkCopy. Эта реализация немного сложнее. Во-первых, нужно создать объект DataTable, содержащий объекты, которые мы хотим вставить:
DataTable GetUsersDataTable()
{
var dt = new DataTable();
dt.Columns.Add(nameof(User.FirstName),
typeof(string));
dt.Columns.Add(nameof(User.LastName),
typeof(string));
dt.Columns.Add(nameof(User.Email),
typeof(string));
…
foreach (var u in GetUsers())
dt.Rows.Add(
u.FirstName, u.LastName, u.Email, …);
return dt;
}
Теперь настроим SqlBulkCopy для выполнения вставки:
using var bc = new SqlBulkCopy(ConnString);
bc.DestinationTableName = "dbo.Users";
bc.ColumnMappings.Add(
nameof(User.FirstName), "FirstName");
bc.ColumnMappings.Add(
nameof(User.LastName), "LastName");
bc.ColumnMappings.Add(
nameof(User.Email), "Email");
await bc.WriteToServerAsync(GetUsersDataTable());
Производительность невероятно высока:
100 – 1,7 мс,
1000 - 7 мс,
10000 - 68 мс,
100000 - 646 мс,
1000000 - 7,34 с.
Итого
SqlBulkCopy работает быстрее всех, однако EF Core Bulk Extensions обеспечивают фантастическую производительность, сохраняя при этом простоту использования, которой славится Entity Framework Core. Поэтому выбор зависит от требований вашего проекта:
- Главное – производительность? SqlBulkCopy или EF Core Bulk Extensions.
- Не хочется платить и нужна простота разработки? Грамотное использование EF Core всё равно даёт неплохие результаты.
Источник: https://www.milanjovanovic.tech/blog/fast-sql-bulk-inserts-with-csharp-and-ef-core
👍16
День 1883. #МоиИнструменты
Назначенные Задания с NCronJob
Hangfire/Quartz или фоновый сервис? А может что-то среднее? Если вам не нужен полноценный планировщик заданий с множеством настроек, но нужно нечто большее, чем просто BackgroundService, рассмотрите NCronJob.
Что это?
Простой и удобный планировщик заданий, работающий поверх IHostedService в .NET. Предоставляет два способа планирования заданий:
- Мгновенные задания — запуск задания прямо сейчас,
- Задания Cron — планирование задания, используя выражение cron.
Идея в том, чтобы иметь простой и лёгкий способ планирования заданий либо повторяющихся через нотацию cron, либо одноразовых (запускаемых с помощью мгновенных заданий). Библиотека построена на основе IHostedService и поэтому идеально подходит для приложений ASP.NET.
Особенности, отличающие NCronJob от BackgroundService:
- 2 вида заданий,
- передача параметров заданиям (как cron, так и мгновенным),
- (Скоро) Уведомления о завершении работы.
Есть и некоторые компромиссы: нет базы данных (что является плюсом, поскольку не нужно ничего настраивать), но это также означает, что задания не сохраняются. Если ваше приложение перезапустится, история исчезнет.
Как использовать?
Библиотека доступна в виде NuGet-пакета:
Для начала определим задание:
Как видите, поддерживаются параметры. Это справедливо как для заданий, запускаемых через cron, так и для мгновенных заданий. Теперь зарегистрируем сервис:
Готово!
Вы также можете запускать мгновенные задания откуда угодно:
В настоящее время авторы пытаются добавить больше функций, поэтому, если вам не хватает важной функциональности, напишите им об этом в GitHub проекта.
Источник: https://steven-giesel.com/blogPost/f58777b8-e10b-4023-845b-9f5ad3b7e48f/ncronjob-scheduling-made-easy
Назначенные Задания с NCronJob
Hangfire/Quartz или фоновый сервис? А может что-то среднее? Если вам не нужен полноценный планировщик заданий с множеством настроек, но нужно нечто большее, чем просто BackgroundService, рассмотрите NCronJob.
Что это?
Простой и удобный планировщик заданий, работающий поверх IHostedService в .NET. Предоставляет два способа планирования заданий:
- Мгновенные задания — запуск задания прямо сейчас,
- Задания Cron — планирование задания, используя выражение cron.
Идея в том, чтобы иметь простой и лёгкий способ планирования заданий либо повторяющихся через нотацию cron, либо одноразовых (запускаемых с помощью мгновенных заданий). Библиотека построена на основе IHostedService и поэтому идеально подходит для приложений ASP.NET.
Особенности, отличающие NCronJob от BackgroundService:
- 2 вида заданий,
- передача параметров заданиям (как cron, так и мгновенным),
- (Скоро) Уведомления о завершении работы.
Есть и некоторые компромиссы: нет базы данных (что является плюсом, поскольку не нужно ничего настраивать), но это также означает, что задания не сохраняются. Если ваше приложение перезапустится, история исчезнет.
Как использовать?
Библиотека доступна в виде NuGet-пакета:
dotnet add package LinkDotNet.NCronJob
Для начала определим задание:
public class PrintHelloWorld : IJob
{
private ILogger<PrintHelloWorld> logger;
public PrintHelloWorld(
ILogger<PrintHelloWorld> logger)
{
this.logger = logger;
}
public Task RunAsync(
JobExecutionContext context,
CancellationToken ct = default)
{
logger.LogInformation(
"Parameter: {Parameter}", context.Parameter);
return Task.CompletedTask;
}
}
Как видите, поддерживаются параметры. Это справедливо как для заданий, запускаемых через cron, так и для мгновенных заданий. Теперь зарегистрируем сервис:
builder.Services.AddNCronJob();
builder.Services
.AddCronJob<PrintHelloWorld>(opt =>
{
// Каждую минуту
opt.CronExpression = "* * * * *";
// необязательный параметр
opt.Parameter = "Hello Parameter";
});
Готово!
Вы также можете запускать мгновенные задания откуда угодно:
public class MyService
{
private IInstantJobRegistry jobReg;
public MyService(IInstantJobRegistry jobReg)
=> this.jobReg = jobReg;
public void MyMethod()
=> jobReg.AddInstantJob<MyJob>(
"Необязательный параметр");
}
В настоящее время авторы пытаются добавить больше функций, поэтому, если вам не хватает важной функциональности, напишите им об этом в GitHub проекта.
Источник: https://steven-giesel.com/blogPost/f58777b8-e10b-4023-845b-9f5ad3b7e48f/ncronjob-scheduling-made-easy
👍21