Многие программисты делают записи, описывают трудности, красивые и не очень решения, с которыми приходится сталкиваться по долгу службы. Это может быть собственный технический блог, рабочая вики, или даже обычный блокнот — суть одна. Постепенно, из маленьких Evernote-заметок может вырасти целая статья на Хабр. Но время идет, перемена места работы сулит изменения в стеке разработки, да и технологии не стоят на месте (кстати, EF Core уже пару месяцев как в версии 1.1). С другой стороны, Entity Framework 6 был и остается "рабочей лошадкой" для доступа к данным в корпоративных приложениях на стеке .net, не в последнюю очередь благодаря своей стабильности, низкому порогу входа и широкой известности. Поэтому, я надеюсь, статья всё еще окажется кому-то полезной.  Содержание: Не хотелось бы начинать очередной раунд спора "Code First vs. Database First". Лучше просто расскажу, как облегчить себе жизнь, если вы предпочитаете Database First. Многие разработчики, использующие этот подход, отмечают неудобство работы с громоздким EDMX-файлом. Этот файл может превратить в ад командную разработку, сильно затрудняя слияние параллельных изменений вследствие постоянного "перемешивания" своей внутренней структуры. Среди прочих недостатков, для моделей с несколькими сотнями сущностей (обычный такой legacy-монолит), вы можете столкнуться с сильным падением скорости любого действия, работая со стандартным EDMX-дизайнером. Решение кажется очевидным — необходимо отказаться от EDMX в пользу альтернативных средств генерации POCO и хранения метаданных. Задача-то несложная, и в EF есть "из коробки" — это пункт "Generate Code First From Database", доступный в Visual Studio (VS2015 точно). Но, на практике — очень неудобно накатывать изменения БД на полученную модель через этот инструмент. Далее, кто работает с EF достаточно давно — может помнить расширение Entity Framework Power Tools, решающее схожие задачи — но, увы, проект более не развивается (на VS2015 без хака не поставить), а часть разработчиков этого инструмента ныне работает непосредственно в команде EF. Казалось бы, все плохо — и тут мы нашли EntityFramework Reverse POCO Generator. Это Т4-шаблон для генерации POCO на основе существующей БД с большим количеством настроек и открытым исходным кодом. Поддерживаются все основные фичи EDMX, и есть ряд дополнительных вкусностей: генерация FakeDbContext/FakeDbSet для юнит-тестирования, покрытие моделей атрибутами (напр. DataContract/DataMember) и др. Благодаря Т4, есть полный контроль за генерацией кода. Резюмируя: работает стабильно, команде нравится, легко мигрировать существующие проекты. Прикрепить к DbContext новый, либо ранее полученный в другом контексте единичный объект обычно не составляет труда. Проблемы начинаются в случае графов, т.е. сущностей со связями — EF "из коробки" не отслеживает изменения в содержимом navigation properties вновь присоединяемой к контексту сущности. Для отслеживания изменений, для каждого объекта-сущности во время жизни контекста должен существовать соответствующий entry — объект со служебной информацией, в том числе состоянием сущности (Added, Modified, Deleted и т.п.). Заполнить entries для присоединения графа — возможно как минимум 2 путями: Пример решения #1 можно найти, например, в свежем Pluralsight-курсе от Julie Lerman, известного специалиста по EF. Для его самостоятельной generic-реализации необходимо большое количество телодвижений. Все сущности должны реализовать интерфейс IStateObject: Тем или иным способом, необходимо обеспечить актуальность значений State, чтобы после ручного присоединения каждой сущности в графе к контексту: пройти по всем entry, редактируя их состояние: В этом случае нам не требуются дополнительные обращения к БД, но решение получается объемное, хрупкое, и потенциально нерабочее для отношений "многие-ко-многим". Еще и модели замусорили (к слову, требование реализации интерфейса можно решить путем модификации Т4-шаблонов из предыдущего раздела статьи).  Рассмотрим решение #2. Буду краток: вызов сей — добавит в контекст сущность root, обновив при этом navigation property с коллекцией дочерних объектов Childs, ценой SELECT-а к БД одного лишь. Что стало возможным благодаря библиотеке GraphDiff, автор которой сделал всю черную работу и выловил основные баги.  Генерация, казалось бы, простой инструкции SELECT… FROM Table WITH (UPDLOCK) не поддерживается EF. Зато есть interceptor'ы, позволяющие модифицировать генерируемый SQL любым подходящим способом, например, с помощью регулярных выражений. Давайте добавим UPDLOCK на каждый генерируемый SELECT в пределах времени жизни контекста (естественно, гранулярность — не обязательно контекст, всё зависит от вашей реализации): Для этого, объявим метод With внутри контекста и зарегистрируем interceptor: EF кэширует такие вещи, как: Кэширование данных есть только в пределах жизни контекста (вспомним метод Find), да и полноценным кэшем это язык назвать не повернется. Как нам организовать единый для всех контекстов, управляемый кэш в памяти процесса? Используем EntityFramework.Plus, либо ее "бедную" альтернативу EntityFramework.Cache: Достаточно запустить SQL-профайлер, чтобы убедиться — 2-й вызов SelectWithCache() не затрагивает БД. Lazy-обращения также будут кэшированы. Более того, возможна интеграция EF и с распределенным кэшем. Например, через самописный cache manager на базе Sytem.Runtime.Caching.ObjectCache, подключенный к EntityFramework.Plus. NCache поддерживает интеграцию с EF "из коробки" (конкретикой не могу поделиться — не пробовал этот кэш). SqlAzureExecutionStrategy — данная стратегия содержится в EF6 (отключена по-умолчанию). При ее использовании — получение определенного кода ошибки в ответе от SQL Server приводит к повторной отправке SQL-инструкции на сервер.  Интересности: В целях тестирования, подменим DbContext прозрачно для вызывающего кода, и заполнить поддельный DbSet тестовыми данными. Приведу несколько способов решения задачи.
Cпособ #1 (длинный): вручную создать заглушки для IMyDbContext и DbSet, явно прописать необходимое поведение. Вот как это может выглядеть с использованием библиотеки Moq:
 По этой теме есть базовая статья с MSDN: Entity Framework Testing with a Mocking Framework (EF6 onwards). А меня этот подход когда-то настолько впечатлил, что получился демо-проект на гитхабе (с использованием EF6 DbFirst, SQL Server, Moq, Ninject). Кстати, в уже упоминавшемся курсе Entity Framework in the Enterprise тестированию посвящена целая глава. Способ #2 (короткий): использовать уже упоминавшийся Reverse POCO Generator, который по умолчанию создает заглушки для ваших DbContext и всех DbSet (внутри FakeDbSet будет обычная in-memory коллекция).

 Для одновременной вставки в БД SQL Server тысяч новых записей — крайне эффективно использовать BULK-операции вместо стандартного построчного INSERT. Проблематику я освещал подробнее в отдельной статье, поэтому приведу ниже только готовые к использованию решения на основе SqlBulkCopy: → EntityFramework.Utilities
→ Entity Framework Extensions На этом у меня всё. Делитесь своими рецептами и хитростями в комментариях =)   .NET backend developer