Патрик Лиой

В C# 8 добавлены Nullable Reference Types, которые предупреждают нас о проблематичных значениях null. Если вы переходите на C# 8, вы можете столкнуться с некоторыми проблемами для Entity Framework. Хотя поначалу это разочаровывает, предупреждения также могут послужить катализатором поиска более эффективных способов сообщить о намерениях. Давайте рассмотрим обновление и разработаем несколько новых шаблонов EF для внедрения.

Обновление ядра Entity Framework

Вам понадобится последняя версия Entity Framework Core, поэтому начните с проверки актуальности ссылок на пакеты Nuget. Я обновил Microsoft.EntityFrameworkCore.SqlServer до версии 3.0.0, так как он имеет первоклассную поддержку работы с нулевыми ссылочными типами.

Включить ссылочные типы, допускающие значение NULL, во всех проектах

Я включил эту функцию для всех проектов в своем решении, поместив файл Directory.build.props рядом с моим файлом *.sln. Я мог бы включить эту функцию в каждом файле *.csproj отдельно, но я не хочу помнить об этом каждый раз, когда добавляю в решение еще один проект. Я хочу решить это один раз, чтобы оно оставалось решенным:

Как только вы включите эту функцию, вы получите предупреждения компилятора, основанные на двух твердых мнениях:

  1. Ссылка, такая как string, никогда не должна быть нулевой.
  2. Если ссылка заслуживает того, чтобы быть нулевой, вы должны сказать об этом: string?

Сначала вы получите много предупреждений: «Вы сказали, что это не будет null, но я думаю, вы ошибаетесь!» В середине метода может быть ясно, что делать в ответ на каждое предупреждение, но когда дело доходит до Entity Framework DbContext и классов сущностей, предупреждения могут немного сбивать с толку.

Работа с DbSet

Моя первая проблема была с собственными свойствами DbSet моего класса DbContext. Любой, кто знаком с EF, просто знает, что эти свойства DbSet будут ненулевыми к тому моменту, когда они нам понадобятся. EF гарантирует, что они будут инициализированы для нас, но компилятор C# этого не знает. Компилятор видит только ненулевой ссылочный тип (DbSet‹Contact›), который очевидно никогда не инициализировался. Вместо того, чтобы «гнаться за предупреждающим сообщением», добавляя бессмысленные назначения конструктора, мы заявим, что в данном случае мы лучше знаем. Добавляем «= по умолчанию!» для каждого свойства DbSet:

Технически это означает: «Инициализируйте его значением по умолчанию (нулем), но действуйте так, как будто оно не равно нулю». На практике мы будем читать это как: «Поверьте мне! EF выставит мне это по умолчанию!»

Работа с сущностями

Затем я получил предупреждение о моем классе объектов Contact. Мы начали с типичного набора изменяемых свойств:

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

На этот раз было бы серьезной ошибкой инициализировать их значением «по умолчанию!». Раньше это было безопасно, потому что в этом случае мы с большой уверенностью знали, что любое использование свойств будет работать без проверки на null. В случае свойств объектов мы знаем, что такой подстраховки не существует. На этот раз нам нужно действительно следовать предложению, инициализируя свойства ненулевых столбцов в конструкторе. В моем случае только столбец PhoneNumber действительно обнулялся:

Подожди, а как же Ид?

Вы можете быть удивлены тем, что мы не принимаем свойство never-null Id через конструктор. Мы опускаем это по уважительной причине.

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

Во-вторых, мы знаем, что EF предпочитает владеть нашими свойствами Id. Например, если мы хотим выполнить INSERT, EF хочет, чтобы мы создали объект, не заморачиваясь с идентификатором, Добавили(…) его, а затем сохранили эти изменения. EF будет знать, что делать, и заполнит наш идентификатор, когда он будет готов. Точно так же в SELECT EF заполнит его еще до того, как мы посмотрим на него.

Наш класс сущности стал немного более подробным, но он также лучше сообщает следующему разработчику, как его следует использовать. Мы говорим: «Вам действительно нужно, чтобы по крайней мере эти значения были действительными, и вы действительно должны держать свои руки подальше от этого свойства Id».

Вывод: неоднозначная ситуация

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

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

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

Этот пост в блоге был частью C# Advent 2019.

Первоначально опубликовано на https://headspring.com 19 декабря 2019 г.