11/11/2009 1:48:00 PM

В нашем проекте мы используем Entity Framework. Разумеется в первой, существующей ипостаси. Штука эта интересная и мощная, но имеет несколько существенных недостатков и неудобностей, среди которых и необходимость наследования всех классов от базового класса EF, и невозможность избавиться от Navigational Properties, и полное отсутствие контроля над поведением (да и интерфейсом) сгенерированных классов, и невозможность юнит-тестирования…

И если с классами немного удалось сладить, написав собственную утилиту-генератор, то вот всё остальное – просто беда. Да, существует такая штука, как TypeMock для юнит-тестирования, но во-первых, она платная для коммерческого использования, а во-вторых, работает через profiling API… Но хоть что-то.

В общем, поскольку нам была обещана приватная встреча с двумя ведущими разработчиками Entity Framework, я решил посмотреть немного, что обещается в следующей версии.

Очень приятным моментом для меня стало то, что Navigational Properties теперь из модели можно просто выкинуть.
Обратите внимание на скриншот: в классе Member нет свойства Member_Properties, в классе Member_Properties нет свойства Member. Это я их удалил :) При этом EF по-прежнему знает о связи этих двух сущностей и даже отображает её в дизайнере.

Navigational Properties можно удалить

Ещё одна приятная мелочь: “нормальные” ID-поля для ключей. Смотрите на Member_Addresses, свойства MemberId и AddressId - “обычные” int-свойства. Navigational Properties для Address и Member тоже есть, я их просто не удалял.

Вторым очень приятным моментом стала поддержка “обычных” классов. “POCO-классов”, как теперь модно говорить. POCO – это “Plain Old CLR Object”. То есть, в качестве entity теперь может выступать класс, не унаследованный от “специального” базового класса.
Это очень, очень, очень здорово!

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

Add Code Generation Item

Таких генераторов для модели можно иметь столько, сколько захочется и генерировать совершенно разные вещи.
Я в своём тестовом проекте просто удалил .designer.cs файл тестовой модели и отключил в свойствах .edmx-файла регенерацию. То есть, .edmx-файл теперь для меня просто описывает модель, которая может быть обновлена из БД, содержит все маппинги и т.д, но не генерирует сама по себе никаких .NET-классов.

Вместо этого я добавил Generation Item, который делает всю эту работу. Этот Generation Item представляет собой обычный .tt-файл, то есть, тот самый обычный темплейт, который существует в Visual Studio ещё с 2005-й версии, но о котором мало кто знает :) темплейт представляет собой простой текстовый файл, в котором на простом и понятном языке (например C#) просто и понятно описывается, как и что должно быть сгенерировано.
Вот, их даже два в моём проекте:

Templates Template file

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

В моём случае первый файл генерирует, собственно, контекст и всё, что с ним связано. Второй – сами entities.

Результат работы темплейтов Сгенерированный класс

При этом Entities, как я уже сказал, можно делать POCO-классами и, например, метить их DataContract/DataMember атрибутами, что позволяет использовать их в WCF и избежать часто ненужного маппинга между DTO и Entities. По сути мы имеем возможность сразу из БД получить нужные нам бизнес-объекты и DTO, что тоже очень здорово и очень облегчает жизнь.

Я уже сказал, что темплейты очень легко переделать на свой лад, чтобы они генерировали то, что нужно. Контроль над генерацией и возможность сгенерировать так, как нужно – важная вещь.
Генерируя те же entities можно ещё каких-то атрибутов навесить, или реализовать геттеры-сеттеры так, как хочется, или переопределить ToString(), или ещё методов добавить…

Я вот, задумавшишь о тестировании, добавил несколько строк в темплейт, чтобы он генерировал мне тот самый “TestModel.Context.Interfaces.cs”, который видно на картинке. Изначально его не было.
В этом файле я просто определяю интерфейс для контекста. Ну, ещё я поправил, чтобы класс контекста реализовал этот интерфейс, дописав “, ITestDbEntities” в сигнатуру класса :) 
Этот интерфейс будет используется при юнит-тестировании.

Интерфейс, он простой, смотреть сюда незачем

Предыдущая версия EF использовала в контексте некий класс EntitySet<>, который был абсолютно неудобен в случае юнит-тестирования, новая версия использует ObjectSet<>, который реализует интерфейс IObjectSet<>. А если есть интерфейс – значит можно делать мок! А раз можно делать мок – значит можно писать юнит-тесты!
То, что надо!

Да, нужно отметить, что каждый раз, когда меняется темплейт или меняется .edmx-файл модели, все относящиеся к ней темплейты перегенерируют то, что они там генерируют. То есть, следить за этим совершенно не надо.
Вы просто меняете то, что хотите поменять, остальное делается “само”: обновляются классы бизнес-модели, DTO, возможно классы менеджеров с примитивными методами типа LoadById, Save, какие-то рутинные классы преобразований и хелперов и т.д, и т.п.

Надо будет посмотреть на это дело поближе.

Comments (6) -

11/11/2009 3:51:55 PM

Artem

Будем надеяться, поделитесь интересной информацией со своей приватной встречи Smile Я, честно говоря, пока EF не внедрял нигде, т.к. на данный момент нет критичных преимуществ перед LINQ to SQL, но у меня масштабы не те.

Artem Russia

11/12/2009 7:36:50 AM

Alexey Raga

"Критичным преимуществом" является то, что на основе EF можно сделать бизнес-модель, а на основе Linq2SQL - нет. Linq2Sql вообще не предполагает объектной модели.
Ибо максимум, что Вы получите - это маппинг таблиц "один к одному" на классы. Что, в общем-то, в общем случае достаточно странно, ибо объекты бизнес-модели не обязаны ложиться (и в основном не ложатся) по принципу "один к одному".

Alexey Raga United States

11/16/2009 1:25:00 AM

build_your_web

А меня раздражают две вещи:
необходимость вызова Include(childName)
и то что автоматическую подгрузку (без Include) реализовали только для .Net 4.0.

Последнее особенно непонятно, т.к. не вижу причин, по которым это нельзя было сделать для 3.5.

build_your_web Russia

11/16/2009 1:31:54 PM

Alexey Raga

Ну, lazy loading для первой версии EF тоже был. И есть. А, собственно, чего ему и не быть, когда вся эта магическая "фича" заключается лишь в том, чтобы в геттере navigational property проверить на IsLoaded и сделать Load если false.
Понятное дело, что в случае грядущего EF это гораздо проще: поправил генератор кода как надо - и радуйся. Однако, и для первой версии всё достигаемо.
Реализация - пожалуйста, вот: http://code.msdn.microsoft.com/EFLazyLoading

Другое дело, что это далеко не всегда полезно. А чаще даже и вредно, ибо объект постоянно находится в недетерминированном состоянии. А ещё производительность хромает в этом случае.

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

Alexey Raga United States

11/17/2009 1:31:50 AM

build_your_web

А как делать Include на несколько связанных таблиц.
Скажем, хочу PDC.Visitors.Include("Profile.FromCountry"), чтоб получить
var visitorInfo = PDC.Visitors.Select(v=>new {v.RegisteredDate, v.Profile.Cell, CountryName = v.Profile.FromCountry.Name})

build_your_web Russia

11/21/2009 7:58:07 PM

build_your_web

Как Include'ить многоуровневые EF сущности. Допустим PDC.Include("Visitors.Profile") ?

build_your_web Russia

Comments are closed

Powered by BlogEngine.NET 2.5.0.6

About the author

Alexey Raga Alexey Raga
.NET software developer.

E-mail me Send mail

Twitter


Recent posts

Archive

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2012

Sign in