Комментарии 16
Спасибо за статью.
Entity Framework, наряду с ASP.NET MVC Framework, одна из интересующих меня вещей. Несмотря на то, что механизм маппинга в ней (посредством трех XML-файлов) немного напрягает.
Linq to SQL я попробовал в паре внутренних проектов, и он вызвал у меня двоякие чувства. С одной стороны, делать запросы через Linq безотносительно к состоянию объектов (выбор из DB или уже загруженной коллекции) удобно, но... с другой стороны, ограничения, накладываемые на структуру базы данных и модели предметной области, отсутствие поддержки many-to-many, несколько странная работа системы отслеживания изменений в DataContext (время от времени изменения в EntitySet'ах ну никак не хотели записываться).
Я надеюсь, Entity Framework будет смотреться намного лучше, тем более, что какие-то его средства (было упомянуто вскользь на Heroes Happen Here) будут включены в SQL Server 2008. К сожалению, пока катастрофически не хватает времени и желания всерьез покопаться с ним.
Еще волнует производительность, ибо база данных обычно нагибается в первую очередь.
Entity Framework, наряду с ASP.NET MVC Framework, одна из интересующих меня вещей. Несмотря на то, что механизм маппинга в ней (посредством трех XML-файлов) немного напрягает.
Linq to SQL я попробовал в паре внутренних проектов, и он вызвал у меня двоякие чувства. С одной стороны, делать запросы через Linq безотносительно к состоянию объектов (выбор из DB или уже загруженной коллекции) удобно, но... с другой стороны, ограничения, накладываемые на структуру базы данных и модели предметной области, отсутствие поддержки many-to-many, несколько странная работа системы отслеживания изменений в DataContext (время от времени изменения в EntitySet'ах ну никак не хотели записываться).
Я надеюсь, Entity Framework будет смотреться намного лучше, тем более, что какие-то его средства (было упомянуто вскользь на Heroes Happen Here) будут включены в SQL Server 2008. К сожалению, пока катастрофически не хватает времени и желания всерьез покопаться с ним.
Еще волнует производительность, ибо база данных обычно нагибается в первую очередь.
+2
Пожалуйста, тем более, что я только изложил чужой материал своими словами, да и то не весь.
Про маппинг ваша информация уже устарела, маппинг в EF сейчас делается через один файл с тремя разделами.
Можно подробней об "ограничения, накладываемые на структуру базы данных и модели предметной области", не совсем понял, что вы имели в виду.
Many-to-many, конечно же поддерживаются (вас читают и те, кто не знают ничего про asp.net и могут не так понять), не поддерживается только прямой мэппинг минуя промежуточную таблицу, но на то оно и DB ORM, чтобы копировать структуру БД один в один. С другой стороны, конечно, хотелось бы и побольше возможностей в этом плане.
Что такое "система отслеживания изменений в DataContext"? Тоже не совсем понял о чем вы говорите.
Про маппинг ваша информация уже устарела, маппинг в EF сейчас делается через один файл с тремя разделами.
Можно подробней об "ограничения, накладываемые на структуру базы данных и модели предметной области", не совсем понял, что вы имели в виду.
Many-to-many, конечно же поддерживаются (вас читают и те, кто не знают ничего про asp.net и могут не так понять), не поддерживается только прямой мэппинг минуя промежуточную таблицу, но на то оно и DB ORM, чтобы копировать структуру БД один в один. С другой стороны, конечно, хотелось бы и побольше возможностей в этом плане.
Что такое "система отслеживания изменений в DataContext"? Тоже не совсем понял о чем вы говорите.
0
>> ограничения, накладываемые на структуру базы данных и модели предметной области
Ну, например, в Linq to SQL поддерживается только модель «одна сущность одна таблица». Разнести данные одной сущности на несколько таблиц уже не получится, что иногда мешает нормализации БД. Наследование тоже только по модели Single Table Inheritance. В EF этих проблемы уже нет.
>> Many-to-many, конечно же поддерживаются
Да, но череззадницудополнительную сущность. Тем более странно, что в известных ORM-решениях (в частности, NHibernate, Gentle.Net) это вполне работает и напрямую. Кстати, тут я вас уже не понял: «прямой мэппинг минуя промежуточную таблицу» как это? Как раз и интересен мэппинг через таблицу вида { FirstEntityID, SecondEntityID }, хотя бы потому, что используется едва ли не в каждом проекте.
>> Что такое "система отслеживания изменений в DataContext"
Это внутренняя подсистема Linq to SQL, отслеживающая изменения в объектах, которые были запрошены. По сути, она хранит объект в исходном состоянии и ссылку на тот, который используется вами. При вызове метода SubmitChanges() для DataContext она сравнивает объекты и вносит в базу данных произошедшие изменения. Кроме того, это позволяет осуществлять проверку, не были ли изменены ваши объекты с момента последней их выборки (optimistic concurrency checking).
Подробнее об этом можно посмотреть тут: http://weblogs.asp.net/scottgu/archive/2…
Так вот, этот самый SubmitChanges() у меня вносил изменения в зависимости от фазы Луны. Может быть, я где-то был неправ, но код-то сгенерированный.
Ну, например, в Linq to SQL поддерживается только модель «одна сущность одна таблица». Разнести данные одной сущности на несколько таблиц уже не получится, что иногда мешает нормализации БД. Наследование тоже только по модели Single Table Inheritance. В EF этих проблемы уже нет.
>> Many-to-many, конечно же поддерживаются
Да, но через
>> Что такое "система отслеживания изменений в DataContext"
Это внутренняя подсистема Linq to SQL, отслеживающая изменения в объектах, которые были запрошены. По сути, она хранит объект в исходном состоянии и ссылку на тот, который используется вами. При вызове метода SubmitChanges() для DataContext она сравнивает объекты и вносит в базу данных произошедшие изменения. Кроме того, это позволяет осуществлять проверку, не были ли изменены ваши объекты с момента последней их выборки (optimistic concurrency checking).
Подробнее об этом можно посмотреть тут: http://weblogs.asp.net/scottgu/archive/2…
Так вот, этот самый SubmitChanges() у меня вносил изменения в зависимости от фазы Луны. Может быть, я где-то был неправ, но код-то сгенерированный.
0
>>>Кстати, тут я вас уже не понял: «прямой мэппинг минуя промежуточную таблицу» — как это?
Этим я их хотел сказать то, что вы уже сказали, при мэппинге участвует промежуточная таблица, с которой надо работать. Соответственно в EF такая таблица не мапится в сущность. Вы правы в Linq есть такое ограничение.
Сам проблем с SubmitChanges() не испытывал, но стало интересно в чем заключались проблемы? Какое-то особое состояние БД? Сильная нагрузка?
Этим я их хотел сказать то, что вы уже сказали, при мэппинге участвует промежуточная таблица, с которой надо работать. Соответственно в EF такая таблица не мапится в сущность. Вы правы в Linq есть такое ограничение.
Сам проблем с SubmitChanges() не испытывал, но стало интересно в чем заключались проблемы? Какое-то особое состояние БД? Сильная нагрузка?
0
Какая уж там сильная нагрузка на внутреннем проекте? :)
Нет, дело совсем не в базе. Там была моделька вида «каталог - элементы», соответственно, один-ко-многим. В каждом каталоге EntitySet, в который грузятся лежащие в нем элементы. Так вот, выбиралось все прекрасно, но вот когда делаешь Add() в EntitySet, а потом сохраняешь изменения, возникала проблема. Либо все писалось в базу, но без CatalogID (равным нулю, если точнее), и элемент не выбирался, либо вообще ничего не сохранялось. Проблему обошел, только тупо присваивая item.Catalog = catalog, и после этого вызывая context.Items.InsertOnSubmit(item).
Вот, кстати, еще один, имхо, недостаток Linq to SQL: чтобы прописать Association, необходимо, чтобы в дочернем объекте существовало свойство, хранящее значение идентификатора родителя. С учетом типизации, то есть, EntityRef<> тут не подойдет. Как раз это я и назвал «ограничением Linq на модели предметной области».
Нет, дело совсем не в базе. Там была моделька вида «каталог - элементы», соответственно, один-ко-многим. В каждом каталоге EntitySet, в который грузятся лежащие в нем элементы. Так вот, выбиралось все прекрасно, но вот когда делаешь Add() в EntitySet, а потом сохраняешь изменения, возникала проблема. Либо все писалось в базу, но без CatalogID (равным нулю, если точнее), и элемент не выбирался, либо вообще ничего не сохранялось. Проблему обошел, только тупо присваивая item.Catalog = catalog, и после этого вызывая context.Items.InsertOnSubmit(item).
Вот, кстати, еще один, имхо, недостаток Linq to SQL: чтобы прописать Association, необходимо, чтобы в дочернем объекте существовало свойство, хранящее значение идентификатора родителя. С учетом типизации, то есть, EntityRef<> тут не подойдет. Как раз это я и назвал «ограничением Linq на модели предметной области».
0
Еще бы на код взглянуть :)
"Прописать Association" - это задать связь с чем-то новым чтоли? На этапе маппинга или вы во время исполнения создаете? Как можно связать дочерний элемент с родителем без наличия идентификатора родителя? Что-то не вникаю. Жара чтоли?
Или вы все про определение сущностей говорите? Тогда все эти недостатки вытекают из одного - Linq to Sql тупо повторяет структуру БД и даже меньше (к примеру не обрабатывает автоматически default constraint, хотя мог бы). Поэтому то и предлагают EF, как далеко продвинутый вариант ORM маппинга.
"Прописать Association" - это задать связь с чем-то новым чтоли? На этапе маппинга или вы во время исполнения создаете? Как можно связать дочерний элемент с родителем без наличия идентификатора родителя? Что-то не вникаю. Жара чтоли?
Или вы все про определение сущностей говорите? Тогда все эти недостатки вытекают из одного - Linq to Sql тупо повторяет структуру БД и даже меньше (к примеру не обрабатывает автоматически default constraint, хотя мог бы). Поэтому то и предлагают EF, как далеко продвинутый вариант ORM маппинга.
0
Я про определение сущностей. Иллюстрирую:
Чтобы создать ссылку на родителя, необходимо добавлять вот такое извращение, как private int? _parentID. Если в EF можно так не делать, это очень радует. Хотя, казалось бы, сделать код, который, если поле/свойство помечено атрибутами Column и Accociation одновременно, читал/писал бы значение заданного поля для связанной сущности, не так сложно, ан нет...
[Table(Name = "Catalogs")]
public class Catalog
{
private int _id;
private EntityRef<Directory> _parent;
[Column(Name = "ID", DbType = "int not null",
IsDbGenerated = true, IsPrimaryKey = true,
CanBeNull = false, Storage = "_id",
AutoSync = AutoSync.OnInsert)]
public int ID
{
get
{
return _id;
}
}
[Column(Name = "ParentID", Storage = "_parentID",
CanBeNull = true, UpdateCheck = UpdateCheck.Never)]
private int? _parentID;
[Association(ThisKey = "_parentID", Storage = "_parent")]
public Directory Parent
{
get
{
return _parent.Entity;
}
set
{
_parentID = value.ID;
_parent.Entity = value;
}
}
...
* This source code was highlighted with Source Code Highlighter.
Чтобы создать ссылку на родителя, необходимо добавлять вот такое извращение, как private int? _parentID. Если в EF можно так не делать, это очень радует. Хотя, казалось бы, сделать код, который, если поле/свойство помечено атрибутами Column и Accociation одновременно, читал/писал бы значение заданного поля для связанной сущности, не так сложно, ан нет...
0
По моему ничего лишнего тут нет. Программист должен иметь возможность обратиться к значению _parentId, а вы его хотите скрывать.
0
Ну зачем ему _parentID, если у него есть Parent? :)
0
Иначе как отработать ситуацию, когда меняется ключ? По вашему я должен создать экземпляр предка и присвоить его потомку, вместо того, чтобы просто присвоить простой идентификатор объявленный как Int? Может быть именно тут порылась собака которая у вас обрушала SubmitChanges()?
0
Может быть, и здесь.
Все равно что-то мне не нравится в таком подходе. Может быть, он и проще, но по логике... Для чего, спрашивается, создаются все эти Entities? Что мешает работать напрямую, скажем, c DataSet? Мы создаем domain model, все-таки исходя из задач, специфичных для предметной области, а не из того, как у нас в БД это хранится. В это-то и соль ORM: в БД своя схема, domain model другая схема, а ORM (или рукописный data access layer, неважно) сопоставляет одно с другим.
По-хорошему, программист должен вообще быть избавлен от деталей, как там у нас данные хранятся.
Допустим, мы пишем магазин. Есть связь вида «категория - продукт». Если мы получаем товары из свойства Products класса Category, логично предположить, что категорию продукта следует брать из свойства Category класса Product, так? А у нас там как Category, так и какой-нибудь CategoryName. Программист, увидя такое, наверняка задастся вопросом (а я бы задался и взялся за декомпилятор) «А как именно мне переместить продукт в другую категорию? Достаточно ли просто поменять CategoryName, или нужно менять свойство Category? А что будет, если я поменяю, скажем, product.Category.Name?»
Все равно что-то мне не нравится в таком подходе. Может быть, он и проще, но по логике... Для чего, спрашивается, создаются все эти Entities? Что мешает работать напрямую, скажем, c DataSet? Мы создаем domain model, все-таки исходя из задач, специфичных для предметной области, а не из того, как у нас в БД это хранится. В это-то и соль ORM: в БД своя схема, domain model другая схема, а ORM (или рукописный data access layer, неважно) сопоставляет одно с другим.
По-хорошему, программист должен вообще быть избавлен от деталей, как там у нас данные хранятся.
Допустим, мы пишем магазин. Есть связь вида «категория - продукт». Если мы получаем товары из свойства Products класса Category, логично предположить, что категорию продукта следует брать из свойства Category класса Product, так? А у нас там как Category, так и какой-нибудь CategoryName. Программист, увидя такое, наверняка задастся вопросом (а я бы задался и взялся за декомпилятор) «А как именно мне переместить продукт в другую категорию? Достаточно ли просто поменять CategoryName, или нужно менять свойство Category? А что будет, если я поменяю, скажем, product.Category.Name?»
0
>> По вашему я должен создать экземпляр предка и присвоить его потомку, вместо того, чтобы просто присвоить простой идентификатор объявленный как Int?
Ну, вообще-то, как раз такие вещи ORM должна отрабатывать.
Ну, вообще-то, как раз такие вещи ORM должна отрабатывать.
0
У меня вроде бы такая задача как раз и реализовывалась стандартными средствами. Попробую ещё детальное как у вас.
Я столкнулся с той проблемой, что когда мы удаляем из коллекции Directory.Catalogs, то LINQ пытается просто удалить связь с родителем, а не удаляет элемент прямо из базы. С одной стороны логично, но как сделать чтобы удаляло напряемую через эту коллекцию сразу не нашел. Пришлось делать только через db.Catalogs. Т.е. работать напрямую с дочерними элементами. В моем случае это был заказ и товары заказа. В общем-то сами по себе товары заказа не имею ценности и я бы вообще закрыл к ним доступ напрямую, но вот с такими проблемами столкнулся..
Я столкнулся с той проблемой, что когда мы удаляем из коллекции Directory.Catalogs, то LINQ пытается просто удалить связь с родителем, а не удаляет элемент прямо из базы. С одной стороны логично, но как сделать чтобы удаляло напряемую через эту коллекцию сразу не нашел. Пришлось делать только через db.Catalogs. Т.е. работать напрямую с дочерними элементами. В моем случае это был заказ и товары заказа. В общем-то сами по себе товары заказа не имею ценности и я бы вообще закрыл к ним доступ напрямую, но вот с такими проблемами столкнулся..
0
Удаляет связь с родителем == устанавливает поле ParentID в БД равным NULL?
Тогда, насколько я понимаю, достаточно установить DeleteOnNull = true в атрибуте Association, маркирующем EntitySet.
Для этого, правда, придется менять либо DBML, либо сгенерированный код.
Подробнее об этом можно прочитать здесь: http://forums.microsoft.com/MSDN/ShowPos…
А вот что делать, когда он не добавляется пока не нашел (собственно, пока и не особо искал :)
Тогда, насколько я понимаю, достаточно установить DeleteOnNull = true в атрибуте Association, маркирующем EntitySet.
Для этого, правда, придется менять либо DBML, либо сгенерированный код.
Подробнее об этом можно прочитать здесь: http://forums.microsoft.com/MSDN/ShowPos…
А вот что делать, когда он не добавляется пока не нашел (собственно, пока и не особо искал :)
+1
Зарегистрируйтесь на Хабре , чтобы оставить комментарий
asp.net: Entity Framework, одно из отличий от LINQ to Sql