Опытный разработчик не так давно зашёл в .Net и поставил цель получить сертификат Microsoft. Свой ежедневный прогресс он описывает на канале .Net Разработчик. Заметки об изученном материале, советы по повышению производительности и поддержке мотивации, ин
dbSet.Where(x => x.Deleted == null);И об этом нужно постоянно помнить. Есть отличный способ решить эту проблему с помощью так называемых «глобальных фильтров запросов». В документации даже есть пример: docs.microsoft.com/en-us/e…/filters. Проблема в том, что в примере показано, как это сделать для отдельного объекта. Если в вашей базе данных много таблиц вам придется каждый раз не забывать добавлять конфигурацию. Придётся сделать кое-что посложнее в методе
OnModelCreating контекста:
protected override void
OnModelCreating(ModelBuilder builder)
{
// остальные настройки
var entities = builder.Model
.GetEntityTypes()
.Where(e => e.ClrType.BaseType == typeof(Auditable))
.Select(e => e.ClrType);
Expression<Func<Auditable, bool>>
expression = del => del.Deleted == null;
foreach (var e in entities)
{
var p = Expression.Parameter(e);
var body =
ReplacingExpressionVisitor
.Replace(expression.Parameters.Single(),
p, expression.Body);
builder.Entity(entity)
.HasQueryFilter(
Expression.Lambda(body, p));
}
base.OnModelCreating(builder);
}
}
Сначала мы выбираем все сущности, которые наследуют от Auditable (см. предыдущий пост). Затем создаём выражение expression для фильтрации. Далее перебираем все выбранные сущности и создаём для каждого типа своё выражение путём замены параметра выражения expression на тип конкретной сущности. И наконец с помощью метода HasQueryFilter() добавляем глобальный фильтр.
Теперь все запросы в нашем приложении будут иметь фильтр и не включать мягко удалённые объекты. Если же для каких-то запросов (например, в блоке администрирования) всё-таки нужно получить все объекты, можно использовать метод IgnoreQueryFilters() в запросе.
Источник: dotnetcoretutorials.com/2022/03…entitiesCreated, Updated и Deleted для сущностей. Они полезны для отладки, а поле Deleted позволяет реализовать «мягкое удаление». Есть довольно простой метод, заключающийся в переопределении поведения сохранения Entity Framework.
Во-первых, нужно определить «базовую модель», от которой могут наследоваться все сущности:
public abstract class Auditable
{
public DateTimeOffset Created { get; set; }
public DateTimeOffset? Updated { get; set; }
public DateTimeOffset? Deleted { get; set; }
}
Это абстрактный класс, т.к. не должно быть возможности создавать его экземпляры. И мы используем DateTimeOffset, чтобы не было путаницы с часовыми поясами.
Далее, мы наследуем сущности от этого класса:
public class Customer : Auditable
{
public int Id { get; set; }
public string Name { get; set; }
}
Теперь нужно, чтоб EF обновляла эти значения при сохранении. Есть несколько способов это сделать, но самый простой — переопределить метод сохранения SaveChangesAsync в классе контекста:
public override Task<int>
SaveChangesAsync(CancellationToken ct = default)
{
foreach (var e in
ChangeTracker.Entries<Auditable>())
{
switch (e.State)
{
case EntityState.Added:
e.Entity.Created = DateTimeOffset.UtcNow;
break;
case EntityState.Modified:
e.Entity.Updated = DateTimeOffset.UtcNow;
break;
case EntityState.Deleted:
e.Entity.Deleted = DateTimeOffset.UtcNow;
e.State = EntityState.Modified;
break;
}
}
return base.SaveChangesAsync(ct);
}
Процесс использует обобщённый метод ChangeTracker.Entries<T>(), который получает все добавляемые сущности типа Auditable (и унаследованные от них), и в зависимости от состояния устанавливает дату создания, обновления или удаления. Также заметьте, что для удаляемых сущностей состояние изменяется на Modified. Наконец, вызывается базовый метод SaveChanges, который фактически выполняет сохранение.
Примечание: Начиная с EF Core 5.0, вместо переопределения SaveChanges можно использовать класс SaveChangesInterceptor.
Заметьте, что можно тем же образом обновлять и другие «автоматизированные» поля. Например, сохранять данные о пользователе, который создал, обновил и удалил объект.
Источник: dotnetcoretutorials.com/2022/03…rameworkTab, чтобы принять его, или просто продолжать печатать, чтобы игнорировать, тогда Copilot предложит другие варианты, основываясь на том, что, по его мнению, вы делаете. Нажмите Esc, чтобы полностью удалить предложение, если оно вам мешает.
Хотя Copilot всегда будет показывать наилучшие рекомендации, вы можете использовать Ctrl+Alt+] и Ctrl+Alt+[ для навигации по вариантам, которые он сгенерирует.
Вы можете изменить настройки Copilot в любое время, щёлкнув на значок GitHub Copilot в нижней части окна редактора. Это позволит вам включать или отключать его для конкретных решений и языков программирования.
Команда GitHub Copilot ждёт ваших отзывов тут.
Источник: github.blog/2022-03…dio-2022PiiString на соответствие требованиям.
1. Шифрование или анонимизация PII
Значение PiiString может быть преобразовано в зависимости от используемой логики кодирования.
public interface IPiiEncoder
{
string ToSystemString(PiiString pii);
PiiString ToPiiString(string str);
}
PiiString как обычный текст:
public class PiiAsPlainText : IPiiEncoder
{
public string ToSystemString(PiiString pii)
=> pii.ToString();
public PiiString ToPiiString(string str)
=> new PiiString(str);
}
Анонимизированная PiiString через хэш SHA256:
public class PiiAsSha256 : IPiiEncoder
{
public string ToSystemString(PiiString pii)
{
var data = Encoding.UTF8.GetBytes(pii.ToString());
using var sha = SHA256.Create();
var hash = sha.ComputeHash(data);
return Convert.ToBase64String(hash);
}
public PiiString ToPiiString(string str)
=> throw new NotSupportedException();
}
PiiString в Entity Framework Core
public class PiiConverter
: ValueConverter<PiiString, string>
{
public PiiConverter(IPiiEncoder encoder)
: base(
v => encoder.ToSystemString(v),
v => encoder.ToPiiString(v))
{}
}
2. Соглашение об обработке данных с третьими лицами
Обычно это сводится к тому, чтобы найти все места в исходном коде, где используется PII, а затем обработать эту информацию для юристов. В Visual Studio есть команда Find All References (Найти все ссылки), которая должна упростить задачу.
Также можно рассмотреть возможность включения примеров использования специальной логики для полей PiiString в тесты, что послужило бы своеобразной документацей об использовании.
Итого
PiiString явно берёт на себя управление PII. Гораздо лучше, чем простые строки. Эта реализация не идеальна, например, кажется странным использовать PiiString для даты рождения или пола. Альтернативным способом является использование атрибута, но имейте в виду, что это подразумевает рефлексию, генераторы кода или нечто подобное, что чрезмерно усложнить реализацию.
Полный код классов с примерами шифрования Aes128, использования в System.Text.Json, Newtonsoft.Json, Nlog, Serilog и EFCore здесь.
Источник: gaevoy.com/2022/03…pes.htmlstring для PII:
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Location { get; set; }
}
Однако не очевидно, как шифровать/анонимизировать эти значения в каждом конкретном случае. Кроме того, идентификация таких полей в исходном коде по запросу юристов компании может быть сложной задачей.
Решение
Ввести явный тип, например, PiiString. Он должен быть как можно более взаимозаменяемым со string, чтобы упростить рефакторинг существующего кода, использующего string. В приложении он должен вести себя как обычная строка, однако вне приложения, он должен быть закодирован/зашифрован/хеширован.
Заметьте, что ниже приведена упрощённая версия класса. Полная тут:
public class PiiString
{
private readonly string _string;
public PiiString(string str)
=> _string = str ??
throw new ArgumentNullException(nameof(str));
public override string ToString()
=> _string;
}
Окончание следует…
Источник: gaevoy.com/2022/03…pes.htmlCtrl+S, либо неявно в процессе сборки проекта.
Источник: devblogs.microsoft.com/visuals…-for-you.csproj, .vbproj или .fsproj).
Измените значение целевой платформы с net5.0 на net6.0.
Также посмотрите на критические изменения в версии 6.0.
Обновите среду разработки
На компьютере, который вы используете для разработки, может быть установлена .NET 5.0 — либо автономно, либо вместе с Visual Studio.
Начиная с сервисного обновления Visual Studio 2019 16.11 и 16.9 в июне 2022 г., компонент .NET 5.0 в Visual Studio будет изменён на неподдерживаемый и необязательный. Это означает, что компоненты Visual Studio могут быть установлены без установки .NET 5.0. Обратите внимание, что это не повлияет на существующие установки, и любые ранее установленные компоненты останутся установленными. Хотя вы сможете повторно выбрать этот необязательный компонент в Visual Studio и переустановить его, настоятельно рекомендуется использовать .NET 6.0 с Visual Studio 2022 для создания приложений, работающих в поддерживаемой среде выполнения .NET.
Версии SDK .NET 5.0 будут по-прежнему поддерживаться в VS 16.11 до декабря 2022 года, когда прекратится поддержка .NET Core 3.1, чтобы клиенты .NET Core 3.1 могли продолжать использовать 16.11 для разработки своих приложений. SDK .NET 5.0 не будет использовать среду выполнения .NET 5.0 при запуске сценариев командной строки и не будет поставляться как отдельный пакет SDK.
Источник: devblogs.microsoft.com/dotnet/…t-updategit config — мощная команда. Также вы можете использовать файл конфигурации. Он существует на уровне проекта, где инициализируется Git (/project/.git/config), или в корне (~/.gitconfig). Если конфигурация не указана, Git использует настройки по умолчанию. Рассмотрим некоторые полезные настройки, которые могут улучшить ваш процесс разработки.
1. Выбор редактора по умолчанию
Git по умолчанию открывает редактор vi. Он может быть сложен в использовании. Однако вы можете использовать предпочитаемый вами редактор для написания коммитов. В файл .gitconfig добавьте:
[core]
editor = code --wait
либо используйте команду:
git config --global core.editor "code --wait"Это откроет редактор VSCode. Настройки для других редакторов можно посмотреть здесь. 2. prune при fetch Если вы удалили ветку, например, после слияния её с основной, команда prune удалит устаревшие ссылки на удалённые ветки в вашем каталоге .git. Для этого вы можете выполнить
git fetch –pruneЛибо задать это поведение по умолчанию в настройках. В файле конфигурации:
[fetch]
prune = true
либо командой:
git config --global fetch.prune true3. Git-псевдонимы В файле конфигурации Git вы можете добавить псевдонимы для тех длинных команд, которыми вы постоянно пользуетесь: commit, stash и т.п. Допустим, вы хотите добавить псевдоним для добавления пустого коммита. В файле конфигурации:
[alias]
empty = git commit --allow-empty
либо командой:
git config --global alias.empty "git commit --allow-empty"Теперь вы можете использовать такую команду:
git empty "Empty commit"В псевдонимах вы можете использовать не только команды Git, но и любые другие команды терминала. Git просто выполнит всё, что указано в команде. Для этого добавьте ! в начало команды:
this = !git init && git add . && git commit -m \"Initial commit.\"Теперь
git this инициализирует репозиторий в папке, добавит все файлы под контроль git и выполнит первый коммит.
4. Ветка по умолчанию
При инициализации репозитория (git init) веткой по умолчанию является main (или master). Не обязательно использовать это имя. В файле конфигурации Git вы можете установить ветку по умолчанию при инициализации:
[init]
defaultBranch = default
Источник: www.freecodecamp.org/news/gi…settings.\bin\Debug и копирует туда все сборки, на которые ссылается проект. Это было настоящим убийцей производительности сборки в старых версиях VS. Надеюсь, это было исправлено. Тем не менее, если на проект P ссылаются N других проектов, существует N+1 файлов сборки P. Мало того, что это трата ресурсов, но также может быть, что версии станут рассинхронизированы, например, из-за того, что некоторые проекты были выгружены в обозревателе решений. Это может привести к неожиданному поведению во время выполнения, например к возникновению MissingMethodException. Поэтому лучше установить, чтобы все проекты решения (кроме тестовых) использовали один и тот же выходной каталог ..\bin\Debug. Таким образом, каждая сборка будет существовать в единственной версии.
Горячая перезагрузка
Благодаря новой функции горячей перезагрузки .NET (ранее известной как Edit and Continue) цикл [написание кода — сборка — запуск — достижение состояния для тестирования — тестирование] сокращается, т.к. шаги [сборка — запуск — достижение состояния для тестирования] могут быть пропущены в многих сценариях редактирования (но не всегда).
При горячей перезагрузке изменения исходного кода отслеживаются, компилируются и внедряются в исполняемые двоичные файлы. Внесение изменений с помощью горячей перезагрузки выполняется значительно быстрее, чем пересборка соответствующего проекта.
Большую часть времени модификации исходного кода между двумя сборками представляют собой крошечную часть пересобираемого кода. Можно ожидать, что в будущем конвейер сборки сможет повысить производительность инкрементной сборки за счет внедрения модификаций кода в двоичный файл вместо пересборки всего файла. Но уже в Visual Studio 2022 можно включить горячую перезагрузку, чтобы ускорить выполнение тестов, пропуская дорогостоящие этапы сборки для поддерживаемых типов изменений. Обратите внимание, что эта функция все ещё является экспериментальной и работает только для .NET 6.
Источник: blog.ndepend.com/improve…formance