Pull to refresh
16
0
Сергей Родюшкин @SergeyRodyushkin

Разработчик и архитектор ASP.NET приложений

Send message
Может быть, и здесь.
Все равно что-то мне не нравится в таком подходе. Может быть, он и проще, но по логике... Для чего, спрашивается, создаются все эти Entities? Что мешает работать напрямую, скажем, c DataSet? Мы создаем domain model, все-таки исходя из задач, специфичных для предметной области, а не из того, как у нас в БД это хранится. В это-то и соль ORM: в БД своя схема, domain model — другая схема, а ORM (или рукописный data access layer, неважно) сопоставляет одно с другим.
По-хорошему, программист должен вообще быть избавлен от деталей, как там у нас данные хранятся.

Допустим, мы пишем магазин. Есть связь вида «категория - продукт». Если мы получаем товары из свойства Products класса Category, логично предположить, что категорию продукта следует брать из свойства Category класса Product, так? А у нас там как Category, так и какой-нибудь CategoryName. Программист, увидя такое, наверняка задастся вопросом (а я бы задался и взялся за декомпилятор) «А как именно мне переместить продукт в другую категорию? Достаточно ли просто поменять CategoryName, или нужно менять свойство Category? А что будет, если я поменяю, скажем, product.Category.Name?»
Ну зачем ему _parentID, если у него есть Parent? :)
Я про определение сущностей. Иллюстрирую:

  [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 одновременно, читал/писал бы значение заданного поля для связанной сущности, не так сложно, ан нет...
Какая уж там сильная нагрузка на внутреннем проекте? :)
Нет, дело совсем не в базе. Там была моделька вида «каталог - элементы», соответственно, один-ко-многим. В каждом каталоге — EntitySet, в который грузятся лежащие в нем элементы. Так вот, выбиралось все прекрасно, но вот когда делаешь Add() в EntitySet, а потом сохраняешь изменения, возникала проблема. Либо все писалось в базу, но без CatalogID (равным нулю, если точнее), и элемент не выбирался, либо вообще ничего не сохранялось. Проблему обошел, только тупо присваивая item.Catalog = catalog, и после этого вызывая context.Items.InsertOnSubmit(item).
Вот, кстати, еще один, имхо, недостаток Linq to SQL: чтобы прописать Association, необходимо, чтобы в дочернем объекте существовало свойство, хранящее значение идентификатора родителя. С учетом типизации, то есть, EntityRef<> тут не подойдет. Как раз это я и назвал «ограничением Linq на модели предметной области».
>> ограничения, накладываемые на структуру базы данных и модели предметной области
Ну, например, в 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() у меня вносил изменения в зависимости от фазы Луны. Может быть, я где-то был неправ, но код-то сгенерированный.
Спасибо за статью.

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. К сожалению, пока катастрофически не хватает времени и желания всерьез покопаться с ним.

Еще волнует производительность, ибо база данных обычно нагибается в первую очередь.
судя по всему, хабровчане включили анонимайзер и отправились исправлять ситуацию :)
там вообще что-то странное. после 16 стало 46, теперь 41.
1024 — красивое число, не находите? :)
Проголосовал за вас.
Если посмотреть на работы, сомнения по поводу законности присуждения вам первого места пропадают сразу:) Искренне желаю вам победы!

P.S.: Результаты голосования фантастические. Хабраэффект? :)
Спасибо, возьму на заметку!
У меня это значение вычисляется один раз.
Вот математика на поверку там была не к месту, хватит и (array.Length - 1) / 2.
Сдаюсь :)
Про Array.Reverse() даже не задумался.
Согласен, выглядит не очень, но вычисление Math.Floor((float)((array.Length - 1) / 2)) на каждом шаге, ИМХО, не очень хорошо.
*зря я условие цикла решил оптимизировать — ошибка вкралась.
правильно так:


for(int i = 0, len = (int)Math.Floor((float)((array.Length - 1) / 2)); i <= len; ++i)
...
Если мне не изменяет память, то выглядит это так:


public string Reverse(string str)
{
char[] array = str.ToCharArray();
char temp;

for (int i = 0, len = Math.Floor((float)((array.Length - 1) / 2); i <= len); ++i)
{
temp = array[i];
array[i] = array[array.Length - (1 + i)];
array[array.Length - (1 + i)] = temp;
}
return new String(array);
}
Я тоже, но мало ли... :)
Ну, это вообще куда-то в другую степь :)

Можно предположить, что любой класс, содержащий виртуальный член (метод, свойство, индексатор или событие) считается виртуальным. Подобно тому, как в C++ были сделаны абстрактные классы.
Так, может быть, в этом и была вся соль вопроса? :)
Я все-таки не до конца осмыслил ваш вопрос :)
Приведенные варианты будут отличаться стек-трейсом. Первый вариант не сохраняет предыдущее состояние стека, поэтому при получении исключения не будет видно, что к нему привело. Во втором варианте все более правильно.

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity